From 7bb85ff21e5f7989512190ba1f802733244be535 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Mon, 1 Dec 2014 18:09:35 +1300 Subject: [PATCH] Support aliases for numeric branches. Fixes #3461 --- doc/articles/aliases.md | 12 ++++++---- src/Composer/Package/Loader/ArrayLoader.php | 10 ++++++++- .../Package/Loader/ValidatingArrayLoader.php | 11 ++++++++++ .../Package/Version/VersionParser.php | 15 +++++++++++++ .../Test/Package/Loader/ArrayLoaderTest.php | 22 +++++++++++++++++++ .../Loader/ValidatingArrayLoaderTest.php | 15 +++++++++++++ .../Package/Version/VersionParserTest.php | 20 +++++++++++++++++ .../Package/Version/VersionSelectorTest.php | 2 ++ 8 files changed, 102 insertions(+), 5 deletions(-) diff --git a/doc/articles/aliases.md b/doc/articles/aliases.md index 2b436322f..79c573d3d 100644 --- a/doc/articles/aliases.md +++ b/doc/articles/aliases.md @@ -38,10 +38,14 @@ specifying a `branch-alias` field under `extra` in `composer.json`: } ``` -The branch version must begin with `dev-` (non-comparable version), the alias -must be a comparable dev version (i.e. start with numbers, and end with -`.x-dev`). The `branch-alias` must be present on the branch that it references. -For `dev-master`, you need to commit it on the `master` branch. +If you alias a non-comparible version (such as dev-develop) `dev-` must prefix the +branch name. You may also alias a comparible version (i.e. start with numbers, +and end with `.x-dev`), but only as a more specific version. +For example, 1.x-dev could be aliased as 1.2.x-dev. + +The alias must be a comparable dev version, and the `branch-alias` must be present on +the branch that it references. For `dev-master`, you need to commit it on the +`master` branch. As a result, anyone can now require `1.0.*` and it will happily install `dev-master`. diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 243b7b574..558a24e35 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -224,7 +224,7 @@ class ArrayLoader implements LoaderInterface */ public function getBranchAlias(array $config) { - if ('dev-' !== substr($config['version'], 0, 4) + if (('dev-' !== substr($config['version'], 0, 4) && '-dev' !== substr($config['version'], -4)) || !isset($config['extra']['branch-alias']) || !is_array($config['extra']['branch-alias']) ) { @@ -248,6 +248,14 @@ class ArrayLoader implements LoaderInterface continue; } + // If using numeric aliases ensure the alias is a valid subversion + if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch)) + && ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch)) + && (stripos($targetPrefix, $sourcePrefix) !== 0) + ) { + continue; + } + return $validatedTargetBranch; } } diff --git a/src/Composer/Package/Loader/ValidatingArrayLoader.php b/src/Composer/Package/Loader/ValidatingArrayLoader.php index 3493d3d5b..9a6f4dd32 100644 --- a/src/Composer/Package/Loader/ValidatingArrayLoader.php +++ b/src/Composer/Package/Loader/ValidatingArrayLoader.php @@ -251,6 +251,17 @@ class ValidatingArrayLoader implements LoaderInterface if ('-dev' !== substr($validatedTargetBranch, -4)) { $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') must be a parseable number like 2.0-dev'; unset($this->config['extra']['branch-alias'][$sourceBranch]); + + continue; + } + + // If using numeric aliases ensure the alias is a valid subversion + if(($sourcePrefix = $this->versionParser->parseNumericAliasPrefix($sourceBranch)) + && ($targetPrefix = $this->versionParser->parseNumericAliasPrefix($targetBranch)) + && (stripos($targetPrefix, $sourcePrefix) !== 0) + ) { + $this->warnings[] = 'extra.branch-alias.'.$sourceBranch.' : the target branch ('.$targetBranch.') is not a valid numeric alias for this version'; + unset($this->config['extra']['branch-alias'][$sourceBranch]); } } } diff --git a/src/Composer/Package/Version/VersionParser.php b/src/Composer/Package/Version/VersionParser.php index 2a2c38371..3925e7e65 100644 --- a/src/Composer/Package/Version/VersionParser.php +++ b/src/Composer/Package/Version/VersionParser.php @@ -169,6 +169,21 @@ class VersionParser throw new \UnexpectedValueException('Invalid version string "'.$version.'"'.$extraMessage); } + /** + * Extract numeric prefix from alias, if it is in numeric format, suitable for + * version comparison + * + * @param string $branch Branch name (e.g. 2.1.x-dev) + * @return string|false Numeric prefix if present (e.g. 2.1.) or false + */ + public function parseNumericAliasPrefix($branch) { + if(preg_match('/^(?(\d+\\.)*\d+).x-dev$/i', $branch, $matches)) { + return $matches['version']."."; + } else { + return false; + } + } + /** * Normalizes a branch name to be able to perform comparisons on it * diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index 6e4e2f5ee..4b98ae26d 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -138,6 +138,28 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Composer\Package\AliasPackage', $package); $this->assertEquals('1.0.x-dev', $package->getPrettyVersion()); + + $config = array( + 'name' => 'B', + 'version' => '4.x-dev', + 'extra' => array('branch-alias' => array('4.x-dev' => '4.0.x-dev')), + ); + + $package = $this->loader->load($config); + + $this->assertInstanceOf('Composer\Package\AliasPackage', $package); + $this->assertEquals('4.0.x-dev', $package->getPrettyVersion()); + + $config = array( + 'name' => 'C', + 'version' => '4.x-dev', + 'extra' => array('branch-alias' => array('4.x-dev' => '3.4.x-dev')), + ); + + $package = $this->loader->load($config); + + $this->assertInstanceOf('Composer\Package\CompletePackage', $package); + $this->assertEquals('4.x-dev', $package->getPrettyVersion()); } public function testAbandoned() diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index 23c47c3c7..2b0829759 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -140,6 +140,7 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'branch-alias' => array( 'dev-master' => '2.0-dev', 'dev-old' => '1.0.x-dev', + '3.x-dev' => '3.1.x-dev' ), ), 'bin' => array( @@ -324,6 +325,20 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase ), false ), + array( + array( + 'name' => 'foo/bar', + 'extra' => array( + 'branch-alias' => array( + '5.x-dev' => '3.1.x-dev' + ), + ) + ), + array( + 'extra.branch-alias.5.x-dev : the target branch (3.1.x-dev) is not a valid numeric alias for this version' + ), + false + ), ); } } diff --git a/tests/Composer/Test/Package/Version/VersionParserTest.php b/tests/Composer/Test/Package/Version/VersionParserTest.php index 88058f6aa..c52ec1382 100644 --- a/tests/Composer/Test/Package/Version/VersionParserTest.php +++ b/tests/Composer/Test/Package/Version/VersionParserTest.php @@ -76,6 +76,26 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase $this->assertSame($expected, $parser->normalize($input)); } + public function numericAliasVersions() { + return array( + array('0.x-dev', '0.'), + array('1.0.x-dev', '1.0.'), + array('1.x-dev', '1.'), + array('1.2.x-dev', '1.2.'), + array('dev-develop', false), + array('dev-master', false), + ); + } + + /** + * @dataProvider numericAliasVersions + */ + public function testParseNumericAliasPrefix($input, $expected) + { + $parser = new VersionParser; + $this->assertSame($expected, $parser->parseNumericAliasPrefix($input)); + } + public function successfulNormalizedVersions() { return array( diff --git a/tests/Composer/Test/Package/Version/VersionSelectorTest.php b/tests/Composer/Test/Package/Version/VersionSelectorTest.php index 90f820e8f..fb3a6f666 100644 --- a/tests/Composer/Test/Package/Version/VersionSelectorTest.php +++ b/tests/Composer/Test/Package/Version/VersionSelectorTest.php @@ -115,6 +115,8 @@ class VersionSelectorTest extends \PHPUnit_Framework_TestCase array('dev-master', true, 'dev', '~2.1@dev', '2.1.x-dev'), array('dev-master', true, 'dev', '~2.1@dev', '2.1.3.x-dev'), array('dev-master', true, 'dev', '~2.0@dev', '2.x-dev'), + // numeric alias + array('3.x-dev', true, 'dev', '~3.0@dev', '3.0.x-dev'), ); }