Finalize platform override feature
- Added tests, docs - Persist to lock file - Add support in config command - Added to json schemapull/3982/head
parent
80b0a35a68
commit
a57c51e8d7
|
@ -82,7 +82,7 @@ resolution.
|
||||||
have a proper setup.
|
have a proper setup.
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the `platform` [config option](04-schema.md#config).
|
||||||
* **--dry-run:** If you want to run through an installation without actually
|
* **--dry-run:** If you want to run through an installation without actually
|
||||||
installing a package, you can use `--dry-run`. This will simulate the
|
installing a package, you can use `--dry-run`. This will simulate the
|
||||||
installation and show you what would happen.
|
installation and show you what would happen.
|
||||||
|
@ -127,7 +127,7 @@ php composer.phar update vendor/*
|
||||||
* **--prefer-dist:** Install packages from `dist` when available.
|
* **--prefer-dist:** Install packages from `dist` when available.
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the `platform` [config option](04-schema.md#config).
|
||||||
* **--dry-run:** Simulate the command without actually doing anything.
|
* **--dry-run:** Simulate the command without actually doing anything.
|
||||||
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
|
* **--dev:** Install packages listed in `require-dev` (this is the default behavior).
|
||||||
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
|
* **--no-dev:** Skip installing packages listed in `require-dev`. The autoloader generation skips the `autoload-dev` rules.
|
||||||
|
@ -171,7 +171,7 @@ php composer.phar require vendor/package:2.* vendor/package2:dev-master
|
||||||
* **--prefer-dist:** Install packages from `dist` when available.
|
* **--prefer-dist:** Install packages from `dist` when available.
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the `platform` [config option](04-schema.md#config).
|
||||||
* **--dev:** Add packages to `require-dev`.
|
* **--dev:** Add packages to `require-dev`.
|
||||||
* **--no-update:** Disables the automatic update of the dependencies.
|
* **--no-update:** Disables the automatic update of the dependencies.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
|
@ -195,7 +195,7 @@ uninstalled.
|
||||||
### Options
|
### Options
|
||||||
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
* **--ignore-platform-reqs:** ignore `php`, `hhvm`, `lib-*` and `ext-*`
|
||||||
requirements and force the installation even if the local machine does not
|
requirements and force the installation even if the local machine does not
|
||||||
fulfill these.
|
fulfill these. See also the `platform` [config option](04-schema.md#config).
|
||||||
* **--dev:** Remove packages from `require-dev`.
|
* **--dev:** Remove packages from `require-dev`.
|
||||||
* **--no-update:** Disables the automatic update of the dependencies.
|
* **--no-update:** Disables the automatic update of the dependencies.
|
||||||
* **--no-progress:** Removes the progress display that can mess with some
|
* **--no-progress:** Removes the progress display that can mess with some
|
||||||
|
|
|
@ -761,6 +761,9 @@ The following options are supported:
|
||||||
against them. For example using
|
against them. For example using
|
||||||
`{"example.org": {"username": "alice", "password": "foo"}` as the value of this
|
`{"example.org": {"username": "alice", "password": "foo"}` as the value of this
|
||||||
option will let composer authenticate against example.org.
|
option will let composer authenticate against example.org.
|
||||||
|
* **platform:** Lets you fake platform packages (PHP and extensions) so that
|
||||||
|
you can emulate a production env or define your target platform in the
|
||||||
|
config. e.g. `{"php": "5.4", "ext-something": "4.0"}`.
|
||||||
* **vendor-dir:** Defaults to `vendor`. You can install dependencies into a
|
* **vendor-dir:** Defaults to `vendor`. You can install dependencies into a
|
||||||
different directory if you want to. `$HOME` and `~` will be replaced by your
|
different directory if you want to. `$HOME` and `~` will be replaced by your
|
||||||
home directory's path in vendor-dir and all `*-dir` options below.
|
home directory's path in vendor-dir and all `*-dir` options below.
|
||||||
|
|
|
@ -145,6 +145,11 @@
|
||||||
"type": ["string", "boolean"],
|
"type": ["string", "boolean"],
|
||||||
"description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt."
|
"description": "What to do after prompting for authentication, one of: true (store), false (do not store) or \"prompt\" (ask every time), defaults to prompt."
|
||||||
},
|
},
|
||||||
|
"platform": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "This is a hash of package name (keys) and version (values) that will be used to mock the platform packages on this machine.",
|
||||||
|
"additionalProperties": true
|
||||||
|
},
|
||||||
"vendor-dir": {
|
"vendor-dir": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The location where all packages are installed, defaults to \"vendor\"."
|
"description": "The location where all packages are installed, defaults to \"vendor\"."
|
||||||
|
|
|
@ -410,6 +410,15 @@ EOT
|
||||||
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
|
throw new \RuntimeException('You must pass the type and a url. Example: php composer.phar config repositories.foo vcs http://bar.com');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handle platform
|
||||||
|
if (preg_match('/^platform\.(.+)/', $settingKey, $matches)) {
|
||||||
|
if ($input->getOption('unset')) {
|
||||||
|
return $this->configSource->removeConfigSetting($settingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->configSource->addConfigSetting($settingKey, $values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
// handle github-oauth
|
// handle github-oauth
|
||||||
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
|
if (preg_match('/^(github-oauth|http-basic)\.(.+)/', $settingKey, $matches)) {
|
||||||
if ($input->getOption('unset')) {
|
if ($input->getOption('unset')) {
|
||||||
|
|
|
@ -79,7 +79,7 @@ class JsonConfigSource implements ConfigSourceInterface
|
||||||
public function addConfigSetting($name, $value)
|
public function addConfigSetting($name, $value)
|
||||||
{
|
{
|
||||||
$this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) {
|
$this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) {
|
||||||
if ($key === 'github-oauth' || $key === 'http-basic') {
|
if (preg_match('{^(github-oauth|http-basic|platform)\.}', $key)) {
|
||||||
list($key, $host) = explode('.', $key, 2);
|
list($key, $host) = explode('.', $key, 2);
|
||||||
if ($this->authConfig) {
|
if ($this->authConfig) {
|
||||||
$config[$key][$host] = $val;
|
$config[$key][$host] = $val;
|
||||||
|
@ -98,7 +98,7 @@ class JsonConfigSource implements ConfigSourceInterface
|
||||||
public function removeConfigSetting($name)
|
public function removeConfigSetting($name)
|
||||||
{
|
{
|
||||||
$this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) {
|
$this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) {
|
||||||
if ($key === 'github-oauth' || $key === 'http-basic') {
|
if (preg_match('{^(github-oauth|http-basic|platform)\.}', $key)) {
|
||||||
list($key, $host) = explode('.', $key, 2);
|
list($key, $host) = explode('.', $key, 2);
|
||||||
if ($this->authConfig) {
|
if ($this->authConfig) {
|
||||||
unset($config[$key][$host]);
|
unset($config[$key][$host]);
|
||||||
|
|
|
@ -206,9 +206,12 @@ class Installer
|
||||||
|
|
||||||
// create installed repo, this contains all local packages + platform packages (php & extensions)
|
// create installed repo, this contains all local packages + platform packages (php & extensions)
|
||||||
$localRepo = $this->repositoryManager->getLocalRepository();
|
$localRepo = $this->repositoryManager->getLocalRepository();
|
||||||
$platformOverride = $this->config->get('platform');
|
if (!$this->update && $this->locker->isLocked()) {
|
||||||
$platformOverride = is_array($platformOverride) ? $platformOverride : array();
|
$platformOverrides = $this->locker->getPlatformOverrides();
|
||||||
$platformRepo = new PlatformRepository($platformOverride);
|
} else {
|
||||||
|
$platformOverrides = $this->config->get('platform') ?: array();
|
||||||
|
}
|
||||||
|
$platformRepo = new PlatformRepository(array(), $platformOverrides);
|
||||||
$repos = array(
|
$repos = array(
|
||||||
$localRepo,
|
$localRepo,
|
||||||
new InstalledArrayRepository(array($installedRootPackage)),
|
new InstalledArrayRepository(array($installedRootPackage)),
|
||||||
|
@ -313,7 +316,8 @@ class Installer
|
||||||
$this->package->getMinimumStability(),
|
$this->package->getMinimumStability(),
|
||||||
$this->package->getStabilityFlags(),
|
$this->package->getStabilityFlags(),
|
||||||
$this->preferStable || $this->package->getPreferStable(),
|
$this->preferStable || $this->package->getPreferStable(),
|
||||||
$this->preferLowest
|
$this->preferLowest,
|
||||||
|
$this->config->get('platform') ?: array()
|
||||||
);
|
);
|
||||||
if ($updatedLock) {
|
if ($updatedLock) {
|
||||||
$this->io->writeError('<info>Writing lock file</info>');
|
$this->io->writeError('<info>Writing lock file</info>');
|
||||||
|
|
|
@ -191,6 +191,13 @@ class Locker
|
||||||
return isset($lockData['prefer-lowest']) ? $lockData['prefer-lowest'] : null;
|
return isset($lockData['prefer-lowest']) ? $lockData['prefer-lowest'] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPlatformOverrides()
|
||||||
|
{
|
||||||
|
$lockData = $this->getLockData();
|
||||||
|
|
||||||
|
return isset($lockData['platform-overrides']) ? $lockData['platform-overrides'] : array();
|
||||||
|
}
|
||||||
|
|
||||||
public function getAliases()
|
public function getAliases()
|
||||||
{
|
{
|
||||||
$lockData = $this->getLockData();
|
$lockData = $this->getLockData();
|
||||||
|
@ -223,10 +230,11 @@ class Locker
|
||||||
* @param array $stabilityFlags
|
* @param array $stabilityFlags
|
||||||
* @param bool $preferStable
|
* @param bool $preferStable
|
||||||
* @param bool $preferLowest
|
* @param bool $preferLowest
|
||||||
|
* @param array $platformOverrides
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest)
|
public function setLockData(array $packages, $devPackages, array $platformReqs, $platformDevReqs, array $aliases, $minimumStability, array $stabilityFlags, $preferStable, $preferLowest, array $platformOverrides)
|
||||||
{
|
{
|
||||||
$lock = array(
|
$lock = array(
|
||||||
'_readme' => array('This file locks the dependencies of your project to a known state',
|
'_readme' => array('This file locks the dependencies of your project to a known state',
|
||||||
|
@ -260,6 +268,9 @@ class Locker
|
||||||
|
|
||||||
$lock['platform'] = $platformReqs;
|
$lock['platform'] = $platformReqs;
|
||||||
$lock['platform-dev'] = $platformDevReqs;
|
$lock['platform-dev'] = $platformDevReqs;
|
||||||
|
if ($platformOverrides) {
|
||||||
|
$lock['platform-overrides'] = $platformOverrides;
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($lock['packages']) && empty($lock['packages-dev']) && empty($lock['platform']) && empty($lock['platform-dev'])) {
|
if (empty($lock['packages']) && empty($lock['packages-dev']) && empty($lock['platform']) && empty($lock['platform-dev'])) {
|
||||||
if ($this->lockFile->exists()) {
|
if ($this->lockFile->exists()) {
|
||||||
|
|
|
@ -24,12 +24,22 @@ class PlatformRepository extends ArrayRepository
|
||||||
{
|
{
|
||||||
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit)?|hhvm|(?:ext|lib)-[^/]+)$}i';
|
const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit)?|hhvm|(?:ext|lib)-[^/]+)$}i';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines overrides so that the platform can be mocked
|
||||||
|
*
|
||||||
|
* Should be an array of package name => version number mappings
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
private $overrides;
|
private $overrides;
|
||||||
|
|
||||||
public function __construct(array $overrides = array())
|
public function __construct(array $packages = array(), array $overrides = array())
|
||||||
{
|
{
|
||||||
parent::__construct(array());
|
parent::__construct($packages);
|
||||||
$this->overrides = $overrides;
|
$this->overrides = array();
|
||||||
|
foreach ($overrides as $name => $version) {
|
||||||
|
$this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function initialize()
|
protected function initialize()
|
||||||
|
@ -40,19 +50,17 @@ class PlatformRepository extends ArrayRepository
|
||||||
|
|
||||||
// Add each of the override versions as options.
|
// Add each of the override versions as options.
|
||||||
// Later we might even replace the extensions instead.
|
// Later we might even replace the extensions instead.
|
||||||
foreach( $this->overrides as $name => $prettyVersion ) {
|
foreach ($this->overrides as $override) {
|
||||||
// Check that it's a platform package.
|
// Check that it's a platform package.
|
||||||
if( preg_match(self::PLATFORM_PACKAGE_REGEX, $name) ) {
|
if (!preg_match(self::PLATFORM_PACKAGE_REGEX, $override['name'])) {
|
||||||
$version = $versionParser->normalize($prettyVersion);
|
throw new \InvalidArgumentException('Invalid platform package name in config.platform: '.$override['name']);
|
||||||
$package = new CompletePackage($name, $version, $prettyVersion);
|
|
||||||
$package->setDescription("Overridden virtual platform package $name.");
|
|
||||||
parent::addPackage($package);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new \InvalidArgumentException('Invalid platform package '.$name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$version = $versionParser->normalize($override['version']);
|
||||||
|
$package = new CompletePackage($override['name'], $version, $override['version']);
|
||||||
|
$package->setDescription('Overridden virtual platform package '.$override['name']);
|
||||||
|
parent::addPackage($package);
|
||||||
|
}
|
||||||
|
|
||||||
$prettyVersion = PluginInterface::PLUGIN_API_VERSION;
|
$prettyVersion = PluginInterface::PLUGIN_API_VERSION;
|
||||||
$version = $versionParser->normalize($prettyVersion);
|
$version = $versionParser->normalize($prettyVersion);
|
||||||
|
@ -186,21 +194,13 @@ class PlatformRepository extends ArrayRepository
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Is it a good thing to redefine the public interface
|
/**
|
||||||
// like this, or is it better to make the "only-add-if-no-in-platform"
|
* {@inheritDoc}
|
||||||
// feature in a
|
*/
|
||||||
// protected function addOverriddenPackage()
|
|
||||||
// instead?
|
|
||||||
public function addPackage(PackageInterface $package)
|
public function addPackage(PackageInterface $package)
|
||||||
{
|
{
|
||||||
/*
|
// Skip if overridden
|
||||||
If we can find the package in this repository,
|
if (isset($this->overrides[strtolower($package->getName())])) {
|
||||||
in any version, it can only mean that it has been
|
|
||||||
added by the config key 'platform' and should
|
|
||||||
the real package (i.e. this one) should not be added.
|
|
||||||
*/
|
|
||||||
if( count($this->findPackages($package->getName())) > 0 ) {
|
|
||||||
// Log a warning that we're ignoring existing package?
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
parent::addPackage($package);
|
parent::addPackage($package);
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
--TEST--
|
||||||
|
Install overridden platform requirements works
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{ "name": "a/a", "version": "1.0.0", "require": { "ext-dummy2": "1.*" } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"a/a": "*",
|
||||||
|
"ext-dummy": "~1.0",
|
||||||
|
"php": "1.0"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"platform": {
|
||||||
|
"php": "1.0.0",
|
||||||
|
"ext-dummy": "1.2.3",
|
||||||
|
"ext-dummy2": "1.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--RUN--
|
||||||
|
install
|
||||||
|
--EXPECT--
|
||||||
|
Installing a/a (1.0.0)
|
|
@ -134,11 +134,12 @@ class LockerTest extends \PHPUnit_Framework_TestCase
|
||||||
'stability-flags' => array(),
|
'stability-flags' => array(),
|
||||||
'platform' => array(),
|
'platform' => array(),
|
||||||
'platform-dev' => array(),
|
'platform-dev' => array(),
|
||||||
|
'platform-overrides' => array('foo/bar' => '1.0'),
|
||||||
'prefer-stable' => false,
|
'prefer-stable' => false,
|
||||||
'prefer-lowest' => false,
|
'prefer-lowest' => false,
|
||||||
));
|
));
|
||||||
|
|
||||||
$locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false, false);
|
$locker->setLockData(array($package1, $package2), array(), array(), array(), array(), 'dev', array(), false, false, array('foo/bar' => '1.0'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLockBadPackages()
|
public function testLockBadPackages()
|
||||||
|
@ -157,7 +158,7 @@ class LockerTest extends \PHPUnit_Framework_TestCase
|
||||||
|
|
||||||
$this->setExpectedException('LogicException');
|
$this->setExpectedException('LogicException');
|
||||||
|
|
||||||
$locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false, false);
|
$locker->setLockData(array($package1), array(), array(), array(), array(), 'dev', array(), false, false, array());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIsFresh()
|
public function testIsFresh()
|
||||||
|
|
Loading…
Reference in New Issue