From 91732eee5578d96937c43d70ff5dd16565fcf9f8 Mon Sep 17 00:00:00 2001 From: James Ho Date: Tue, 17 Sep 2019 14:30:31 +0100 Subject: [PATCH 01/20] 8330 Correct issue where permission umask of files were not set when archiving in zip format with ZipArchiver --- src/Composer/Package/Archiver/ZipArchiver.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Composer/Package/Archiver/ZipArchiver.php b/src/Composer/Package/Archiver/ZipArchiver.php index 65694cb88..b6cfaf73f 100644 --- a/src/Composer/Package/Archiver/ZipArchiver.php +++ b/src/Composer/Package/Archiver/ZipArchiver.php @@ -45,6 +45,18 @@ class ZipArchiver implements ArchiverInterface } else { $zip->addFile($filepath, $localname); } + + /** + * ZipArchive::setExternalAttributesName is available from >= PHP 5.6 + */ + if (PHP_VERSION_ID >= 50600) { + $perms = fileperms($filepath); + + /** + * Ensure to preserve the permission umasks for the filepath in the archive. + */ + $zip->setExternalAttributesName($localname, ZipArchive::OPSYS_UNIX, $perms << 16); + } } if ($zip->close()) { return $target; From a8abdd96392bfc8aa293be98f3c936e58b8e2881 Mon Sep 17 00:00:00 2001 From: xy2z Date: Sat, 12 Oct 2019 22:22:27 +0200 Subject: [PATCH 02/20] Hide installed packages from suggestions when package is not found --- src/Composer/Command/InitCommand.php | 6 ++++++ src/Composer/Repository/ArrayRepository.php | 14 ++++++++++++++ src/Composer/Repository/CompositeRepository.php | 15 +++++++++++++++ src/Composer/Repository/RepositoryInterface.php | 9 +++++++++ 4 files changed, 44 insertions(+) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index f4d6d99d3..7efdff049 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -792,7 +792,13 @@ EOT } $similarPackages = array(); + $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository(); + foreach ($results as $result) { + if ($installedRepo->hasPackageName($result['name'])) { + // Ignore installed package + continue; + } $similarPackages[$result['name']] = levenshtein($package, $result['name']); } asort($similarPackages); diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 4f0409a60..4cfbe42f9 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -132,6 +132,20 @@ class ArrayRepository extends BaseRepository return false; } + /** + * {@inheritDoc} + */ + public function hasPackageName(string $packageName) + { + foreach ($this->getPackages() as $repoPackage) { + if ($packageName === $repoPackage->getName()) { + return true; + } + } + + return false; + } + /** * Adds a new package to the repository * diff --git a/src/Composer/Repository/CompositeRepository.php b/src/Composer/Repository/CompositeRepository.php index ce57504f0..c19a04a7b 100644 --- a/src/Composer/Repository/CompositeRepository.php +++ b/src/Composer/Repository/CompositeRepository.php @@ -64,6 +64,21 @@ class CompositeRepository extends BaseRepository return false; } + /** + * {@inheritdoc} + */ + public function hasPackageName(string $packageName) + { + foreach ($this->repositories as $repository) { + /* @var $repository RepositoryInterface */ + if ($repository->hasPackageName($packageName)) { + return true; + } + } + + return false; + } + /** * {@inheritdoc} */ diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 9a2aaf3b5..8e1717fce 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -35,6 +35,15 @@ interface RepositoryInterface extends \Countable */ public function hasPackage(PackageInterface $package); + /** + * Checks if specified package name is registered (installed). + * + * @param string $package_name package name (vendor/project) + * + * @return bool + */ + public function hasPackageName(string $packageName); + /** * Searches for the first match of a package by name and version. * From 1a0d5dfacd3c0fdafcd4a120ee0b4ae0df5dbed7 Mon Sep 17 00:00:00 2001 From: xy2z Date: Thu, 24 Oct 2019 15:13:17 +0200 Subject: [PATCH 03/20] Now using findPackage() instead of HasPackageName() --- src/Composer/Command/InitCommand.php | 2 +- src/Composer/Repository/ArrayRepository.php | 14 -------------- src/Composer/Repository/CompositeRepository.php | 15 --------------- src/Composer/Repository/RepositoryInterface.php | 9 --------- 4 files changed, 1 insertion(+), 39 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index 7efdff049..d50e90c4e 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -795,7 +795,7 @@ EOT $installedRepo = $this->getComposer()->getRepositoryManager()->getLocalRepository(); foreach ($results as $result) { - if ($installedRepo->hasPackageName($result['name'])) { + if ($installedRepo->findPackage($result['name'], '*')) { // Ignore installed package continue; } diff --git a/src/Composer/Repository/ArrayRepository.php b/src/Composer/Repository/ArrayRepository.php index 4cfbe42f9..4f0409a60 100644 --- a/src/Composer/Repository/ArrayRepository.php +++ b/src/Composer/Repository/ArrayRepository.php @@ -132,20 +132,6 @@ class ArrayRepository extends BaseRepository return false; } - /** - * {@inheritDoc} - */ - public function hasPackageName(string $packageName) - { - foreach ($this->getPackages() as $repoPackage) { - if ($packageName === $repoPackage->getName()) { - return true; - } - } - - return false; - } - /** * Adds a new package to the repository * diff --git a/src/Composer/Repository/CompositeRepository.php b/src/Composer/Repository/CompositeRepository.php index c19a04a7b..ce57504f0 100644 --- a/src/Composer/Repository/CompositeRepository.php +++ b/src/Composer/Repository/CompositeRepository.php @@ -64,21 +64,6 @@ class CompositeRepository extends BaseRepository return false; } - /** - * {@inheritdoc} - */ - public function hasPackageName(string $packageName) - { - foreach ($this->repositories as $repository) { - /* @var $repository RepositoryInterface */ - if ($repository->hasPackageName($packageName)) { - return true; - } - } - - return false; - } - /** * {@inheritdoc} */ diff --git a/src/Composer/Repository/RepositoryInterface.php b/src/Composer/Repository/RepositoryInterface.php index 8e1717fce..9a2aaf3b5 100644 --- a/src/Composer/Repository/RepositoryInterface.php +++ b/src/Composer/Repository/RepositoryInterface.php @@ -35,15 +35,6 @@ interface RepositoryInterface extends \Countable */ public function hasPackage(PackageInterface $package); - /** - * Checks if specified package name is registered (installed). - * - * @param string $package_name package name (vendor/project) - * - * @return bool - */ - public function hasPackageName(string $packageName); - /** * Searches for the first match of a package by name and version. * From a91c946e273f673cc4cbe6e5b4f13f58003251b9 Mon Sep 17 00:00:00 2001 From: xy2z Date: Tue, 12 Nov 2019 16:32:04 +0100 Subject: [PATCH 04/20] Hide not-yet installed packages from "require" and "init" suggestions --- src/Composer/Command/InitCommand.php | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index d50e90c4e..d2e2c9e75 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -436,11 +436,40 @@ EOT } $versionParser = new VersionParser(); + + $composer = $this->getComposer(false); + $installedRepo = ($composer) ? $composer->getRepositoryManager()->getLocalRepository() : null; $io = $this->getIO(); while (null !== $package = $io->ask('Search for a package: ')) { $matches = $this->findPackages($package); if (count($matches)) { + // Exclude existing packages + $existingPackages = []; + foreach ($matches as $position => $foundPackage) { + if ($installedRepo && $installedRepo->findPackage($foundPackage['name'], '*')) { + $existingPackages[] = $position; + continue; + } + + foreach ($requires as $requiredPackage) { + $existingPackageName = substr($requiredPackage, 0, strpos($requiredPackage, ' ')); + + if ($foundPackage['name'] == $existingPackageName) { + $existingPackages[] = $position; + break; + } + } + } + + // Remove existing packages from search results. + if (!empty($existingPackages)) { + foreach ($existingPackages as $position) { + unset($matches[$position]); + } + $matches = array_values($matches); + } + $exactMatch = null; $choices = array(); foreach ($matches as $position => $foundPackage) { From 6add026df04434bd3e6d2b005c27f788732cb2e0 Mon Sep 17 00:00:00 2001 From: Fonata Date: Wed, 25 Dec 2019 20:13:57 +0100 Subject: [PATCH 05/20] Explain the syntax of stability flags --- doc/04-schema.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 7c66813af..6e273c1a0 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -276,10 +276,11 @@ Example: All links are optional fields. -`require` and `require-dev` additionally support stability flags ([root-only](04-schema.md#root-package)). +`require` and `require-dev` additionally support _stability flags_ ([root-only](04-schema.md#root-package)). +They take the form "_constraint_@_stability flag_". These allow you to further restrict or expand the stability of a package beyond the scope of the [minimum-stability](#minimum-stability) setting. You can apply -them to a constraint, or apply them to an empty constraint if you want to +them to a constraint, or apply them to an empty _constraint_ if you want to allow unstable packages of a dependency for example. Example: From 731d94a2a3da66b9dbc0935757dcd285ae225dc0 Mon Sep 17 00:00:00 2001 From: Stephan Vock Date: Sat, 4 Jan 2020 17:01:27 +0000 Subject: [PATCH 06/20] VcsRepositories: mark archived repositories as abandoned --- src/Composer/Repository/Vcs/GitHubDriver.php | 5 +++ src/Composer/Repository/Vcs/GitLabDriver.php | 36 +++++++++++++++ .../Test/Repository/Vcs/GitHubDriverTest.php | 44 ++++++++++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/Composer/Repository/Vcs/GitHubDriver.php b/src/Composer/Repository/Vcs/GitHubDriver.php index e44dd875b..b8948c4c7 100644 --- a/src/Composer/Repository/Vcs/GitHubDriver.php +++ b/src/Composer/Repository/Vcs/GitHubDriver.php @@ -34,6 +34,7 @@ class GitHubDriver extends VcsDriver protected $hasIssues; protected $infoCache = array(); protected $isPrivate = false; + private $isArchived = false; /** * Git Driver @@ -162,6 +163,9 @@ class GitHubDriver extends VcsDriver if (!isset($composer['support']['issues']) && $this->hasIssues) { $composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository); } + if (!isset($composer['abandoned']) && $this->isArchived) { + $composer['abandoned'] = true; + } } if ($this->shouldCache($identifier)) { @@ -425,6 +429,7 @@ class GitHubDriver extends VcsDriver $this->rootIdentifier = 'master'; } $this->hasIssues = !empty($this->repoData['has_issues']); + $this->isArchived = !empty($this->repoData['archived']); } protected function attemptCloneFallback() diff --git a/src/Composer/Repository/Vcs/GitLabDriver.php b/src/Composer/Repository/Vcs/GitLabDriver.php index e346b0306..306bf7124 100644 --- a/src/Composer/Repository/Vcs/GitLabDriver.php +++ b/src/Composer/Repository/Vcs/GitLabDriver.php @@ -119,6 +119,42 @@ class GitLabDriver extends VcsDriver $this->remoteFilesystem = $remoteFilesystem; } + /** + * {@inheritDoc} + */ + public function getComposerInformation($identifier) + { + if ($this->gitDriver) { + return $this->gitDriver->getComposerInformation($identifier); + } + + if (!isset($this->infoCache[$identifier])) { + if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) { + return $this->infoCache[$identifier] = JsonFile::parseJson($res); + } + + $composer = $this->getBaseComposerInformation($identifier); + + if ($composer) { + // specials for gitlab (this data is only available if authentication is provided) + if (!isset($composer['support']['issues']) && isset($this->project['_links']['issues'])) { + $composer['support']['issues'] = $this->project['_links']['issues']; + } + if (!isset($composer['abandoned']) && !empty($this->project['archived'])) { + $composer['abandoned'] = true; + } + } + + if ($this->shouldCache($identifier)) { + $this->cache->write($identifier, json_encode($composer)); + } + + $this->infoCache[$identifier] = $composer; + } + + return $this->infoCache[$identifier]; + } + /** * {@inheritdoc} */ diff --git a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php index ba9c6d4f7..35f37c9ad 100644 --- a/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php +++ b/tests/Composer/Test/Repository/Vcs/GitHubDriverTest.php @@ -207,7 +207,49 @@ class GitHubDriverTest extends TestCase $this->assertEquals($repoUrl, $source['url']); $this->assertEquals($sha, $source['reference']); - $gitHubDriver->getComposerInformation($identifier); + $data = $gitHubDriver->getComposerInformation($identifier); + + $this->assertArrayNotHasKey('abandoned', $data); + } + + public function testPublicRepositoryArchived() + { + $repoUrl = 'http://github.com/composer/packagist'; + $repoApiUrl = 'https://api.github.com/repos/composer/packagist'; + $identifier = 'v0.0.0'; + $sha = 'SOMESHA'; + $composerJsonUrl = 'https://api.github.com/repos/composer/packagist/contents/composer.json?ref=' . $sha; + + $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(); + $io->expects($this->any()) + ->method('isInteractive') + ->will($this->returnValue(true)); + + $remoteFilesystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem') + ->setConstructorArgs(array($io)) + ->getMock(); + + $remoteFilesystem->expects($this->at(0)) + ->method('getContents') + ->with($this->equalTo('github.com'), $this->equalTo($repoApiUrl), $this->equalTo(false)) + ->will($this->returnValue('{"master_branch": "test_master", "owner": {"login": "composer"}, "name": "packagist", "archived": true}')); + + $remoteFilesystem->expects($this->at(1)) + ->method('getContents') + ->with($this->equalTo('github.com'), $this->equalTo($composerJsonUrl), $this->equalTo(false)) + ->will($this->returnValue('{"encoding": "base64", "content": "' . base64_encode('{"name": "composer/packagist"}') . '"}')); + + $repoConfig = array( + 'url' => $repoUrl, + ); + + $gitHubDriver = new GitHubDriver($repoConfig, $io, $this->config, null, $remoteFilesystem); + $gitHubDriver->initialize(); + $this->setAttribute($gitHubDriver, 'tags', array($identifier => $sha)); + + $data = $gitHubDriver->getComposerInformation($sha); + + $this->assertTrue($data['abandoned']); } public function testPrivateRepositoryNoInteraction() From 917680e0d4d89d50277047cbcf7fc3c209057ed0 Mon Sep 17 00:00:00 2001 From: "Chad Wade Day, Jr" Date: Mon, 6 Jan 2020 19:29:00 -0800 Subject: [PATCH 07/20] Make global exec execute commands in working directory --- src/Composer/Command/ExecCommand.php | 10 +++++++++- src/Composer/Console/Application.php | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/ExecCommand.php b/src/Composer/Command/ExecCommand.php index c9184c707..d6e49b4f9 100644 --- a/src/Composer/Command/ExecCommand.php +++ b/src/Composer/Command/ExecCommand.php @@ -39,7 +39,7 @@ class ExecCommand extends BaseCommand ->setHelp( <<setVerbosity(OutputInterface::VERBOSITY_QUIET); } + if (getcwd() !== $this->getApplication()->getWorkingDirectory()) { + try { + chdir($this->getApplication()->getWorkingDirectory()); + } catch (\Exception $e) { + throw new \RuntimeException('Could not switch back to working directory "'.$this->getApplication()->getWorkingDirectory().'"', 0, $e); + } + } + return $dispatcher->dispatchScript('__exec_command', true, $input->getArgument('args')); } } diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 15167af9c..65bb07f62 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -62,6 +62,12 @@ class Application extends BaseApplication private $hasPluginCommands = false; private $disablePluginsByDefault = false; + /** + * @var string Store the working directory so Composer + * can switch back to it if there are issues + */ + private $workingDirectory = ''; + public function __construct() { static $shutdownRegistered = false; @@ -91,6 +97,8 @@ class Application extends BaseApplication $this->io = new NullIO(); + $this->workingDirectory = getcwd(); + parent::__construct('Composer', Composer::getVersion()); } @@ -491,4 +499,14 @@ class Application extends BaseApplication return $commands; } + + /** + * Get the working directoy + * + * @return string + */ + public function getWorkingDirectory() + { + return $this->workingDirectory; + } } From 5ea6fd0bcbb820daf79c4ff8dae24073b4ef8604 Mon Sep 17 00:00:00 2001 From: Adriano Ferreira Date: Tue, 7 Jan 2020 14:14:27 -0200 Subject: [PATCH 08/20] Implemented way to use preferred-install for defining granular preferences through CLI Currently, preferred-install accepts the hash of patterns as the value in the composer.json. I've followed the same approach as used in extra and platform for letting the user define install preferences through CLI in the format: `composer config preferred-install my-organization/stable-package.dist`. --- src/Composer/Command/ConfigCommand.php | 46 +++++++++++++++---- src/Composer/Json/JsonManipulator.php | 8 ++++ .../Test/Json/JsonManipulatorTest.php | 45 ++++++++++++++++++ 3 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index ce6d2d0ba..3577b336e 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -306,14 +306,6 @@ EOT 'process-timeout' => array('is_numeric', 'intval'), 'use-include-path' => array($booleanValidator, $booleanNormalizer), 'use-github-api' => array($booleanValidator, $booleanNormalizer), - 'preferred-install' => array( - function ($val) { - return in_array($val, array('auto', 'source', 'dist'), true); - }, - function ($val) { - return $val; - }, - ), 'store-auths' => array( function ($val) { return in_array($val, array('true', 'false', 'prompt'), true); @@ -458,6 +450,16 @@ EOT }, ), ); + $uniqueOrDotNestedArray = array( + 'preferred-install' => array( + function ($val) { + return in_array($val, array('auto', 'source', 'dist'), true); + }, + function ($val) { + return $val; + }, + ), + ); if ($input->getOption('unset') && (isset($uniqueConfigValues[$settingKey]) || isset($multiConfigValues[$settingKey]))) { if ($settingKey === 'disable-tls' && $this->config->get('disable-tls')) { @@ -478,6 +480,34 @@ EOT return 0; } + if (isset($uniqueOrDotNestedArray[$settingKey])) { + + try { + $this->handleSingleValue($settingKey, $uniqueOrDotNestedArray[$settingKey], $values, 'addConfigSetting'); + } catch ( \RuntimeException $e ) { + + if ( $input->getOption( 'unset' ) ) { + $this->configSource->removeProperty( $settingKey ); + + return 0; + } + + $valueData = explode( '.', $values[0] ); + + if ( ! isset( $valueData[0], $valueData[1] ) ) { + throw new \RuntimeException( 'Invalid pattern format. It should be my-organization/stable-package.dist' ); + } + + list( $validator, $normalizer ) = $uniqueOrDotNestedArray[ $settingKey ]; + if ( ! $validator( $valueData[1] ) ) { + throw new \RuntimeException( 'Invalid option for install method. It accepts only: auto, source, and dist' ); + } + + $this->configSource->addProperty( 'config.' . $settingKey . '.' . $valueData[0], $valueData[1] ); + } + + return 0; + } // handle properties $uniqueProps = array( diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index e64a56f71..8c6efd325 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -167,6 +167,10 @@ class JsonManipulator public function addProperty($name, $value) { + if ( substr( $name, 0, 7 ) === 'config.' ) { + return $this->addConfigSetting( substr( $name, 7 ), $value ); + } + if (substr($name, 0, 6) === 'extra.') { return $this->addSubNode('extra', substr($name, 6), $value); } @@ -180,6 +184,10 @@ class JsonManipulator public function removeProperty($name) { + if ( substr( $name, 0, 7 ) === 'config.' ) { + return $this->removeConfigSetting( substr( $name, 7 ) ); + } + if (substr($name, 0, 6) === 'extra.') { return $this->removeSubNode('extra', substr($name, 6)); } diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 8bc7831af..4f70e8669 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -1814,6 +1814,51 @@ class JsonManipulatorTest extends TestCase ', $manipulator->getContents()); } + public function testAddConfigWithPackage() { + $manipulator = new JsonManipulator('{ + "repositories": [ + { + "type": "package", + "package": { + "authors": [], + "extra": { + "package-xml": "package.xml" + } + } + } + ], + "config": { + "platform": { + "php": "5.3.9" + } + } +}'); + + $this->assertTrue($manipulator->addProperty('config.preferred-install.my-organization/stable-package', 'dist')); + $this->assertEquals('{ + "repositories": [ + { + "type": "package", + "package": { + "authors": [], + "extra": { + "package-xml": "package.xml" + } + } + } + ], + "config": { + "platform": { + "php": "5.3.9" + }, + "preferred-install": { + "my-organization/stable-package": "dist" + } + } +} +', $manipulator->getContents()); + } + public function testAddRepositoryCanInitializeEmptyRepositories() { $manipulator = new JsonManipulator('{ From 607d491921f5b86974353f35a2b19333217533ff Mon Sep 17 00:00:00 2001 From: Adriano Ferreira Date: Fri, 10 Jan 2020 09:11:38 -0200 Subject: [PATCH 09/20] Implemented way to add packages into suggest through CLI --- src/Composer/Command/ConfigCommand.php | 12 ++++++ src/Composer/Json/JsonManipulator.php | 8 ++++ .../Test/Json/JsonManipulatorTest.php | 40 +++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index ce6d2d0ba..0d408be14 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -601,6 +601,18 @@ EOT return 0; } + if (preg_match('/^suggest\.(.+)/', $settingKey, $matches)) { + if ($input->getOption('unset')) { + $this->configSource->removeProperty($settingKey); + + return 0; + } + + $this->configSource->addProperty($settingKey, $values[0]); + + return 0; + } + // handle platform if (preg_match('/^platform\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { diff --git a/src/Composer/Json/JsonManipulator.php b/src/Composer/Json/JsonManipulator.php index e64a56f71..e1ee43485 100644 --- a/src/Composer/Json/JsonManipulator.php +++ b/src/Composer/Json/JsonManipulator.php @@ -167,6 +167,10 @@ class JsonManipulator public function addProperty($name, $value) { + if (substr($name, 0, 8) === 'suggest.') { + return $this->addSubNode('suggest', substr($name, 8), $value); + } + if (substr($name, 0, 6) === 'extra.') { return $this->addSubNode('extra', substr($name, 6), $value); } @@ -180,6 +184,10 @@ class JsonManipulator public function removeProperty($name) { + if (substr($name, 0, 8) === 'suggest.') { + return $this->removeSubNode('suggest', substr($name, 8)); + } + if (substr($name, 0, 6) === 'extra.') { return $this->removeSubNode('extra', substr($name, 6)); } diff --git a/tests/Composer/Test/Json/JsonManipulatorTest.php b/tests/Composer/Test/Json/JsonManipulatorTest.php index 8bc7831af..914de1882 100644 --- a/tests/Composer/Test/Json/JsonManipulatorTest.php +++ b/tests/Composer/Test/Json/JsonManipulatorTest.php @@ -1814,6 +1814,46 @@ class JsonManipulatorTest extends TestCase ', $manipulator->getContents()); } + public function testAddSuggestWithPackage() + { + $manipulator = new JsonManipulator('{ + "repositories": [ + { + "type": "package", + "package": { + "authors": [], + "extra": { + "package-xml": "package.xml" + } + } + } + ], + "suggest": { + "package": "Description" + } +}'); + + $this->assertTrue($manipulator->addProperty('suggest.new-package', 'new-description')); + $this->assertEquals('{ + "repositories": [ + { + "type": "package", + "package": { + "authors": [], + "extra": { + "package-xml": "package.xml" + } + } + } + ], + "suggest": { + "package": "Description", + "new-package": "new-description" + } +} +', $manipulator->getContents()); + } + public function testAddRepositoryCanInitializeEmptyRepositories() { $manipulator = new JsonManipulator('{ From 4e667f891b7bea0550b83c5cf1aa087a3736bb5c Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 13 Jan 2020 13:45:04 +0100 Subject: [PATCH 10/20] Fix 5.3 build --- tests/Composer/Test/Util/ProcessExecutorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Composer/Test/Util/ProcessExecutorTest.php b/tests/Composer/Test/Util/ProcessExecutorTest.php index 29723b4a5..63ed0b7de 100644 --- a/tests/Composer/Test/Util/ProcessExecutorTest.php +++ b/tests/Composer/Test/Util/ProcessExecutorTest.php @@ -108,7 +108,7 @@ class ProcessExecutorTest extends TestCase public function testConsoleIODoesNotFormatSymfonyConsoleStyle() { $output = new BufferedOutput(OutputInterface::VERBOSITY_NORMAL, true); - $process = new ProcessExecutor(new ConsoleIO(new ArrayInput([]), $output, new HelperSet([]))); + $process = new ProcessExecutor(new ConsoleIO(new ArrayInput(array()), $output, new HelperSet(array()))); $process->execute('echo \'foo\''); $this->assertSame('foo'.PHP_EOL, $output->fetch()); From ef6ef8ac0fcbb4c2c45202d87a791b63b5528f97 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 13 Jan 2020 14:19:37 +0100 Subject: [PATCH 11/20] Hint at the partial update command, fixes #8508, refs #8332 --- src/Composer/Command/ValidateCommand.php | 2 +- src/Composer/Installer.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/ValidateCommand.php b/src/Composer/Command/ValidateCommand.php index 5aba74adf..7ec15ccb9 100644 --- a/src/Composer/Command/ValidateCommand.php +++ b/src/Composer/Command/ValidateCommand.php @@ -93,7 +93,7 @@ EOT $composer = Factory::create($io, $file, $input->hasParameterOption('--no-plugins')); $locker = $composer->getLocker(); if ($locker->isLocked() && !$locker->isFresh()) { - $lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update`.'; + $lockErrors[] = 'The lock file is not up to date with the latest changes in composer.json, it is recommended that you run `composer update` or `composer update `.'; } $this->outputResult($io, $file, $errors, $warnings, $checkPublish, $publishErrors, $checkLock, $lockErrors, true, $isStrict); diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 11edcbe72..e00b3817a 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -447,7 +447,7 @@ class Installer $this->io->writeError('Installing dependencies'.($this->devMode ? ' (including require-dev)' : '').' from lock file'); if (!$this->locker->isFresh()) { - $this->io->writeError('Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. Run update to update them.', true, IOInterface::QUIET); + $this->io->writeError('Warning: The lock file is not up to date with the latest changes in composer.json. You may be getting outdated dependencies. It is recommended that you run `composer update` or `composer update `.', true, IOInterface::QUIET); } foreach ($lockedRepository->getPackages() as $package) { From 8d24b61bef4839104db7f05c23400e598a8915e4 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 13 Jan 2020 15:50:34 +0100 Subject: [PATCH 12/20] Tweak and add comments to the working dir fix with global exec, refs #8515 --- src/Composer/Command/ExecCommand.php | 7 +++++-- src/Composer/Console/Application.php | 14 +++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Composer/Command/ExecCommand.php b/src/Composer/Command/ExecCommand.php index d6e49b4f9..d530def66 100644 --- a/src/Composer/Command/ExecCommand.php +++ b/src/Composer/Command/ExecCommand.php @@ -92,9 +92,12 @@ EOT $output->setVerbosity(OutputInterface::VERBOSITY_QUIET); } - if (getcwd() !== $this->getApplication()->getWorkingDirectory()) { + // If the CWD was modified, we restore it to what it was initially, as it was + // most likely modified by the global command, and we want exec to run in the local working directory + // not the global one + if (getcwd() !== $this->getApplication()->getInitialWorkingDirectory()) { try { - chdir($this->getApplication()->getWorkingDirectory()); + chdir($this->getApplication()->getInitialWorkingDirectory()); } catch (\Exception $e) { throw new \RuntimeException('Could not switch back to working directory "'.$this->getApplication()->getWorkingDirectory().'"', 0, $e); } diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 65bb07f62..6de2ee5d4 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -63,10 +63,9 @@ class Application extends BaseApplication private $disablePluginsByDefault = false; /** - * @var string Store the working directory so Composer - * can switch back to it if there are issues + * @var string Store the initial working directory at startup time */ - private $workingDirectory = ''; + private $initialWorkingDirectory = ''; public function __construct() { @@ -97,7 +96,7 @@ class Application extends BaseApplication $this->io = new NullIO(); - $this->workingDirectory = getcwd(); + $this->initialWorkingDirectory = getcwd(); parent::__construct('Composer', Composer::getVersion()); } @@ -139,6 +138,7 @@ class Application extends BaseApplication if ($newWorkDir = $this->getNewWorkingDir($input)) { $oldWorkingDir = getcwd(); chdir($newWorkDir); + $this->initialWorkingDirectory = $newWorkDir; $io->writeError('Changed CWD to ' . getcwd(), true, IOInterface::DEBUG); } @@ -501,12 +501,12 @@ class Application extends BaseApplication } /** - * Get the working directoy + * Get the working directoy at startup time * * @return string */ - public function getWorkingDirectory() + public function getInitialWorkingDirectory() { - return $this->workingDirectory; + return $this->initialWorkingDirectory; } } From e8426d2c013562c187f2c2e1a26ac6fdf2d7ee45 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 13 Jan 2020 17:44:24 +0100 Subject: [PATCH 13/20] Adjust config handling of suggest, refs #8520 --- src/Composer/Command/ConfigCommand.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 0d408be14..13a67b550 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -111,6 +111,14 @@ To disable packagist: You can alter repositories in the global config.json file by passing in the --global option. +To add or edit suggested packages you can use: + + %command.full_name% suggest.package reason for the suggestion + +To add or edit extra properties you can use: + + %command.full_name% extra.property value + To edit the file in an external editor: %command.full_name% --editor @@ -601,6 +609,7 @@ EOT return 0; } + // handle suggest if (preg_match('/^suggest\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { $this->configSource->removeProperty($settingKey); @@ -608,7 +617,14 @@ EOT return 0; } - $this->configSource->addProperty($settingKey, $values[0]); + $this->configSource->addProperty($settingKey, implode(' ', $values)); + + return 0; + } + + // handle unsetting extra/suggest + if (in_array($settingKey, array('suggest', 'extra'), true) && $input->getOption('unset')) { + $this->configSource->removeProperty($settingKey); return 0; } @@ -625,6 +641,8 @@ EOT return 0; } + + // handle unsetting platform if ($settingKey === 'platform' && $input->getOption('unset')) { $this->configSource->removeConfigSetting($settingKey); From f5dab18b7d392ce17c17aa0c29f0a2559aca3390 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Mon, 13 Jan 2020 18:21:57 +0100 Subject: [PATCH 14/20] Remove dead code, refs #8517 --- src/Composer/Command/ConfigCommand.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 3ddd7cb9d..7f79f3251 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -466,16 +466,6 @@ EOT }, ), ); - $uniqueOrDotNestedArray = array( - 'preferred-install' => array( - function ($val) { - return in_array($val, array('auto', 'source', 'dist'), true); - }, - function ($val) { - return $val; - }, - ), - ); if ($input->getOption('unset') && (isset($uniqueConfigValues[$settingKey]) || isset($multiConfigValues[$settingKey]))) { if ($settingKey === 'disable-tls' && $this->config->get('disable-tls')) { From 3791a574a242704ecec2ea7a11da74d264bbb86c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Tue, 7 Jan 2020 09:14:35 +0100 Subject: [PATCH 15/20] Provide partial packages names if available, closes #8516, fixes #8526 --- .../Repository/ComposerRepository.php | 5 ++++ .../Repository/ComposerRepositoryTest.php | 29 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index c595b20c2..a58896eeb 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -249,6 +249,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $this->loadProviderListings($this->loadRootServerFile()); } + if ($this->hasPartialPackages && null === $this->partialPackagesByName) { + $this->initializePartialPackages(); + return array_keys($this->partialPackagesByName); + } + if ($this->lazyProvidersUrl) { // Can not determine list of provided packages for lazy repositories return array(); diff --git a/tests/Composer/Test/Repository/ComposerRepositoryTest.php b/tests/Composer/Test/Repository/ComposerRepositoryTest.php index 8e9216b35..4ec826334 100644 --- a/tests/Composer/Test/Repository/ComposerRepositoryTest.php +++ b/tests/Composer/Test/Repository/ComposerRepositoryTest.php @@ -271,4 +271,33 @@ class ComposerRepositoryTest extends TestCase ), ); } + + public function testGetProviderNamesWillReturnPartialPackageNames() + { + $rfs = $this->getMockBuilder('Composer\Util\RemoteFilesystem') + ->disableOriginalConstructor() + ->getMock(); + + $rfs->expects($this->at(0)) + ->method('getContents') + ->with('example.org', 'http://example.org/packages.json', false) + ->willReturn(json_encode(array( + 'providers-lazy-url' => '/foo/p/%package%.json', + 'packages' => array('foo/bar' => array( + 'dev-branch' => array(), + 'v1.0.0' => array(), + )) + ))); + + $repository = new ComposerRepository( + array('url' => 'http://example.org/packages.json'), + new NullIO(), + FactoryMock::createConfig(), + null, + $rfs + ); + + $this->assertTrue($repository->hasProviders()); + $this->assertEquals(array('foo/bar'), $repository->getProviderNames()); + } } From 0b767e0b83efa80005a781a8141c94016365f907 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 14 Jan 2020 09:31:27 +0100 Subject: [PATCH 16/20] Allow calling getProviderNames multiple times, refs #8516 --- src/Composer/Repository/ComposerRepository.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Composer/Repository/ComposerRepository.php b/src/Composer/Repository/ComposerRepository.php index a58896eeb..649c2f115 100644 --- a/src/Composer/Repository/ComposerRepository.php +++ b/src/Composer/Repository/ComposerRepository.php @@ -249,8 +249,11 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito $this->loadProviderListings($this->loadRootServerFile()); } - if ($this->hasPartialPackages && null === $this->partialPackagesByName) { - $this->initializePartialPackages(); + if ($this->hasPartialPackages) { + if (null === $this->partialPackagesByName) { + $this->initializePartialPackages(); + } + return array_keys($this->partialPackagesByName); } From 6c795dedc0c72ffd2e16b43ef04a839f534eb66b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 14 Jan 2020 09:36:53 +0100 Subject: [PATCH 17/20] Add openssl version to diagnose command, refs #8506 --- src/Composer/Command/DiagnoseCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Composer/Command/DiagnoseCommand.php b/src/Composer/Command/DiagnoseCommand.php index 9bc271fa1..6c9158630 100644 --- a/src/Composer/Command/DiagnoseCommand.php +++ b/src/Composer/Command/DiagnoseCommand.php @@ -172,6 +172,8 @@ EOT $io->write(sprintf('PHP binary path: %s', PHP_BINARY)); } + $io->write(sprintf('OpenSSL version: %s', OPENSSL_VERSION_TEXT)); + return $this->exitCode; } From a2dadb91bf87a0d5cccb98fdca1e4fd66459a896 Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 14 Jan 2020 12:52:20 +0100 Subject: [PATCH 18/20] Return two packages in PathRepository when on a feature branch, one for feature branch and one for the source branch it came from, fixes #8498, fixes #8477 --- .../Package/Version/VersionGuesser.php | 22 ++++++++++++++++++- src/Composer/Repository/PathRepository.php | 15 +++++++++---- .../Package/Version/VersionGuesserTest.php | 14 +++++++++--- 3 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/Composer/Package/Version/VersionGuesser.php b/src/Composer/Package/Version/VersionGuesser.php index e6ff84965..f35aabeb2 100644 --- a/src/Composer/Package/Version/VersionGuesser.php +++ b/src/Composer/Package/Version/VersionGuesser.php @@ -59,7 +59,7 @@ class VersionGuesser * @param array $packageConfig * @param string $path Path to guess into * - * @return null|array versionData, 'version', 'pretty_version' and 'commit' keys + * @return null|array versionData, 'version', 'pretty_version' and 'commit' keys, if the version is a feature branch, 'feature_version' and 'feature_pretty_version' keys may also be returned */ public function guessVersion(array $packageConfig, $path) { @@ -88,10 +88,18 @@ class VersionGuesser private function postprocess(array $versionData) { + if (!empty($versionData['feature_version']) && $versionData['feature_version'] === $versionData['version'] && $versionData['feature_pretty_version'] === $versionData['feature_pretty_version']) { + unset($versionData['feature_version'], $versionData['feature_pretty_version']); + } + if ('-dev' === substr($versionData['version'], -4) && preg_match('{\.9{7}}', $versionData['version'])) { $versionData['pretty_version'] = preg_replace('{(\.9{7})+}', '.x', $versionData['version']); } + if (!empty($versionData['feature_version']) && '-dev' === substr($versionData['feature_version'], -4) && preg_match('{\.9{7}}', $versionData['feature_version'])) { + $versionData['feature_pretty_version'] = preg_replace('{(\.9{7})+}', '.x', $versionData['feature_version']); + } + return $versionData; } @@ -101,6 +109,8 @@ class VersionGuesser $commit = null; $version = null; $prettyVersion = null; + $featureVersion = null; + $featurePrettyVersion = null; $isDetached = false; // try to fetch current version from git branch @@ -135,6 +145,8 @@ class VersionGuesser } if ($isFeatureBranch) { + $featureVersion = $version; + $featurePrettyVersion = $prettyVersion; // try to find the best (nearest) version branch to assume this feature's version $result = $this->guessFeatureVersion($packageConfig, $version, $branches, 'git rev-list %candidate%..%branch%', $path); $version = $result['version']; @@ -147,6 +159,8 @@ class VersionGuesser if ($result) { $version = $result['version']; $prettyVersion = $result['pretty_version']; + $featureVersion = null; + $featurePrettyVersion = null; } } @@ -157,6 +171,10 @@ class VersionGuesser } } + if ($featureVersion) { + return array('version' => $version, 'commit' => $commit, 'pretty_version' => $prettyVersion, 'feature_version' => $featureVersion, 'feature_pretty_version' => $featurePrettyVersion); + } + return array('version' => $version, 'commit' => $commit, 'pretty_version' => $prettyVersion); } @@ -198,6 +216,8 @@ class VersionGuesser // try to find the best (nearest) version branch to assume this feature's version $result = $this->guessFeatureVersion($packageConfig, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"', $path); $result['commit'] = ''; + $result['feature_version'] = $version; + $result['feature_pretty_version'] = $version; return $result; } diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php index 6e218f159..4e6bb05b7 100644 --- a/src/Composer/Repository/PathRepository.php +++ b/src/Composer/Repository/PathRepository.php @@ -153,19 +153,26 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn } } + $output = ''; + if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) { + $package['dist']['reference'] = trim($output); + } + if (!isset($package['version'])) { $versionData = $this->versionGuesser->guessVersion($package, $path); if (is_array($versionData) && $versionData['pretty_version']) { + // if there is a feature branch detected, we add a second packages with the feature branch version + if (!empty($versionData['feature_pretty_version'])) { + $package['version'] = $versionData['feature_pretty_version']; + $this->addPackage($this->loader->load($package)); + } + $package['version'] = $versionData['pretty_version']; } else { $package['version'] = 'dev-master'; } } - $output = ''; - if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) { - $package['dist']['reference'] = trim($output); - } $package = $this->loader->load($package); $this->addPackage($package); } diff --git a/tests/Composer/Test/Package/Version/VersionGuesserTest.php b/tests/Composer/Test/Package/Version/VersionGuesserTest.php index fe229d679..6eeafd122 100644 --- a/tests/Composer/Test/Package/Version/VersionGuesserTest.php +++ b/tests/Composer/Test/Package/Version/VersionGuesserTest.php @@ -126,13 +126,15 @@ class VersionGuesserTest extends TestCase $this->assertEquals("9999999-dev", $versionArray['version']); $this->assertEquals("dev-master", $versionArray['pretty_version']); + $this->assertArrayNotHasKey('feature_version', $versionArray); + $this->assertArrayNotHasKey('feature_pretty_version', $versionArray); $this->assertEquals($commitHash, $versionArray['commit']); } public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNaming() { $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; - $anotherCommitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; + $anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea'; $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') ->setMethods(array('execute')) @@ -172,12 +174,14 @@ class VersionGuesserTest extends TestCase $this->assertEquals("dev-arbitrary", $versionArray['version']); $this->assertEquals($anotherCommitHash, $versionArray['commit']); + $this->assertEquals("dev-current", $versionArray['feature_version']); + $this->assertEquals("dev-current", $versionArray['feature_pretty_version']); } public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNamingRegex() { $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; - $anotherCommitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; + $anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea'; $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') ->setMethods(array('execute')) @@ -217,12 +221,14 @@ class VersionGuesserTest extends TestCase $this->assertEquals("dev-latest-testing", $versionArray['version']); $this->assertEquals($anotherCommitHash, $versionArray['commit']); + $this->assertEquals("dev-current", $versionArray['feature_version']); + $this->assertEquals("dev-current", $versionArray['feature_pretty_version']); } public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNamingWhenOnNonFeatureBranch() { $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; - $anotherCommitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; + $anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea'; $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') ->setMethods(array('execute')) @@ -251,6 +257,8 @@ class VersionGuesserTest extends TestCase $this->assertEquals("dev-latest-testing", $versionArray['version']); $this->assertEquals($commitHash, $versionArray['commit']); + $this->assertArrayNotHasKey('feature_version', $versionArray); + $this->assertArrayNotHasKey('feature_pretty_version', $versionArray); } public function testDetachedHeadBecomesDevHash() From 669033f1e7773dcd1f363e2cd7f2aca14f61be4e Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 14 Jan 2020 14:41:31 +0100 Subject: [PATCH 19/20] Collect existing packages only once, refs #8372 --- src/Composer/Command/InitCommand.php | 39 ++++++++++++---------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/Composer/Command/InitCommand.php b/src/Composer/Command/InitCommand.php index b35d774df..358eb848c 100644 --- a/src/Composer/Command/InitCommand.php +++ b/src/Composer/Command/InitCommand.php @@ -439,38 +439,32 @@ EOT $versionParser = new VersionParser(); + // Collect existing packages $composer = $this->getComposer(false); - $installedRepo = ($composer) ? $composer->getRepositoryManager()->getLocalRepository() : null; + $installedRepo = $composer ? $composer->getRepositoryManager()->getLocalRepository() : null; + $existingPackages = []; + if ($installedRepo) { + foreach ($installedRepo->getPackages() as $package) { + $existingPackages[] = $package->getName(); + } + } + foreach ($requires as $requiredPackage) { + $existingPackages[] = substr($requiredPackage, 0, strpos($requiredPackage, ' ')); + } + unset($composer, $installedRepo, $requiredPackage); + $io = $this->getIO(); while (null !== $package = $io->ask('Search for a package: ')) { $matches = $this->findPackages($package); if (count($matches)) { - // Exclude existing packages - $existingPackages = []; - foreach ($matches as $position => $foundPackage) { - if ($installedRepo && $installedRepo->findPackage($foundPackage['name'], '*')) { - $existingPackages[] = $position; - continue; - } - - foreach ($requires as $requiredPackage) { - $existingPackageName = substr($requiredPackage, 0, strpos($requiredPackage, ' ')); - - if ($foundPackage['name'] == $existingPackageName) { - $existingPackages[] = $position; - break; - } - } - } - // Remove existing packages from search results. - if (!empty($existingPackages)) { - foreach ($existingPackages as $position) { + foreach ($matches as $position => $foundPackage) { + if (in_array($foundPackage['name'], $existingPackages, true)) { unset($matches[$position]); } - $matches = array_values($matches); } + $matches = array_values($matches); $exactMatch = null; $choices = array(); @@ -569,6 +563,7 @@ EOT if (false !== $package) { $requires[] = $package; + $existingPackages[] = substr($package, 0, strpos($package, ' ')); } } } From 4b6c25d4bc33d49097320e29e6e5705b12e9d6ef Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Tue, 14 Jan 2020 15:35:52 +0100 Subject: [PATCH 20/20] Use Authorization header instead of deprecated access_token query param, fixes #8454 --- src/Composer/Util/RemoteFilesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index 6d343f7a1..4885b7530 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/src/Composer/Util/RemoteFilesystem.php @@ -278,7 +278,7 @@ class RemoteFilesystem if (isset($options['github-token'])) { // only add the access_token if it is actually a github URL (in case we were redirected to S3) if (preg_match('{^https?://([a-z0-9-]+\.)*github\.com/}', $fileUrl)) { - $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token']; + $options['http']['header'][] = 'Authorization: token '.$options['github-token']; } unset($options['github-token']); }