diff --git a/CHANGELOG.md b/CHANGELOG.md index e385e0158..7ec5e810d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,14 @@ * Fixed symlink creation in linux VM guest filesystems to be recognized by Windows (#10592) * Performance improvement in pool optimization step (#10585) +### [2.2.11] 2022-04-01 + + * Added missing config.bitbucket-oauth in composer-schema.json + * Added --2.2 flag to `self-update` to pin the Composer version to the 2.2 LTS range (#10682) + * Updated semver, jsonlint deps for minor fixes + * Fixed generation of autoload crashing if a package has a broken path (#10688) + * Removed dev-master=>dev-main alias from #10372 as it does not work when reloading from lock file and extracting dev deps (#10651) + ### [2.2.10] 2022-03-29 * Fixed Bitbucket authorization detection due to API changes (#10657) @@ -1461,6 +1469,7 @@ [2.3.0]: https://github.com/composer/composer/compare/2.3.0-RC2...2.3.0 [2.3.0-RC2]: https://github.com/composer/composer/compare/2.3.0-RC1...2.3.0-RC2 [2.3.0-RC1]: https://github.com/composer/composer/compare/2.2.9...2.3.0-RC1 +[2.2.11]: https://github.com/composer/composer/compare/2.2.10...2.2.11 [2.2.10]: https://github.com/composer/composer/compare/2.2.9...2.2.10 [2.2.9]: https://github.com/composer/composer/compare/2.2.8...2.2.9 [2.2.8]: https://github.com/composer/composer/compare/2.2.7...2.2.8 diff --git a/composer.lock b/composer.lock index 57da6d750..e7b9c66cb 100644 --- a/composer.lock +++ b/composer.lock @@ -224,16 +224,16 @@ }, { "name": "composer/semver", - "version": "3.3.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71" + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/5d8e574bb0e69188786b8ef77d43341222a41a71", - "reference": "5d8e574bb0e69188786b8ef77d43341222a41a71", + "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", + "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", "shasum": "" }, "require": { @@ -285,7 +285,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.3.1" + "source": "https://github.com/composer/semver/tree/3.3.2" }, "funding": [ { @@ -301,7 +301,7 @@ "type": "tidelift" } ], - "time": "2022-03-16T11:22:07+00:00" + "time": "2022-04-01T19:23:25+00:00" }, { "name": "composer/spdx-licenses", diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index f617f0d8c..68f540bcf 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -162,6 +162,7 @@ class AutoloadGenerator * @param string $suffix * @return int * @throws \Seld\JsonLint\ParsingException + * @throws \RuntimeException */ public function dump(Config $config, InstalledRepositoryInterface $localRepo, RootPackageInterface $rootPackage, InstallationManager $installationManager, string $targetDir, bool $scanPsrPackages = false, string $suffix = '') { @@ -1240,6 +1241,9 @@ INITIALIZER; } $resolvedPath = realpath($installPath . '/' . $updir); + if (false === $resolvedPath) { + continue; + } $autoloads[] = preg_quote(strtr($resolvedPath, '\\', '/')) . '/' . $path . '($|/)'; continue; } diff --git a/src/Composer/Command/SelfUpdateCommand.php b/src/Composer/Command/SelfUpdateCommand.php index 259abd5db..9148d9f34 100644 --- a/src/Composer/Command/SelfUpdateCommand.php +++ b/src/Composer/Command/SelfUpdateCommand.php @@ -59,6 +59,7 @@ class SelfUpdateCommand extends BaseCommand new InputOption('snapshot', null, InputOption::VALUE_NONE, 'Force an update to the snapshot channel'), new InputOption('1', null, InputOption::VALUE_NONE, 'Force an update to the stable channel, but only use 1.x versions'), new InputOption('2', null, InputOption::VALUE_NONE, 'Force an update to the stable channel, but only use 2.x versions'), + new InputOption('2.2', null, InputOption::VALUE_NONE, 'Force an update to the stable channel, but only use 2.2.x LTS versions'), new InputOption('set-channel-only', null, InputOption::VALUE_NONE, 'Only store the channel as the default one and then exit'), )) ->setHelp( @@ -181,8 +182,12 @@ EOT } } - if ($requestedChannel && is_numeric($requestedChannel) && strpos($latestStable['version'], $requestedChannel) !== 0) { - $io->writeError('Warning: You forced the install of '.$latestVersion.' via --'.$requestedChannel.', but '.$latestStable['version'].' is the latest stable version. Updating to it via composer self-update --stable is recommended.'); + $effectiveChannel = $requestedChannel === null ? $versionsUtil->getChannel() : $requestedChannel; + if (is_numeric($effectiveChannel) && strpos($latestStable['version'], $effectiveChannel) !== 0) { + $io->writeError('Warning: You forced the install of '.$latestVersion.' via --'.$effectiveChannel.', but '.$latestStable['version'].' is the latest stable version. Updating to it via composer self-update --stable is recommended.'); + } + if (isset($latest['eol'])) { + $io->writeError('Warning: Version '.$latestVersion.' is EOL / End of Life. '.$latestStable['version'].' is the latest stable version. Updating to it via composer self-update --stable is recommended.'); } if (Preg::isMatch('{^[0-9a-f]{40}$}', $updateVersion) && $updateVersion !== $latestVersion) { diff --git a/src/Composer/SelfUpdate/Versions.php b/src/Composer/SelfUpdate/Versions.php index 40301eb20..0390cc08d 100644 --- a/src/Composer/SelfUpdate/Versions.php +++ b/src/Composer/SelfUpdate/Versions.php @@ -12,6 +12,7 @@ namespace Composer\SelfUpdate; +use Composer\Pcre\Preg; use Composer\Util\HttpDownloader; use Composer\Config; @@ -21,7 +22,7 @@ use Composer\Config; class Versions { /** @var string[] */ - public static $channels = array('stable', 'preview', 'snapshot', '1', '2'); + public static $channels = array('stable', 'preview', 'snapshot', '1', '2', '2.2'); /** @var HttpDownloader */ private $httpDownloader; @@ -29,8 +30,8 @@ class Versions private $config; /** @var string */ private $channel; - /** @var array> */ - private $versionsData; + /** @var array>|null */ + private $versionsData = null; public function __construct(Config $config, HttpDownloader $httpDownloader) { @@ -50,7 +51,7 @@ class Versions $channelFile = $this->config->get('home').'/update-channel'; if (file_exists($channelFile)) { $channel = trim(file_get_contents($channelFile)); - if (in_array($channel, array('stable', 'preview', 'snapshot'), true)) { + if (in_array($channel, array('stable', 'preview', 'snapshot', '2.2'), true)) { return $this->channel = $channel; } } @@ -71,13 +72,14 @@ class Versions $channelFile = $this->config->get('home').'/update-channel'; $this->channel = $channel; - file_put_contents($channelFile, (is_numeric($channel) ? 'stable' : $channel).PHP_EOL); + // rewrite '2' and '1' channels to stable for future self-updates, but LTS ones like '2.2' remain pinned + file_put_contents($channelFile, (Preg::isMatch('{^\d+$}D', $channel) ? 'stable' : $channel).PHP_EOL); } /** * @param string|null $channel * - * @return array{path: string, version: string, min-php: int} + * @return array{path: string, version: string, min-php: int, eol?: true} */ public function getLatest(?string $channel = null): array { @@ -93,11 +95,11 @@ class Versions } /** - * @return array> + * @return array> */ private function getVersionsData(): array { - if (!$this->versionsData) { + if (null === $this->versionsData) { if ($this->config->get('disable-tls') === true) { $protocol = 'http'; } else { diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 848dc9198..51ac10a8e 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -14,6 +14,7 @@ namespace Composer\Test\Autoload; use Composer\Autoload\AutoloadGenerator; use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory; +use Composer\Package\CompletePackage; use Composer\Package\Link; use Composer\Package\Version\VersionParser; use Composer\Semver\Constraint\Constraint; @@ -772,8 +773,8 @@ EOF; include $this->vendorDir.'/composer/autoload_classmap.php' ); $this->assertAutoloadFiles('classmap5', $this->vendorDir.'/composer', 'classmap'); - $this->assertStringNotContainsString('$loader->setClassMapAuthoritative(true);', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); - $this->assertStringNotContainsString('$loader->setApcuPrefix(', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringNotContainsString('$loader->setClassMapAuthoritative(true);', (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringNotContainsString('$loader->setApcuPrefix(', (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); } public function testClassMapAutoloadingAuthoritativeAndApcu(): void @@ -821,8 +822,8 @@ EOF; ); $this->assertAutoloadFiles('classmap8', $this->vendorDir.'/composer', 'classmap'); - $this->assertStringContainsString('$loader->setClassMapAuthoritative(true);', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); - $this->assertStringContainsString('$loader->setApcuPrefix(', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringContainsString('$loader->setClassMapAuthoritative(true);', (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringContainsString('$loader->setApcuPrefix(', (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); } public function testClassMapAutoloadingAuthoritativeAndApcuPrefix(): void @@ -870,8 +871,8 @@ EOF; ); $this->assertAutoloadFiles('classmap8', $this->vendorDir.'/composer', 'classmap'); - $this->assertStringContainsString('$loader->setClassMapAuthoritative(true);', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); - $this->assertStringContainsString('$loader->setApcuPrefix(\'custom\\\'Prefix\');', file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringContainsString('$loader->setClassMapAuthoritative(true);', (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringContainsString('$loader->setApcuPrefix(\'custom\\\'Prefix\');', (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); } public function testFilesAutoloadGeneration(): void @@ -1433,8 +1434,8 @@ EOF; $this->assertStringEqualsFile($vendorDir.'/composer/autoload_namespaces.php', $expectedNamespace); $this->assertStringEqualsFile($vendorDir.'/composer/autoload_psr4.php', $expectedPsr4); $this->assertStringEqualsFile($vendorDir.'/composer/autoload_classmap.php', $expectedClassmap); - $this->assertStringContainsString("\$vendorDir . '/b/b/bootstrap.php',\n", file_get_contents($vendorDir.'/composer/autoload_files.php')); - $this->assertStringContainsString("\$baseDir . '/test.php',\n", file_get_contents($vendorDir.'/composer/autoload_files.php')); + $this->assertStringContainsString("\$vendorDir . '/b/b/bootstrap.php',\n", (string) file_get_contents($vendorDir.'/composer/autoload_files.php')); + $this->assertStringContainsString("\$baseDir . '/test.php',\n", (string) file_get_contents($vendorDir.'/composer/autoload_files.php')); } public function testUpLevelRelativePaths(): void @@ -1518,7 +1519,85 @@ EOF; $this->assertStringEqualsFile($this->vendorDir.'/composer/autoload_namespaces.php', $expectedNamespace); $this->assertStringEqualsFile($this->vendorDir.'/composer/autoload_psr4.php', $expectedPsr4); $this->assertStringEqualsFile($this->vendorDir.'/composer/autoload_classmap.php', $expectedClassmap); - $this->assertStringContainsString("\$baseDir . '/../test.php',\n", file_get_contents($this->vendorDir.'/composer/autoload_files.php')); + $this->assertStringContainsString("\$baseDir . '/../test.php',\n", (string) file_get_contents($this->vendorDir.'/composer/autoload_files.php')); + } + + public function testAutoloadRulesInPackageThatDoesNotExistOnDisk(): void + { + $package = new RootPackage('root/a', '1.0', '1.0'); + $package->setRequires(array( + 'dep/a' => new Link('root/a', 'dep/a', new MatchAllConstraint(), 'requires'), + )); + $dep = new CompletePackage('dep/a', '1.0', '1.0'); + + $this->repository->expects($this->any()) + ->method('getCanonicalPackages') + ->will($this->returnValue(array($dep))); + + $dep->setAutoload(array( + 'psr-0' => array('Foo' => './src'), + )); + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_19'); + + $expectedNamespace = <<<'EOF' + array($vendorDir . '/dep/a/src'), +); + +EOF; + $this->assertStringEqualsFile($this->vendorDir.'/composer/autoload_namespaces.php', $expectedNamespace); + + $dep->setAutoload(array( + 'psr-4' => array('Acme\Foo\\' => './src-psr4'), + )); + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_19'); + + $expectedPsr4 = <<<'EOF' + array($vendorDir . '/dep/a/src-psr4'), +); + +EOF; + $this->assertStringEqualsFile($this->vendorDir.'/composer/autoload_psr4.php', $expectedPsr4); + + $dep->setAutoload(array( + 'classmap' => array('classmap'), + )); + try { + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_19'); + } catch (\RuntimeException $e) { + $this->assertSame('Could not scan for classes inside "'.$this->vendorDir.'/dep/a/classmap" which does not appear to be a file nor a folder', $e->getMessage()); + } + + $dep->setAutoload(array( + 'files' => array('./test.php'), + )); + $this->generator->dump($this->config, $this->repository, $package, $this->im, 'composer', true, '_19'); + $this->assertStringContainsString("\$vendorDir . '/dep/a/test.php',\n", (string) file_get_contents($this->vendorDir.'/composer/autoload_files.php')); + + $package->setAutoload(array( + 'exclude-from-classmap' => array('../excludedroot', 'root/excl'), + )); + $dep->setAutoload(array( + 'exclude-from-classmap' => array('../../excluded', 'foo/bar'), + )); + $map = $this->generator->buildPackageMap($this->im, $package, array($dep)); + $parsed = $this->generator->parseAutoloads($map, $package); + $this->assertSame(array(preg_quote(dirname($this->workingDir)).'/excludedroot($|/)', preg_quote($this->workingDir).'/root/excl($|/)'), $parsed['exclude-from-classmap']); } public function testEmptyPaths(): void @@ -1736,10 +1815,10 @@ EOF; if (null === $expectedFixture) { $this->assertFalse(file_exists($this->vendorDir . '/composer/platform_check.php')); - $this->assertStringNotContainsString("require __DIR__ . '/platform_check.php';", file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringNotContainsString("require __DIR__ . '/platform_check.php';", (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); } else { $this->assertFileContentEquals(__DIR__ . '/Fixtures/platform/' . $expectedFixture . '.php', $this->vendorDir . '/composer/platform_check.php'); - $this->assertStringContainsString("require __DIR__ . '/platform_check.php';", file_get_contents($this->vendorDir.'/composer/autoload_real.php')); + $this->assertStringContainsString("require __DIR__ . '/platform_check.php';", (string) file_get_contents($this->vendorDir.'/composer/autoload_real.php')); } }