1
0
Fork 0

Implement update mirrors/nothing/lock as its own installer mode

These special commands no longer (ab)use the partial update mechanism
but rather create a special install request for all current lock file
contents and later override any modified code references to the
originals. This leads to up to date remote metadata but no other
changes.
pull/7936/head
Nils Adermann 2019-11-07 17:35:44 +01:00
parent eaae360ce6
commit 6925005ac9
10 changed files with 104 additions and 57 deletions

View File

@ -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('<error>You cannot simultaneously update only a selection of packages and regenerate the lock file metadata.</error>');
return -1;
}
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
@ -146,7 +159,8 @@ EOT
->setClassMapAuthoritative($authoritative) ->setClassMapAuthoritative($authoritative)
->setApcuAutoloader($apcu) ->setApcuAutoloader($apcu)
->setUpdate(true) ->setUpdate(true)
->setUpdateWhitelist($input->getOption('lock') ? array('lock') : $packages) ->setUpdateMirrors($updateMirrors)
->setUpdateWhitelist($packages)
->setWhitelistTransitiveDependencies($input->getOption('with-dependencies')) ->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
->setWhitelistAllDependencies($input->getOption('with-all-dependencies')) ->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs')) ->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))

View File

@ -152,11 +152,19 @@ class LockTransaction
} }
// TODO additionalFixedRepository needs to be looked at here as well? // TODO additionalFixedRepository needs to be looked at here as well?
public function getNewLockPackages($devMode) public function getNewLockPackages($devMode, $updateMirrors = false)
{ {
$packages = array(); $packages = array();
foreach ($this->resultPackages[$devMode ? 'dev' : 'non-dev'] as $package) { foreach ($this->resultPackages[$devMode ? 'dev' : 'non-dev'] as $package) {
if (!($package instanceof AliasPackage) && !($package instanceof RootAliasPackage)) { 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; $packages[] = $package;
} }
} }

View File

@ -184,7 +184,7 @@ class PoolBuilder
if (isset($this->rootReferences[$name])) { if (isset($this->rootReferences[$name])) {
// do not modify the references on already locked packages // do not modify the references on already locked packages
if (!$request->isFixedPackage($package)) { if (!$request->isFixedPackage($package)) {
$this->setReferences($package, $this->rootReferences[$name]); $package->setSourceDistReferences($this->rootReferences[$name]);
} }
} }
@ -225,19 +225,5 @@ class PoolBuilder
return $loadNames; 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);
}
}
} }

View File

@ -38,6 +38,7 @@ use Composer\Package\AliasPackage;
use Composer\Package\BasePackage; use Composer\Package\BasePackage;
use Composer\Package\CompletePackage; use Composer\Package\CompletePackage;
use Composer\Package\Link; use Composer\Package\Link;
use Composer\Package\LinkConstraint\VersionConstraint;
use Composer\Package\Loader\ArrayLoader; use Composer\Package\Loader\ArrayLoader;
use Composer\Package\Dumper\ArrayDumper; use Composer\Package\Dumper\ArrayDumper;
use Composer\Package\Package; use Composer\Package\Package;
@ -137,6 +138,7 @@ class Installer
* *
* @var array|null * @var array|null
*/ */
protected $updateMirrors = false;
protected $updateWhitelist = null; protected $updateWhitelist = null;
protected $whitelistTransitiveDependencies = false; protected $whitelistTransitiveDependencies = false;
protected $whitelistAllDependencies = false; protected $whitelistAllDependencies = false;
@ -192,6 +194,10 @@ class Installer
gc_collect_cycles(); gc_collect_cycles();
gc_disable(); 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 // Force update if there is no lock file present
if (!$this->update && !$this->locker->isLocked()) { if (!$this->update && !$this->locker->isLocked()) {
// TODO throw an error instead? // TODO throw an error instead?
@ -370,8 +376,15 @@ class Installer
$links = array_merge($this->package->getRequires(), $this->package->getDevRequires()); $links = array_merge($this->package->getRequires(), $this->package->getDevRequires());
foreach ($links as $link) { // 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
$request->install($link->getTarget(), $link->getConstraint()); 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 // if the updateWhitelist is enabled, packages not in it are also fixed
@ -489,8 +502,8 @@ class Installer
} }
$updatedLock = $this->locker->setLockData( $updatedLock = $this->locker->setLockData(
$lockTransaction->getNewLockPackages(false), $lockTransaction->getNewLockPackages(false, $this->updateMirrors),
$lockTransaction->getNewLockPackages(true), $lockTransaction->getNewLockPackages(true, $this->updateMirrors),
$platformReqs, $platformReqs,
$platformDevReqs, $platformDevReqs,
$aliases, $aliases,
@ -912,23 +925,6 @@ class Installer
return $normalizedAliases; 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 PlatformRepository $platformRepo
* @param array $aliases * @param array $aliases
@ -1044,7 +1040,7 @@ class Installer
$depPackages = array_merge($depPackages, call_user_func_array('array_merge', $matchesByPattern)); $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('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>'); $this->io->writeError('<warning>Package "' . $packageName . '" listed for update is not installed. Ignoring.</warning>');
} }
@ -1347,6 +1343,19 @@ class Installer
return $this; 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 * restrict the update operation to a few packages, all other packages
* that are already installed will be kept at their current version * that are already installed will be kept at their current version

View File

@ -411,4 +411,9 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{ {
return $this->aliasOf->setDistType($type); return $this->aliasOf->setDistType($type);
} }
public function setSourceDistReferences($reference)
{
return $this->aliasOf->setSourceDistReferences($reference);
}
} }

View File

@ -569,6 +569,23 @@ class Package extends BasePackage
return $this->archiveExcludes; 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. * Replaces current version and pretty version with passed values.
* It also sets stability. * It also sets stability.

View File

@ -386,4 +386,13 @@ interface PackageInterface
* @return void * @return void
*/ */
public function setDistReference($reference); 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);
} }

View File

@ -149,14 +149,14 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
"packages": [ "packages": [
{ {
"name": "a/a", "version": "dev-master", "name": "a/a", "version": "dev-master",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/a/newa", "type": "git" }, "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/newa", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/zipball/2222222222222222222222222222222222222222", "type": "zip" }, "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/newa/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library" "type": "library"
}, },
{ {
"name": "b/b", "version": "2.0.3", "name": "b/b", "version": "2.0.3",
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" }, "source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/newb", "type": "git" },
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" }, "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/newb/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library" "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" }, "dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/zipball/1111111111111111111111111111111111111111", "type": "zip" },
"type": "library" "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", "name": "f/f", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/newf", "type": "git" }, "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", "name": "g/g", "version": "dev-master",
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/g/newg", "type": "git" }, "source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/newg", "type": "git" },
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/g/newg/zipball/1111111111111111111111111111111111111111", "type": "zip" }, "dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/newg/zipball/0000000000000000000000000000000000000000", "type": "zip" },
"type": "library" "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": [] "platform-dev": []
} }
--RUN-- --RUN--
update a/a b/b d/d g/g update mirrors
--EXPECT-- --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)

View File

@ -42,7 +42,7 @@ Converting from one VCS type to another (including an URL change) should update
"platform-dev": [] "platform-dev": []
} }
--RUN-- --RUN--
update update mirrors
--EXPECT-LOCK-- --EXPECT-LOCK--
{ {
"packages": [ "packages": [

View File

@ -269,11 +269,19 @@ class InstallerTest extends TestCase
}); });
$application->get('update')->setCode(function ($input, $output) use ($installer) { $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 $installer
->setDevMode(!$input->getOption('no-dev')) ->setDevMode(!$input->getOption('no-dev'))
->setUpdate(true) ->setUpdate(true)
->setDryRun($input->getOption('dry-run')) ->setDryRun($input->getOption('dry-run'))
->setUpdateWhitelist($input->getArgument('packages')) ->setUpdateMirrors($updateMirrors)
->setUpdateWhitelist($packages)
->setWhitelistTransitiveDependencies($input->getOption('with-dependencies')) ->setWhitelistTransitiveDependencies($input->getOption('with-dependencies'))
->setWhitelistAllDependencies($input->getOption('with-all-dependencies')) ->setWhitelistAllDependencies($input->getOption('with-all-dependencies'))
->setPreferStable($input->getOption('prefer-stable')) ->setPreferStable($input->getOption('prefer-stable'))