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'));
}
}