diff --git a/src/Composer/Command/UpdateCommand.php b/src/Composer/Command/UpdateCommand.php
index 99bd2d74b..0c3d3e6c7 100644
--- a/src/Composer/Command/UpdateCommand.php
+++ b/src/Composer/Command/UpdateCommand.php
@@ -121,6 +121,19 @@ EOT
}
}
+ // the arguments lock/nothing/mirrors are not package names but trigger a mirror update instead
+ // they are further mutually exclusive with listing actual package names
+ $filteredPackages = array_filter($packages, function ($package) {
+ return !in_array($package, array('lock', 'nothing', 'mirrors'), true);
+ });
+ $updateMirrors = $input->getOption('lock') || count($filteredPackages) != count($packages);
+ $packages = $filteredPackages;
+
+ if ($updateMirrors && !empty($packages)) {
+ $io->writeError('You cannot simultaneously update only a selection of packages and regenerate the lock file metadata.');
+ return -1;
+ }
+
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
@@ -146,7 +159,8 @@ EOT
->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu)
->setUpdate(true)
- ->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $packages)
+ ->setUpdateMirrors($updateMirrors)
+ ->setUpdateWhitelist($packages)
->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))
diff --git a/src/Composer/DependencyResolver/LockTransaction.php b/src/Composer/DependencyResolver/LockTransaction.php
index 75b2efb5b..7255de3dd 100644
--- a/src/Composer/DependencyResolver/LockTransaction.php
+++ b/src/Composer/DependencyResolver/LockTransaction.php
@@ -152,11 +152,19 @@ class LockTransaction
}
// TODO additionalFixedRepository needs to be looked at here as well?
- public function getNewLockPackages($devMode)
+ public function getNewLockPackages($devMode, $updateMirrors = false)
{
$packages = array();
foreach ($this->resultPackages[$devMode ? 'dev' : 'non-dev'] as $package) {
if (!($package instanceof AliasPackage) && !($package instanceof RootAliasPackage)) {
+ // if we're just updating mirrors we need to reset references to the same as currently "present" packages' references to keep the lock file as-is
+ if ($updateMirrors && !isset($this->presentMap[spl_object_hash($package)])) {
+ foreach ($this->presentMap as $presentPackage) {
+ if ($package->getName() == $presentPackage->getName() && $package->getVersion() == $presentPackage->getVersion() && $presentPackage->getSourceReference()) {
+ $package->setSourceDistReferences($presentPackage->getSourceReference());
+ }
+ }
+ }
$packages[] = $package;
}
}
diff --git a/src/Composer/DependencyResolver/PoolBuilder.php b/src/Composer/DependencyResolver/PoolBuilder.php
index 39ac5098f..1c995bc4e 100644
--- a/src/Composer/DependencyResolver/PoolBuilder.php
+++ b/src/Composer/DependencyResolver/PoolBuilder.php
@@ -184,7 +184,7 @@ class PoolBuilder
if (isset($this->rootReferences[$name])) {
// do not modify the references on already locked packages
if (!$request->isFixedPackage($package)) {
- $this->setReferences($package, $this->rootReferences[$name]);
+ $package->setSourceDistReferences($this->rootReferences[$name]);
}
}
@@ -225,19 +225,5 @@ class PoolBuilder
return $loadNames;
}
-
- private function setReferences(Package $package, $reference)
- {
- $package->setSourceReference($reference);
-
- // only bitbucket, github and gitlab have auto generated dist URLs that easily allow replacing the reference in the dist URL
- // TODO generalize this a bit for self-managed/on-prem versions? Some kind of replace token in dist urls which allow this?
- if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $package->getDistUrl())) {
- $package->setDistReference($reference);
- $package->setDistUrl(preg_replace('{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i', $reference, $package->getDistUrl()));
- } elseif ($package->getDistReference()) { // update the dist reference if there was one, but if none was provided ignore it
- $package->setDistReference($reference);
- }
- }
}
diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php
index dea1e03e5..86d251756 100644
--- a/src/Composer/Installer.php
+++ b/src/Composer/Installer.php
@@ -38,6 +38,7 @@ use Composer\Package\AliasPackage;
use Composer\Package\BasePackage;
use Composer\Package\CompletePackage;
use Composer\Package\Link;
+use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Dumper\ArrayDumper;
use Composer\Package\Package;
@@ -137,6 +138,7 @@ class Installer
*
* @var array|null
*/
+ protected $updateMirrors = false;
protected $updateWhitelist = null;
protected $whitelistTransitiveDependencies = false;
protected $whitelistAllDependencies = false;
@@ -192,6 +194,10 @@ class Installer
gc_collect_cycles();
gc_disable();
+ if ($this->updateWhitelist && $this->updateMirrors) {
+ throw new \RuntimeException("The installer options updateMirrors and updateWhitelist are mutually exclusive.");
+ }
+
// Force update if there is no lock file present
if (!$this->update && !$this->locker->isLocked()) {
// TODO throw an error instead?
@@ -370,8 +376,15 @@ class Installer
$links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
- foreach ($links as $link) {
- $request->install($link->getTarget(), $link->getConstraint());
+ // if we're updating mirrors we want to keep exactly the same versions installed which are in the lock file, but we want current remote metadata
+ if ($this->updateMirrors) {
+ foreach ($lockedRepository->getPackages() as $lockedPackage) {
+ $request->install($lockedPackage->getName(), new Constraint('==', $lockedPackage->getVersion()));
+ }
+ } else {
+ foreach ($links as $link) {
+ $request->install($link->getTarget(), $link->getConstraint());
+ }
}
// if the updateWhitelist is enabled, packages not in it are also fixed
@@ -489,8 +502,8 @@ class Installer
}
$updatedLock = $this->locker->setLockData(
- $lockTransaction->getNewLockPackages(false),
- $lockTransaction->getNewLockPackages(true),
+ $lockTransaction->getNewLockPackages(false, $this->updateMirrors),
+ $lockTransaction->getNewLockPackages(true, $this->updateMirrors),
$platformReqs,
$platformDevReqs,
$aliases,
@@ -912,23 +925,6 @@ class Installer
return $normalizedAliases;
}
- // TODO do we still need this function?
- private function updateInstallReferences(PackageInterface $package, $reference)
- {
- if (!$reference) {
- return;
- }
-
- $package->setSourceReference($reference);
-
- if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $package->getDistUrl())) {
- $package->setDistReference($reference);
- $package->setDistUrl(preg_replace('{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i', $reference, $package->getDistUrl()));
- } elseif ($package->getDistReference()) { // update the dist reference if there was one, but if none was provided ignore it
- $package->setDistReference($reference);
- }
- }
-
/**
* @param PlatformRepository $platformRepo
* @param array $aliases
@@ -1044,7 +1040,7 @@ class Installer
$depPackages = array_merge($depPackages, call_user_func_array('array_merge', $matchesByPattern));
}
- if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock', 'mirrors'))) {
+ if (count($depPackages) == 0 && !$nameMatchesRequiredPackage) {
$this->io->writeError('Package "' . $packageName . '" listed for update is not installed. Ignoring.');
}
@@ -1347,6 +1343,19 @@ class Installer
return $this;
}
+ /**
+ * Update the lock file to the exact same versions and references but use current remote metadata like URLs and mirror info
+ *
+ * @param bool $updateMirrors
+ * @return Installer
+ */
+ public function setUpdateMirrors($updateMirrors)
+ {
+ $this->updateMirrors = $updateMirrors;
+
+ return $this;
+ }
+
/**
* restrict the update operation to a few packages, all other packages
* that are already installed will be kept at their current version
diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php
index 89f197856..b103139dd 100644
--- a/src/Composer/Package/AliasPackage.php
+++ b/src/Composer/Package/AliasPackage.php
@@ -411,4 +411,9 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return $this->aliasOf->setDistType($type);
}
+
+ public function setSourceDistReferences($reference)
+ {
+ return $this->aliasOf->setSourceDistReferences($reference);
+ }
}
diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php
index 6c7b426e7..c633e1856 100644
--- a/src/Composer/Package/Package.php
+++ b/src/Composer/Package/Package.php
@@ -569,6 +569,23 @@ class Package extends BasePackage
return $this->archiveExcludes;
}
+ /**
+ * {@inheritDoc}
+ */
+ public function setSourceDistReferences($reference)
+ {
+ $this->setSourceReference($reference);
+
+ // only bitbucket, github and gitlab have auto generated dist URLs that easily allow replacing the reference in the dist URL
+ // TODO generalize this a bit for self-managed/on-prem versions? Some kind of replace token in dist urls which allow this?
+ if (preg_match('{^https?://(?:(?:www\.)?bitbucket\.org|(api\.)?github\.com|(?:www\.)?gitlab\.com)/}i', $this->getDistUrl())) {
+ $this->setDistReference($reference);
+ $this->setDistUrl(preg_replace('{(?<=/|sha=)[a-f0-9]{40}(?=/|$)}i', $reference, $this->getDistUrl()));
+ } elseif ($this->getDistReference()) { // update the dist reference if there was one, but if none was provided ignore it
+ $this->setDistReference($reference);
+ }
+ }
+
/**
* Replaces current version and pretty version with passed values.
* It also sets stability.
diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php
index 25a2e9bfe..7e83839ff 100644
--- a/src/Composer/Package/PackageInterface.php
+++ b/src/Composer/Package/PackageInterface.php
@@ -386,4 +386,13 @@ interface PackageInterface
* @return void
*/
public function setDistReference($reference);
+
+ /**
+ * Set dist and source references and update dist URL for ones that contain a reference
+ *
+ * @param string $reference
+ *
+ * @return void
+ */
+ public function setSourceDistReferences($reference);
}
diff --git a/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test b/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test
index 9d88870b0..9bfca4c85 100644
--- a/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test
+++ b/tests/Composer/Test/Fixtures/installer/update-mirrors-changes-url.test
@@ -149,14 +149,14 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
"packages": [
{
"name": "a/a", "version": "dev-master",
- "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/a/newa", "type": "git" },
- "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/zipball/2222222222222222222222222222222222222222", "type": "zip" },
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/newa", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/newa/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
"name": "b/b", "version": "2.0.3",
- "source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" },
- "dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" },
+ "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/newb", "type": "git" },
+ "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/newb/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
{
@@ -171,12 +171,6 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library"
},
- {
- "name": "e/e", "version": "dev-master",
- "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/e/newe", "type": "git" },
- "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/e/newe/zipball/1111111111111111111111111111111111111111", "type": "zip" },
- "type": "library"
- },
{
"name": "f/f", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/newf", "type": "git" },
@@ -185,8 +179,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
},
{
"name": "g/g", "version": "dev-master",
- "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/g/newg", "type": "git" },
- "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/g/newg/zipball/1111111111111111111111111111111111111111", "type": "zip" },
+ "source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/newg", "type": "git" },
+ "dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/newg/zipball/0000000000000000000000000000000000000000", "type": "zip" },
"type": "library"
}
],
@@ -206,8 +200,5 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
"platform-dev": []
}
--RUN--
-update a/a b/b d/d g/g
+update mirrors
--EXPECT--
-Installing e/e (dev-master 1111111)
-Updating a/a (dev-master 1111111) to a/a (dev-master 2222222)
-Updating g/g (dev-master 0000000) to g/g (dev-master 1111111)
diff --git a/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test b/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test
index dfb3f650d..a82487a31 100644
--- a/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test
+++ b/tests/Composer/Test/Fixtures/installer/update-picks-up-change-of-vcs-type.test
@@ -42,7 +42,7 @@ Converting from one VCS type to another (including an URL change) should update
"platform-dev": []
}
--RUN--
-update
+update mirrors
--EXPECT-LOCK--
{
"packages": [
diff --git a/tests/Composer/Test/InstallerTest.php b/tests/Composer/Test/InstallerTest.php
index ce87d111f..f1e55a794 100644
--- a/tests/Composer/Test/InstallerTest.php
+++ b/tests/Composer/Test/InstallerTest.php
@@ -269,11 +269,19 @@ class InstallerTest extends TestCase
});
$application->get('update')->setCode(function ($input, $output) use ($installer) {
+ $packages = $input->getArgument('packages');
+ $filteredPackages = array_filter($packages, function ($package) {
+ return !in_array($package, array('lock', 'nothing', 'mirrors'), true);
+ });
+ $updateMirrors = $input->getOption('lock') || count($filteredPackages) != count($packages);
+ $packages = $filteredPackages;
+
$installer
->setDevMode(!$input->getOption('no-dev'))
->setUpdate(true)
->setDryRun($input->getOption('dry-run'))
- ->setUpdateWhitelist($input->getArgument('packages'))
+ ->setUpdateMirrors($updateMirrors)
+ ->setUpdateWhitelist($packages)
->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
->setPreferStable($input->getOption('prefer-stable'))