From bf08b6eb90a4b8838eac9d2d1c0142109a0d9380 Mon Sep 17 00:00:00 2001 From: Steve Buzonas Date: Thu, 26 Feb 2015 11:20:59 -0500 Subject: [PATCH 1/6] add support for package based install type preferences --- res/composer-schema.json | 4 ++-- src/Composer/Config.php | 14 +++++++++++ src/Composer/Downloader/DownloadManager.php | 26 ++++++++++++++++++++- src/Composer/Factory.php | 6 ++++- 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index ac84d5eb0..2e74a97f7 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -117,8 +117,8 @@ "description": "If true, the Composer autoloader will also look for classes in the PHP include path." }, "preferred-install": { - "type": "string", - "description": "The install method Composer will prefer to use, defaults to auto and can be any of source, dist or auto." + "type": ["string", "object"], + "description": "The install method Composer will prefer to use, defaults to auto and can be any of source, dist, auto, or a hash of {\"pattern\": \"preference\"}." }, "notify-on-install": { "type": "boolean", diff --git a/src/Composer/Config.php b/src/Composer/Config.php index b6d942c51..ead9c67a8 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -108,6 +108,20 @@ class Config foreach ($config['config'] as $key => $val) { if (in_array($key, array('github-oauth', 'http-basic')) && isset($this->config[$key])) { $this->config[$key] = array_merge($this->config[$key], $val); + } elseif ('preferred-install' === $key && isset($this->config[$key])) { + if (is_string($val)) { + $val = array('*' => $val); + } + if (is_string($this->config[$key])) { + $this->config[$key] = array('*' => $this->config[$key]); + } + $this->config[$key] = array_merge($this->config[$key], $val); + // the full match pattern needs to be last + if (isset($this->config[$key]['*'])) { + $wildcard = $this->config[$key]['*']; + unset($this->config[$key]['*']); + $this->config[$key]['*'] = $wildcard; + } } else { $this->config[$key] = $val; } diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 5c0980725..675b7a051 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -26,6 +26,7 @@ class DownloadManager private $io; private $preferDist = false; private $preferSource = false; + private $packagePreferences = array(); private $filesystem; private $downloaders = array(); @@ -69,6 +70,19 @@ class DownloadManager return $this; } + /** + * Sets fine tuned preference settings for package level source/dist selection. + * + * @param array $preferences array of preferences by package patterns + * @return DownloadManager + */ + public function setPreferences(array $preferences) + { + $this->packagePreferences = $preferences; + + return $this; + } + /** * Sets whether to output download progress information for all registered * downloaders @@ -184,7 +198,17 @@ class DownloadManager throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } - if ((!$package->isDev() || $this->preferDist) && !$preferSource) { + if (!$this->preferDist && !$preferSource) { + foreach ($this->packagePreferences as $pattern => $preference) { + $pattern = '{^'.str_replace('*', '.*', $pattern).'$}i'; + if (preg_match($pattern, $package->getName())) { + if ('dist' === $preference || (!$package->isDev() && 'auto' === $preference)) { + $sources = array_reverse($sources); + } + break; + } + } + } elseif ((!$package->isDev() || $this->preferDist) && !$preferSource) { $sources = array_reverse($sources); } diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index da0a9ef63..aeb83f0de 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -383,7 +383,7 @@ class Factory } $dm = new Downloader\DownloadManager($io); - switch ($config->get('preferred-install')) { + switch ($preferred = $config->get('preferred-install')) { case 'dist': $dm->setPreferDist(true); break; @@ -396,6 +396,10 @@ class Factory break; } + if (is_array($preferred)) { + $dm->setPreferences($preferred); + } + $dm->setDownloader('git', new Downloader\GitDownloader($io, $config)); $dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config)); $dm->setDownloader('hg', new Downloader\HgDownloader($io, $config)); From 124739d055dc110483137527ba546bfa62f2a8fb Mon Sep 17 00:00:00 2001 From: Steve Buzonas Date: Sat, 18 Apr 2015 18:25:59 -0400 Subject: [PATCH 2/6] fix preference resolution to be backwards compatible --- src/Composer/Config.php | 4 +++ src/Composer/Downloader/DownloadManager.php | 34 ++++++++++++++------- tests/Composer/Test/ConfigTest.php | 20 ++++++++++++ 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index ead9c67a8..2d823cea3 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -21,6 +21,10 @@ class Config { const RELATIVE_PATHS = 1; + const INSTALL_PREFERENCE_AUTO = 'auto'; + const INSTALL_PREFERENCE_DIST = 'dist'; + const INSTALL_PREFERENCE_SOURCE = 'source'; + public static $defaultConfig = array( 'process-timeout' => 300, 'use-include-path' => false, diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 675b7a051..857a404dc 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -12,6 +12,7 @@ namespace Composer\Downloader; +use Composer\Config; use Composer\Package\PackageInterface; use Composer\IO\IOInterface; use Composer\Util\Filesystem; @@ -198,17 +199,7 @@ class DownloadManager throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } - if (!$this->preferDist && !$preferSource) { - foreach ($this->packagePreferences as $pattern => $preference) { - $pattern = '{^'.str_replace('*', '.*', $pattern).'$}i'; - if (preg_match($pattern, $package->getName())) { - if ('dist' === $preference || (!$package->isDev() && 'auto' === $preference)) { - $sources = array_reverse($sources); - } - break; - } - } - } elseif ((!$package->isDev() || $this->preferDist) && !$preferSource) { + if (Config::INSTALL_PREFERENCE_DIST === $this->resolvePackageInstallPreference($package, $preferSource)) { $sources = array_reverse($sources); } @@ -296,4 +287,25 @@ class DownloadManager $downloader->remove($package, $targetDir); } } + + protected function resolvePackageInstallPreference(PackageInterface $package, $preferSource = false) + { + if ($this->preferSource || $preferSource) { + return Config::INSTALL_PREFERENCE_SOURCE; + } + if ($this->preferDist) { + return Config::INSTALL_PREFERENCE_DIST; + } + foreach ($this->packagePreferences as $pattern => $preference) { + $pattern = '{^'.str_replace('*', '.*', $pattern).'$}i'; + if (preg_match($pattern, $package->getName())) { + if (Config::INSTALL_PREFERENCE_DIST === $preference || (!$package->isDev() && Config::INSTALL_PREFERENCE_AUTO === $preference)) { + return Config::INSTALL_PREFERENCE_DIST; + } + return Config::INSTALL_PREFERENCE_SOURCE; + } + } + + return $package->isDev() ? Config::INSTALL_PREFERENCE_SOURCE : Config::INSTALL_PREFERENCE_DIST; + } } diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index e50af5e87..fb7a5ef22 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -112,6 +112,26 @@ class ConfigTest extends \PHPUnit_Framework_TestCase return $data; } + public function testPreferredInstallAsString() + { + $config = new Config(false); + $config->merge(array('config' => array('preferred-install' => 'source'))); + + $this->assertEquals(array('*' => 'source'), $config->get('preferred-install')); + } + + public function testMergePreferredInstall() + { + $config = new Config(false); + $config->merge(array('config' => array('preferred-install' => 'dist'))); + $config->merge(array('config' => array('preferred-install' => array('foo/*' => 'source')))); + + // This assertion needs to make sure full wildcard preferences are placed last + // Handled by composer because we convert string preferences for BC, all other + // care for ordering and collision prevention is up to the user + $this->assertEquals(array('foo/*' => 'source', '*' => 'dist'), $config->get('preferred-install')); + } + public function testMergeGithubOauth() { $config = new Config(false); From 73c1f8c0e009a4be089bc771f37eea69423a9009 Mon Sep 17 00:00:00 2001 From: Steve Buzonas Date: Sat, 18 Apr 2015 18:31:16 -0400 Subject: [PATCH 3/6] prefer strings for install handling when possible --- src/Composer/Config.php | 28 ++++++++++++++++------------ tests/Composer/Test/ConfigTest.php | 3 ++- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 2d823cea3..a95d5a33d 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -113,18 +113,22 @@ class Config if (in_array($key, array('github-oauth', 'http-basic')) && isset($this->config[$key])) { $this->config[$key] = array_merge($this->config[$key], $val); } elseif ('preferred-install' === $key && isset($this->config[$key])) { - if (is_string($val)) { - $val = array('*' => $val); - } - if (is_string($this->config[$key])) { - $this->config[$key] = array('*' => $this->config[$key]); - } - $this->config[$key] = array_merge($this->config[$key], $val); - // the full match pattern needs to be last - if (isset($this->config[$key]['*'])) { - $wildcard = $this->config[$key]['*']; - unset($this->config[$key]['*']); - $this->config[$key]['*'] = $wildcard; + if (is_array($val) || is_array($this->config[$key])) { + if (is_string($val)) { + $val = array('*' => $val); + } + if (is_string($this->config[$key])) { + $this->config[$key] = array('*' => $this->config[$key]); + } + $this->config[$key] = array_merge($this->config[$key], $val); + // the full match pattern needs to be last + if (isset($this->config[$key]['*'])) { + $wildcard = $this->config[$key]['*']; + unset($this->config[$key]['*']); + $this->config[$key]['*'] = $wildcard; + } + } else { + $this->config[$key] = $val; } } else { $this->config[$key] = $val; diff --git a/tests/Composer/Test/ConfigTest.php b/tests/Composer/Test/ConfigTest.php index fb7a5ef22..441344c29 100644 --- a/tests/Composer/Test/ConfigTest.php +++ b/tests/Composer/Test/ConfigTest.php @@ -116,8 +116,9 @@ class ConfigTest extends \PHPUnit_Framework_TestCase { $config = new Config(false); $config->merge(array('config' => array('preferred-install' => 'source'))); + $config->merge(array('config' => array('preferred-install' => 'dist'))); - $this->assertEquals(array('*' => 'source'), $config->get('preferred-install')); + $this->assertEquals('dist', $config->get('preferred-install')); } public function testMergePreferredInstall() From b44c3bee525ff9af5bdd6acd5a93daa9ba613712 Mon Sep 17 00:00:00 2001 From: Steve Buzonas Date: Sat, 18 Apr 2015 20:59:51 -0400 Subject: [PATCH 4/6] add tests to cover different use cases of package install preferences --- src/Composer/Downloader/DownloadManager.php | 17 +- .../Test/Downloader/DownloadManagerTest.php | 360 ++++++++++++++++++ 2 files changed, 369 insertions(+), 8 deletions(-) diff --git a/src/Composer/Downloader/DownloadManager.php b/src/Composer/Downloader/DownloadManager.php index 857a404dc..dae049032 100644 --- a/src/Composer/Downloader/DownloadManager.php +++ b/src/Composer/Downloader/DownloadManager.php @@ -199,7 +199,7 @@ class DownloadManager throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified'); } - if (Config::INSTALL_PREFERENCE_DIST === $this->resolvePackageInstallPreference($package, $preferSource)) { + if (!$preferSource && ($this->preferDist || Config::INSTALL_PREFERENCE_DIST === $this->resolvePackageInstallPreference($package))) { $sources = array_reverse($sources); } @@ -288,14 +288,15 @@ class DownloadManager } } - protected function resolvePackageInstallPreference(PackageInterface $package, $preferSource = false) + /** + * Determines the install preference of a package + * + * @param PackageInterface $package package instance + * + * @return string + */ + protected function resolvePackageInstallPreference(PackageInterface $package) { - if ($this->preferSource || $preferSource) { - return Config::INSTALL_PREFERENCE_SOURCE; - } - if ($this->preferDist) { - return Config::INSTALL_PREFERENCE_DIST; - } foreach ($this->packagePreferences as $pattern => $preference) { $pattern = '{^'.str_replace('*', '.*', $pattern).'$}i'; if (preg_match($pattern, $package->getName())) { diff --git a/tests/Composer/Test/Downloader/DownloadManagerTest.php b/tests/Composer/Test/Downloader/DownloadManagerTest.php index 1728c583e..fa3e546d3 100644 --- a/tests/Composer/Test/Downloader/DownloadManagerTest.php +++ b/tests/Composer/Test/Downloader/DownloadManagerTest.php @@ -757,6 +757,366 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase $manager->remove($package, 'vendor/bundles/FOS/UserBundle'); } + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithoutPreferenceDev() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('isDev') + ->will($this->returnValue(true)); + + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + + $manager->download($package, 'target_dir'); + } + + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithoutPreferenceNoDev() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('isDev') + ->will($this->returnValue(false)); + + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('dist'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + + $manager->download($package, 'target_dir'); + } + + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithoutMatchDev() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('isDev') + ->will($this->returnValue(true)); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('bar/package')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + $manager->setPreferences(array('foo/*' => 'source')); + + $manager->download($package, 'target_dir'); + } + + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithoutMatchNoDev() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('isDev') + ->will($this->returnValue(false)); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('bar/package')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('dist'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + $manager->setPreferences(array('foo/*' => 'source')); + + $manager->download($package, 'target_dir'); + } + + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithMatchAutoDev() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('isDev') + ->will($this->returnValue(true)); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('foo/package')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + $manager->setPreferences(array('foo/*' => 'auto')); + + $manager->download($package, 'target_dir'); + } + + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithMatchAutoNoDev() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('isDev') + ->will($this->returnValue(false)); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('foo/package')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('dist'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + $manager->setPreferences(array('foo/*' => 'auto')); + + $manager->download($package, 'target_dir'); + } + + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithMatchSource() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('foo/package')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('source'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + $manager->setPreferences(array('foo/*' => 'source')); + + $manager->download($package, 'target_dir'); + } + + /** + * @covers Composer\Downloader\DownloadManager::resolvePackageInstallPreference + */ + public function testInstallPreferenceWithMatchDist() + { + $package = $this->createPackageMock(); + $package + ->expects($this->once()) + ->method('getSourceType') + ->will($this->returnValue('git')); + $package + ->expects($this->once()) + ->method('getDistType') + ->will($this->returnValue('pear')); + $package + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue('foo/package')); + $package + ->expects($this->once()) + ->method('setInstallationSource') + ->with('dist'); + + $downloader = $this->createDownloaderMock(); + $downloader + ->expects($this->once()) + ->method('download') + ->with($package, 'target_dir'); + + $manager = $this->getMockBuilder('Composer\Downloader\DownloadManager') + ->setConstructorArgs(array($this->io, false, $this->filesystem)) + ->setMethods(array('getDownloaderForInstalledPackage')) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getDownloaderForInstalledPackage') + ->with($package) + ->will($this->returnValue($downloader)); + $manager->setPreferences(array('foo/*' => 'dist')); + + $manager->download($package, 'target_dir'); + } + private function createDownloaderMock() { return $this->getMockBuilder('Composer\Downloader\DownloaderInterface') From 20745f839cb497611449e62c3e3f3f59aff1b070 Mon Sep 17 00:00:00 2001 From: Steve Buzonas Date: Sat, 18 Apr 2015 21:11:04 -0400 Subject: [PATCH 5/6] add documentation for preferred-install changes --- doc/04-schema.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index 587f9da29..be1303b2a 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -743,7 +743,8 @@ The following options are supported: will also look for classes in the PHP include path. * **preferred-install:** Defaults to `auto` and can be any of `source`, `dist` or `auto`. This option allows you to set the install method Composer will prefer to - use. + use. Can optionally be a hash of patterns for more granular install preferences. + See example below. * **store-auths:** What to do after prompting for authentication, one of: `true` (always store), `false` (do not store) and `"prompt"` (ask every time), defaults to `"prompt"`. @@ -822,6 +823,26 @@ Example: > besides your `composer.json`. That way you can gitignore it and every > developer can place their own credentials in there. +preferred-install Example: + +```json +{ + "config": { + "preferred-install": { + "my-organization/stable-package": "dist", + "my-organization/*": "source", + "partner-organization/*": "auto", + "*": "dist" + } + } +} +``` + +> **Note:** Order matters. More specific patterns should be earlier than +> more relaxed patterns. When mixing the string notation with the hash +> configuration in global and package configurations the string notation +> is translated to a `*` package pattern. + ### scripts (root-only) Composer allows you to hook into various parts of the installation process @@ -910,4 +931,4 @@ Then "composer show -s" will give you `versions : * dev-latest-testing`. Optional. -← [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) → \ No newline at end of file +← [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) → From 7f8677d410e61ea9e8971258bfdec57aab0d8906 Mon Sep 17 00:00:00 2001 From: Steve Buzonas Date: Sat, 18 Apr 2015 21:17:19 -0400 Subject: [PATCH 6/6] fixed tabs to spaces in doc --- doc/04-schema.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index be1303b2a..3166f8762 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -830,9 +830,9 @@ preferred-install Example: "config": { "preferred-install": { "my-organization/stable-package": "dist", - "my-organization/*": "source", - "partner-organization/*": "auto", - "*": "dist" + "my-organization/*": "source", + "partner-organization/*": "auto", + "*": "dist" } } }