Explorar el Código

Merge pull request #702 from Seldaek/specify-refs

Specify commit/refs for dev requirements
Nils Adermann hace 13 años
padre
commit
81de780533

+ 1 - 0
CHANGELOG.md

@@ -1,5 +1,6 @@
 * 1.0.0-alpha4
 
+  * Schema: Added references for dev versions, requiring 'dev-master#abcdef' for example will force the abcdef commit
   * Added caching of GitHub metadata (faster startup time with custom GitHub VCS repos)
 
 * 1.0.0-alpha3 (2012-05-13)

+ 17 - 0
doc/04-schema.md

@@ -230,6 +230,23 @@ Example:
         }
     }
 
+`require` and `require-dev` additionally support explicit references (i.e.
+commit) for dev versions to make sure they are blocked to a given state, even
+when you run update. These only work if you explicitly require a dev version
+and append the reference with `#<ref>`. Note that while this is convenient at
+times, it should not really be how you use packages in the long term. You
+should always try to switch to tagged releases as soon as you can, especially
+if the project you work on will not be touched for a while.
+
+Example:
+
+    {
+        "require": {
+            "monolog/monolog": "dev-master#2eb0c0978d290a1c45346a1955188929cb4e5db7"
+            "acme/foo": "1.0.x-dev#abc123"
+        }
+    }
+
 #### require
 
 Lists packages required by this package. The package will not be installed

+ 49 - 26
src/Composer/Installer.php

@@ -294,42 +294,50 @@ class Installer
         }
 
         // force dev packages to be updated if we update or install from a (potentially new) lock
-        if ($this->update || $installFromLock) {
-            foreach ($localRepo->getPackages() as $package) {
-                // skip non-dev packages
-                if (!$package->isDev()) {
-                    continue;
+        foreach ($localRepo->getPackages() as $package) {
+            // skip non-dev packages
+            if (!$package->isDev()) {
+                continue;
+            }
+
+            // skip packages that will be updated/uninstalled
+            foreach ($operations as $operation) {
+                if (('update' === $operation->getJobType() && $operation->getInitialPackage()->equals($package))
+                    || ('uninstall' === $operation->getJobType() && $operation->getPackage()->equals($package))
+                ) {
+                    continue 2;
                 }
+            }
 
-                // skip packages that will be updated/uninstalled
-                foreach ($operations as $operation) {
-                    if (('update' === $operation->getJobType() && $operation->getInitialPackage()->equals($package))
-                        || ('uninstall' === $operation->getJobType() && $operation->getPackage()->equals($package))
-                    ) {
-                        continue 2;
+            // force update to locked version if it does not match the installed version
+            if ($installFromLock) {
+                $lockData = $this->locker->getLockData();
+                unset($lockedReference);
+                foreach ($lockData['packages'] as $lockedPackage) {
+                    if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) {
+                        $lockedReference = $lockedPackage['source-reference'];
+                        break;
                     }
                 }
-
+                if (isset($lockedReference) && $lockedReference !== $package->getSourceReference()) {
+                    // changing the source ref to update to will be handled in the operations loop below
+                    $operations[] = new UpdateOperation($package, clone $package);
+                }
+            } else {
                 // force update to latest on update
                 if ($this->update) {
                     $newPackage = $this->repositoryManager->findPackage($package->getName(), $package->getVersion());
                     if ($newPackage && $newPackage->getSourceReference() !== $package->getSourceReference()) {
                         $operations[] = new UpdateOperation($package, $newPackage);
                     }
-                } elseif ($installFromLock) {
-                    // force update to locked version if it does not match the installed version
-                    $lockData = $this->locker->getLockData();
-                    unset($lockedReference);
-                    foreach ($lockData['packages'] as $lockedPackage) {
-                        if (!empty($lockedPackage['source-reference']) && strtolower($lockedPackage['package']) === $package->getName()) {
-                            $lockedReference = $lockedPackage['source-reference'];
-                            break;
-                        }
-                    }
-                    if (isset($lockedReference) && $lockedReference !== $package->getSourceReference()) {
-                        // changing the source ref to update to will be handled in the operations loop below
-                        $operations[] = new UpdateOperation($package, $package);
-                    }
+                }
+
+                // force installed package to update to referenced version if it does not match the installed version
+                $references = $this->package->getReferences();
+
+                if (isset($references[$package->getName()]) && $references[$package->getName()] !== $package->getSourceReference()) {
+                    // changing the source ref to update to will be handled in the operations loop below
+                    $operations[] = new UpdateOperation($package, clone $package);
                 }
             }
         }
@@ -378,7 +386,22 @@ class Installer
                             }
                         }
                     }
+                } else {
+                    // not installing from lock, force dev packages' references if they're in root package refs
+                    $package = null;
+                    if ('update' === $operation->getJobType()) {
+                        $package = $operation->getTargetPackage();
+                    } elseif ('install' === $operation->getJobType()) {
+                        $package = $operation->getPackage();
+                    }
+                    if ($package && $package->isDev()) {
+                        $references = $this->package->getReferences();
+                        if (isset($references[$package->getName()])) {
+                            $package->setSourceReference($references[$package->getName()]);
+                        }
+                    }
                 }
+
                 $this->installationManager->execute($localRepo, $operation);
 
                 $event = 'Composer\Script\ScriptEvents::POST_PACKAGE_'.strtoupper($operation->getJobType());

+ 20 - 7
src/Composer/Package/Loader/RootPackageLoader.php

@@ -63,17 +63,18 @@ class RootPackageLoader extends ArrayLoader
 
         $aliases = array();
         $stabilityFlags = array();
-        if (isset($config['require'])) {
-            $aliases = $this->extractAliases($config['require'], $aliases);
-            $stabilityFlags = $this->extractStabilityFlags($config['require'], $stabilityFlags);
-        }
-        if (isset($config['require-dev'])) {
-            $aliases = $this->extractAliases($config['require-dev'], $aliases);
-            $stabilityFlags = $this->extractStabilityFlags($config['require-dev'], $stabilityFlags);
+        $references = array();
+        foreach (array('require', 'require-dev') as $linkType) {
+            if (isset($config[$linkType])) {
+                $aliases = $this->extractAliases($config[$linkType], $aliases);
+                $stabilityFlags = $this->extractStabilityFlags($config[$linkType], $stabilityFlags);
+                $references = $this->extractReferences($config[$linkType], $references);
+            }
         }
 
         $package->setAliases($aliases);
         $package->setStabilityFlags($stabilityFlags);
+        $package->setReferences($references);
 
         if (isset($config['minimum-stability'])) {
             $package->setMinimumStability(VersionParser::normalizeStability($config['minimum-stability']));
@@ -145,4 +146,16 @@ class RootPackageLoader extends ArrayLoader
 
         return $stabilityFlags;
     }
+
+    private function extractReferences(array $requires, array $references)
+    {
+        foreach ($requires as $reqName => $reqVersion) {
+            if (preg_match('{^[^,\s@]+?#([a-f0-9]+)$}', $reqVersion, $match) && 'dev' === ($stabilityName = VersionParser::parseStability($reqVersion))) {
+                $name = strtolower($reqName);
+                $references[$name] = $match[1];
+            }
+        }
+
+        return $references;
+    }
 }

+ 19 - 0
src/Composer/Package/MemoryPackage.php

@@ -51,6 +51,7 @@ class MemoryPackage extends BasePackage
     // TODO BC change dev to stable end of june?
     protected $minimumStability = 'dev';
     protected $stabilityFlags = array();
+    protected $references = array();
 
     protected $requires = array();
     protected $conflicts = array();
@@ -637,6 +638,24 @@ class MemoryPackage extends BasePackage
         return $this->stabilityFlags;
     }
 
+    /**
+     * Set the references
+     *
+     * @param array $references
+     */
+    public function setReferences(array $references)
+    {
+        $this->references = $references;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getReferences()
+    {
+        return $this->references;
+    }
+
     /**
      * Set the autoload mapping
      *

+ 6 - 0
src/Composer/Package/Version/VersionParser.php

@@ -33,6 +33,8 @@ class VersionParser
      */
     static public function parseStability($version)
     {
+        $version = preg_replace('{#[a-f0-9]+$}i', '', $version);
+
         if ('dev-' === substr($version, 0, 4) || '-dev' === substr($version, -4)) {
             return 'dev';
         }
@@ -155,6 +157,10 @@ class VersionParser
             $constraints = empty($match[1]) ? '*' : $match[1];
         }
 
+        if (preg_match('{^(dev-[^,\s@]+?|[^,\s@]+?\.x-dev)#[a-f0-9]+$}i', $constraints, $match)) {
+            $constraints = $match[1];
+        }
+
         $constraints = preg_split('{\s*,\s*}', trim($constraints));
 
         if (count($constraints) > 1) {

+ 21 - 0
tests/Composer/Test/Fixtures/installer/install-reference.test

@@ -0,0 +1,21 @@
+--TEST--
+Installs a dev package forcing it's reference
+--COMPOSER--
+{
+    "repositories": [
+        {
+            "type": "package",
+            "package": [
+                {
+                    "name": "a/a", "version": "dev-master",
+                    "source": { "reference": "abc123", "url": "", "type": "git" }
+                }
+            ]
+        }
+    ],
+    "require": {
+        "a/a": "dev-master#def000"
+    }
+}
+--EXPECT--
+Installing a/a (dev-master def000)

+ 28 - 0
tests/Composer/Test/Fixtures/installer/update-reference.test

@@ -0,0 +1,28 @@
+--TEST--
+Updates a dev package forcing it's reference
+--COMPOSER--
+{
+    "repositories": [
+        {
+            "type": "package",
+            "package": [
+                {
+                    "name": "a/a", "version": "dev-master",
+                    "source": { "reference": "abc123", "url": "", "type": "git" }
+                }
+            ]
+        }
+    ],
+    "require": {
+        "a/a": "dev-master#def000"
+    }
+}
+--INSTALLED--
+[
+    {
+        "name": "a/a", "version": "dev-master",
+        "source": { "reference": "abc123", "url": "", "type": "git" }
+    }
+]
+--EXPECT--
+Updating a/a (dev-master abc123) to a/a (dev-master def000)

+ 16 - 0
tests/Composer/Test/Package/Version/VersionParserTest.php

@@ -112,6 +112,21 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
         $this->assertSame((string) new VersionConstraint('=', '1.0.0.0'), (string) $parser->parseConstraints('1.0@dev'));
     }
 
+    public function testParseConstraintsIgnoresReferenceOnDevVersion()
+    {
+        $parser = new VersionParser;
+        $this->assertSame((string) new VersionConstraint('=', '1.0.9999999.9999999-dev'), (string) $parser->parseConstraints('1.0.x-dev#abcd123'));
+    }
+
+    /**
+     * @expectedException UnexpectedValueException
+     */
+    public function testParseConstraintsFailsOnBadReference()
+    {
+        $parser = new VersionParser;
+        $this->assertSame((string) new VersionConstraint('=', '1.0.0.0'), (string) $parser->parseConstraints('1.0#abcd123'));
+    }
+
     /**
      * @dataProvider simpleConstraints
      */
@@ -213,6 +228,7 @@ class VersionParserTest extends \PHPUnit_Framework_TestCase
         return array(
             array('stable', '1.0'),
             array('dev',    'v2.0.x-dev'),
+            array('dev',    'v2.0.x-dev#abc123'),
             array('RC',     '3.0-RC2'),
             array('dev',    'dev-master'),
             array('dev',    '3.1.2-dev'),