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
parent
eaae360ce6
commit
6925005ac9
|
@ -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'))
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -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": [
|
||||||
|
|
|
@ -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'))
|
||||||
|
|
Loading…
Reference in New Issue