Merge branch 'master' into filter-packages
commit
ea0ce9dd7d
|
@ -89,7 +89,7 @@ jobs:
|
|||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
extensions: "intl"
|
||||
extensions: "intl, zip"
|
||||
ini-values: "memory_limit=-1, phar.readonly=0"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ jobs:
|
|||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
extensions: "intl"
|
||||
extensions: "intl, zip"
|
||||
ini-values: "memory_limit=-1"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: "cs2pr"
|
||||
|
@ -52,5 +52,5 @@ jobs:
|
|||
|
||||
- name: Run PHPStan
|
||||
run: |
|
||||
bin/composer require --dev phpstan/phpstan:^0.12 phpunit/phpunit:^7.5 --with-all-dependencies
|
||||
bin/composer require --dev phpstan/phpstan:^0.12.26 phpunit/phpunit:^7.5 --with-all-dependencies
|
||||
vendor/bin/phpstan analyse --configuration=phpstan/config.neon || vendor/bin/phpstan analyse --configuration=phpstan/config.neon --error-format=checkstyle | cs2pr
|
||||
|
|
|
@ -785,7 +785,7 @@ Lists the name, version and license of every package installed. Use
|
|||
|
||||
### Options
|
||||
|
||||
* **--format:** Format of the output: text or json (default: "text")
|
||||
* **--format:** Format of the output: text, json or summary (default: "text")
|
||||
* **--no-dev:** Remove dev dependencies from the output
|
||||
|
||||
## run-script
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
# HTTP basic authentication
|
||||
|
||||
Your [Satis or Toran Proxy](handling-private-packages-with-satis.md) server
|
||||
Your [Satis or Private Packagist](handling-private-packages-with-satis.md) server
|
||||
could be secured with http basic authentication. In order to allow your project
|
||||
to have access to these packages you will have to tell composer how to
|
||||
authenticate with your credentials.
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
parameters:
|
||||
level: 1
|
||||
autoload_files:
|
||||
- '../src/bootstrap.php'
|
||||
excludes_analyse:
|
||||
- '../tests/Composer/Test/Fixtures/*'
|
||||
- '../tests/Composer/Test/Autoload/Fixtures/*'
|
||||
|
@ -27,6 +25,7 @@ parameters:
|
|||
|
||||
# BC with older PHPUnit
|
||||
- '~^Call to an undefined static method PHPUnit\\Framework\\TestCase::setExpectedException\(\)\.$~'
|
||||
|
||||
paths:
|
||||
- ../src
|
||||
- ../tests
|
||||
|
|
|
@ -565,6 +565,10 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"default-branch": {
|
||||
"type": ["boolean"],
|
||||
"description": "Internal use only, do not specify this in composer.json. Indicates whether this version is the default branch of the linked VCS repository. Defaults to false."
|
||||
},
|
||||
"abandoned": {
|
||||
"type": ["boolean", "string"],
|
||||
"description": "Indicates whether this package has been abandoned, it can be boolean or a package name/URL pointing to a recommended alternative. Defaults to false."
|
||||
|
|
|
@ -257,16 +257,16 @@ EOF;
|
|||
EOF;
|
||||
}
|
||||
|
||||
$blacklist = null;
|
||||
$excluded = null;
|
||||
if (!empty($autoloads['exclude-from-classmap'])) {
|
||||
$blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
|
||||
$excluded = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
|
||||
}
|
||||
|
||||
$classMap = array();
|
||||
$ambiguousClasses = array();
|
||||
$scannedFiles = array();
|
||||
foreach ($autoloads['classmap'] as $dir) {
|
||||
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap, $ambiguousClasses, $scannedFiles);
|
||||
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $excluded, null, null, $classMap, $ambiguousClasses, $scannedFiles);
|
||||
}
|
||||
|
||||
if ($scanPsrPackages) {
|
||||
|
@ -289,7 +289,7 @@ EOF;
|
|||
continue;
|
||||
}
|
||||
|
||||
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap, $ambiguousClasses, $scannedFiles);
|
||||
$classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $excluded, $namespace, $group['type'], $classMap, $ambiguousClasses, $scannedFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -368,9 +368,9 @@ EOF;
|
|||
return count($classMap);
|
||||
}
|
||||
|
||||
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespaceFilter, $autoloadType, array $classMap, array &$ambiguousClasses, array &$scannedFiles)
|
||||
private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $excluded, $namespaceFilter, $autoloadType, array $classMap, array &$ambiguousClasses, array &$scannedFiles)
|
||||
{
|
||||
foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType, true, $scannedFiles) as $class => $path) {
|
||||
foreach ($this->generateClassMap($dir, $excluded, $namespaceFilter, $autoloadType, true, $scannedFiles) as $class => $path) {
|
||||
$pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n";
|
||||
if (!isset($classMap[$class])) {
|
||||
$classMap[$class] = $pathCode;
|
||||
|
@ -382,9 +382,9 @@ EOF;
|
|||
return $classMap;
|
||||
}
|
||||
|
||||
private function generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType, $showAmbiguousWarning, array &$scannedFiles)
|
||||
private function generateClassMap($dir, $excluded, $namespaceFilter, $autoloadType, $showAmbiguousWarning, array &$scannedFiles)
|
||||
{
|
||||
return ClassMapGenerator::createMap($dir, $blacklist, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType, $scannedFiles);
|
||||
return ClassMapGenerator::createMap($dir, $excluded, $showAmbiguousWarning ? $this->io : null, $namespaceFilter, $autoloadType, $scannedFiles);
|
||||
}
|
||||
|
||||
public function buildPackageMap(InstallationManager $installationManager, PackageInterface $mainPackage, array $packages)
|
||||
|
@ -488,15 +488,15 @@ EOF;
|
|||
}
|
||||
|
||||
if (isset($autoloads['classmap'])) {
|
||||
$blacklist = null;
|
||||
$excluded = null;
|
||||
if (!empty($autoloads['exclude-from-classmap'])) {
|
||||
$blacklist = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
|
||||
$excluded = '{(' . implode('|', $autoloads['exclude-from-classmap']) . ')}';
|
||||
}
|
||||
|
||||
$scannedFiles = array();
|
||||
foreach ($autoloads['classmap'] as $dir) {
|
||||
try {
|
||||
$loader->addClassMap($this->generateClassMap($dir, $blacklist, null, null, false, $scannedFiles));
|
||||
$loader->addClassMap($this->generateClassMap($dir, $excluded, null, null, false, $scannedFiles));
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->io->writeError('<warning>'.$e->getMessage().'</warning>');
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class ClassMapGenerator
|
|||
* Iterate over all files in the given directory searching for classes
|
||||
*
|
||||
* @param \Iterator|string $path The path to search in or an iterator
|
||||
* @param string $blacklist Regex that matches against the file path that exclude from the classmap.
|
||||
* @param string $excluded Regex that matches against the file path that exclude from the classmap.
|
||||
* @param IOInterface $io IO object
|
||||
* @param string $namespace Optional namespace prefix to filter by
|
||||
* @param string $autoloadType psr-0|psr-4 Optional autoload standard to use mapping rules
|
||||
|
@ -59,7 +59,7 @@ class ClassMapGenerator
|
|||
* @throws \RuntimeException When the path is neither an existing file nor directory
|
||||
* @return array A class map array
|
||||
*/
|
||||
public static function createMap($path, $blacklist = null, IOInterface $io = null, $namespace = null, $autoloadType = null, &$scannedFiles = array())
|
||||
public static function createMap($path, $excluded = null, IOInterface $io = null, $namespace = null, $autoloadType = null, &$scannedFiles = array())
|
||||
{
|
||||
$basePath = $path;
|
||||
if (is_string($path)) {
|
||||
|
@ -102,12 +102,12 @@ class ClassMapGenerator
|
|||
continue;
|
||||
}
|
||||
|
||||
// check the realpath of the file against the blacklist as the path might be a symlink and the blacklist is realpath'd so symlink are resolved
|
||||
if ($blacklist && preg_match($blacklist, strtr($realPath, '\\', '/'))) {
|
||||
// check the realpath of the file against the excluded paths as the path might be a symlink and the excluded path is realpath'd so symlink are resolved
|
||||
if ($excluded && preg_match($excluded, strtr($realPath, '\\', '/'))) {
|
||||
continue;
|
||||
}
|
||||
// check non-realpath of file for directories symlink in project dir
|
||||
if ($blacklist && preg_match($blacklist, strtr($filePath, '\\', '/'))) {
|
||||
if ($excluded && preg_match($excluded, strtr($filePath, '\\', '/'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ use Composer\Plugin\CommandEvent;
|
|||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Loop;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
@ -112,9 +113,10 @@ EOT
|
|||
$archiveManager = $composer->getArchiveManager();
|
||||
} else {
|
||||
$factory = new Factory;
|
||||
$process = new ProcessExecutor();
|
||||
$httpDownloader = $factory->createHttpDownloader($io, $config);
|
||||
$downloadManager = $factory->createDownloadManager($io, $config, $httpDownloader);
|
||||
$archiveManager = $factory->createArchiveManager($config, $downloadManager, new Loop($httpDownloader));
|
||||
$downloadManager = $factory->createDownloadManager($io, $config, $httpDownloader, $process);
|
||||
$archiveManager = $factory->createArchiveManager($config, $downloadManager, new Loop($httpDownloader, $process));
|
||||
}
|
||||
|
||||
if ($packageName) {
|
||||
|
|
|
@ -201,6 +201,8 @@ EOT
|
|||
|
||||
// install dependencies of the created project
|
||||
if ($noInstall === false) {
|
||||
$composer->getInstallationManager()->setOutputProgress(!$noProgress);
|
||||
|
||||
$installer = Installer::create($io, $composer);
|
||||
$installer->setPreferSource($preferSource)
|
||||
->setPreferDist($preferDist)
|
||||
|
@ -212,6 +214,10 @@ EOT
|
|||
->setClassMapAuthoritative($config->get('classmap-authoritative'))
|
||||
->setApcuAutoloader($config->get('apcu-autoloader'));
|
||||
|
||||
if (!$composer->getLocker()->isLocked()) {
|
||||
$installer->setUpdate(true);
|
||||
}
|
||||
|
||||
if ($disablePlugins) {
|
||||
$installer->disablePlugins();
|
||||
}
|
||||
|
@ -405,7 +411,8 @@ EOT
|
|||
->setPreferDist($preferDist);
|
||||
|
||||
$projectInstaller = new ProjectInstaller($directory, $dm, $fs);
|
||||
$im = $factory->createInstallationManager(new Loop($httpDownloader), $io);
|
||||
$im = $factory->createInstallationManager(new Loop($httpDownloader, $process), $io);
|
||||
$im->setOutputProgress(!$noProgress);
|
||||
$im->addInstaller($projectInstaller);
|
||||
$im->execute(new InstalledFilesystemRepository(new JsonFile('php://memory')), array(new InstallOperation($package)));
|
||||
$im->notifyInstalls($io);
|
||||
|
|
|
@ -431,7 +431,11 @@ EOT
|
|||
}
|
||||
|
||||
$versionsUtil = new Versions($config, $this->httpDownloader);
|
||||
$latest = $versionsUtil->getLatest();
|
||||
try {
|
||||
$latest = $versionsUtil->getLatest();
|
||||
} catch (\Exception $e) {
|
||||
return $e;
|
||||
}
|
||||
|
||||
if (Composer::VERSION !== $latest['version'] && Composer::VERSION !== '@package_version@') {
|
||||
return '<comment>You are not running the latest '.$versionsUtil->getChannel().' version, run `composer self-update` to update ('.Composer::VERSION.' => '.$latest['version'].')</comment>';
|
||||
|
|
|
@ -106,6 +106,8 @@ EOT
|
|||
|
||||
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
|
||||
|
||||
$composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$install
|
||||
->setDryRun($input->getOption('dry-run'))
|
||||
->setVerbose($input->getOption('verbose'))
|
||||
|
|
|
@ -21,6 +21,7 @@ use Symfony\Component\Console\Helper\Table;
|
|||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
/**
|
||||
* @author Benoît Merlet <benoit.merlet@gmail.com>
|
||||
|
@ -111,6 +112,28 @@ EOT
|
|||
)));
|
||||
break;
|
||||
|
||||
case 'summary':
|
||||
$dependencies = array();
|
||||
foreach ($packages as $package) {
|
||||
$license = $package->getLicense();
|
||||
$licenseName = $license[0];
|
||||
if (!isset($dependencies[$licenseName])) {
|
||||
$dependencies[$licenseName] = 0;
|
||||
}
|
||||
$dependencies[$licenseName]++;
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
foreach ($dependencies as $usedLicense => $numberOfDependencies) {
|
||||
$rows[] = array($usedLicense, $numberOfDependencies);
|
||||
}
|
||||
|
||||
$symfonyIo = new SymfonyStyle($input, $output);
|
||||
$symfonyIo->table(
|
||||
array('License', 'Number of dependencies'),
|
||||
$rows
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException(sprintf('Unsupported format "%s". See help for supported formats.', $format));
|
||||
}
|
||||
|
|
|
@ -220,6 +220,8 @@ EOT
|
|||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'remove', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$install = Installer::create($io, $composer);
|
||||
|
||||
$updateDevMode = !$input->getOption('update-no-dev');
|
||||
|
|
|
@ -286,6 +286,8 @@ EOT
|
|||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$install = Installer::create($io, $composer);
|
||||
|
||||
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
|
||||
|
|
|
@ -180,6 +180,8 @@ EOT
|
|||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'update', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$composer->getInstallationManager()->setOutputProgress(!$input->getOption('no-progress'));
|
||||
|
||||
$install = Installer::create($io, $composer);
|
||||
|
||||
$config = $composer->getConfig();
|
||||
|
|
|
@ -156,7 +156,7 @@ class Application extends BaseApplication
|
|||
}
|
||||
|
||||
// prompt user for dir change if no composer.json is present in current dir
|
||||
if ($io->isInteractive() && !$newWorkDir && !in_array($commandName, array('', 'list', 'init', 'about', 'help', 'diagnose', 'self-update', 'global', 'create-project'), true) && !file_exists(Factory::getComposerFile())) {
|
||||
if ($io->isInteractive() && !$newWorkDir && !in_array($commandName, array('', 'list', 'init', 'about', 'help', 'diagnose', 'self-update', 'global', 'create-project', 'outdated'), true) && !file_exists(Factory::getComposerFile())) {
|
||||
$dir = dirname(getcwd());
|
||||
$home = realpath(getenv('HOME') ?: getenv('USERPROFILE') ?: '/');
|
||||
|
||||
|
|
|
@ -61,7 +61,12 @@ class InstallOperation extends SolverOperation
|
|||
*/
|
||||
public function show($lock)
|
||||
{
|
||||
return ($lock ? 'Locking ' : 'Installing ').'<info>'.$this->package->getPrettyName().'</info> (<comment>'.$this->package->getFullPrettyVersion().'</comment>)';
|
||||
return self::format($this->package, $lock);
|
||||
}
|
||||
|
||||
public static function format(PackageInterface $package, $lock = false)
|
||||
{
|
||||
return ($lock ? 'Locking ' : 'Installing ').'<info>'.$package->getPrettyName().'</info> (<comment>'.$package->getFullPrettyVersion().'</comment>)';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,7 +61,12 @@ class UninstallOperation extends SolverOperation
|
|||
*/
|
||||
public function show($lock)
|
||||
{
|
||||
return 'Removing <info>'.$this->package->getPrettyName().'</info> (<comment>'.$this->package->getFullPrettyVersion().'</comment>)';
|
||||
return self::format($this->package, $lock);
|
||||
}
|
||||
|
||||
public static function format(PackageInterface $package, $lock = false)
|
||||
{
|
||||
return 'Removing <info>'.$package->getPrettyName().'</info> (<comment>'.$package->getFullPrettyVersion().'</comment>)';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -75,20 +75,25 @@ class UpdateOperation extends SolverOperation
|
|||
*/
|
||||
public function show($lock)
|
||||
{
|
||||
$fromVersion = $this->initialPackage->getFullPrettyVersion();
|
||||
$toVersion = $this->targetPackage->getFullPrettyVersion();
|
||||
return self::format($this->initialPackage, $this->targetPackage, $lock);
|
||||
}
|
||||
|
||||
if ($fromVersion === $toVersion && $this->initialPackage->getSourceReference() !== $this->targetPackage->getSourceReference()) {
|
||||
$fromVersion = $this->initialPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_SOURCE_REF);
|
||||
$toVersion = $this->targetPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_SOURCE_REF);
|
||||
} elseif ($fromVersion === $toVersion && $this->initialPackage->getDistReference() !== $this->targetPackage->getDistReference()) {
|
||||
$fromVersion = $this->initialPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_DIST_REF);
|
||||
$toVersion = $this->targetPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_DIST_REF);
|
||||
public static function format(PackageInterface $initialPackage, PackageInterface $targetPackage, $lock = false)
|
||||
{
|
||||
$fromVersion = $initialPackage->getFullPrettyVersion();
|
||||
$toVersion = $targetPackage->getFullPrettyVersion();
|
||||
|
||||
if ($fromVersion === $toVersion && $initialPackage->getSourceReference() !== $targetPackage->getSourceReference()) {
|
||||
$fromVersion = $initialPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_SOURCE_REF);
|
||||
$toVersion = $targetPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_SOURCE_REF);
|
||||
} elseif ($fromVersion === $toVersion && $initialPackage->getDistReference() !== $targetPackage->getDistReference()) {
|
||||
$fromVersion = $initialPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_DIST_REF);
|
||||
$toVersion = $targetPackage->getFullPrettyVersion(true, PackageInterface::DISPLAY_DIST_REF);
|
||||
}
|
||||
|
||||
$actionName = VersionParser::isUpgrade($this->initialPackage->getVersion(), $this->targetPackage->getVersion()) ? 'Upgrading' : 'Downgrading';
|
||||
$actionName = VersionParser::isUpgrade($initialPackage->getVersion(), $targetPackage->getVersion()) ? 'Upgrading' : 'Downgrading';
|
||||
|
||||
return $actionName.' <info>'.$this->initialPackage->getPrettyName().'</info> (<comment>'.$fromVersion.'</comment> => <comment>'.$toVersion.'</comment>)';
|
||||
return $actionName.' <info>'.$initialPackage->getPrettyName().'</info> (<comment>'.$fromVersion.'</comment> => <comment>'.$toVersion.'</comment>)';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\DependencyResolver;
|
|||
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Semver\CompilingMatcher;
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Constraint\Constraint;
|
||||
use Composer\Package\PackageInterface;
|
||||
|
@ -146,9 +147,7 @@ class Pool implements \Countable
|
|||
$candidateVersion = $candidate->getVersion();
|
||||
|
||||
if ($candidateName === $name) {
|
||||
$pkgConstraint = new Constraint('==', $candidateVersion);
|
||||
|
||||
if ($constraint === null || $constraint->matches($pkgConstraint)) {
|
||||
if ($constraint === null || CompilingMatcher::match($constraint, Constraint::OP_EQ, $candidateVersion)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,21 +12,21 @@
|
|||
|
||||
namespace Composer\DependencyResolver;
|
||||
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\Package;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\Version\StabilityFilter;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Plugin\PrePoolCreateEvent;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\Repository\RootPackageRepository;
|
||||
use Composer\Semver\CompilingMatcher;
|
||||
use Composer\Semver\Constraint\Constraint;
|
||||
use Composer\Semver\Constraint\ConstraintInterface;
|
||||
use Composer\Semver\Constraint\MatchAllConstraint;
|
||||
use Composer\Semver\Constraint\MultiConstraint;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Plugin\PrePoolCreateEvent;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
use Composer\Semver\Intervals;
|
||||
|
||||
/**
|
||||
|
@ -103,7 +103,7 @@ class PoolBuilder
|
|||
* @param int[] $stabilityFlags an array of package name => BasePackage::STABILITY_* value
|
||||
* @psalm-param array<string, int> $stabilityFlags
|
||||
* @param array[] $rootAliases
|
||||
* @psalm-param list<array{package: string, version: string, alias: string, alias_normalized: string}> $rootAliases
|
||||
* @psalm-param array<string, array<string, array{alias: string, alias_normalized: string}>> $rootAliases
|
||||
* @param string[] $rootReferences an array of package name => source reference
|
||||
* @psalm-param array<string, string> $rootReferences
|
||||
*/
|
||||
|
@ -111,7 +111,7 @@ class PoolBuilder
|
|||
{
|
||||
$this->acceptableStabilities = $acceptableStabilities;
|
||||
$this->stabilityFlags = $stabilityFlags;
|
||||
$this->rootAliases = $this->getRootAliasesPerPackage($rootAliases);
|
||||
$this->rootAliases = $rootAliases;
|
||||
$this->rootReferences = $rootReferences;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->io = $io;
|
||||
|
@ -193,7 +193,7 @@ class PoolBuilder
|
|||
|
||||
$found = false;
|
||||
foreach ($aliasedPackages as $packageOrAlias) {
|
||||
if ($constraint->matches(new Constraint('==', $packageOrAlias->getVersion()))) {
|
||||
if (CompilingMatcher::match($constraint, Constraint::OP_EQ, $packageOrAlias->getVersion())) {
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
|
@ -497,19 +497,5 @@ class PoolBuilder
|
|||
unset($this->aliasMap[spl_object_hash($package)]);
|
||||
}
|
||||
}
|
||||
|
||||
private function getRootAliasesPerPackage(array $aliases)
|
||||
{
|
||||
$normalizedAliases = array();
|
||||
|
||||
foreach ($aliases as $alias) {
|
||||
$normalizedAliases[$alias['package']][$alias['version']] = array(
|
||||
'alias' => $alias['alias'],
|
||||
'alias_normalized' => $alias['alias_normalized'],
|
||||
);
|
||||
}
|
||||
|
||||
return $normalizedAliases;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -264,7 +264,7 @@ abstract class Rule
|
|||
return 'You can only install one version of a package, so only one of these can be installed: ' . $this->formatPackagesUnique($pool, $literals, $isVerbose) . '.';
|
||||
case self::RULE_LEARNED:
|
||||
if (isset($learnedPool[$this->reasonData])) {
|
||||
$learnedString = ', learned rules:' . Problem::formatDeduplicatedRules($learnedPool[$this->reasonData], ' ', $repositorySet, $request, $pool, $installedMap, $learnedPool);
|
||||
$learnedString = ', learned rules:' . Problem::formatDeduplicatedRules($learnedPool[$this->reasonData], ' ', $repositorySet, $request, $pool, $isVerbose, $installedMap, $learnedPool);
|
||||
} else {
|
||||
$learnedString = ' (reasoning unavailable)';
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ use Composer\Package\PackageInterface;
|
|||
use Symfony\Component\Finder\Finder;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Exception\IrrecoverableDownloadException;
|
||||
use React\Promise\PromiseInterface;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
|
||||
/**
|
||||
* Base downloader for archives
|
||||
|
@ -28,14 +30,7 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
{
|
||||
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true)
|
||||
{
|
||||
$res = parent::download($package, $path, $prevPackage, $output);
|
||||
|
||||
// if not downgrading and the dir already exists it seems we have an inconsistent state in the vendor dir and the user should fix it
|
||||
if (!$prevPackage && is_dir($path) && !$this->filesystem->isDirEmpty($path)) {
|
||||
throw new IrrecoverableDownloadException('Expected empty path to extract '.$package.' into but directory exists: '.$path);
|
||||
}
|
||||
|
||||
return $res;
|
||||
return parent::download($package, $path, $prevPackage, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -46,40 +41,75 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
public function install(PackageInterface $package, $path, $output = true)
|
||||
{
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>): Extracting archive");
|
||||
$this->io->writeError(" - " . InstallOperation::format($package).": Extracting archive");
|
||||
} else {
|
||||
$this->io->writeError('Extracting archive', false);
|
||||
}
|
||||
|
||||
$this->filesystem->ensureDirectoryExists($path);
|
||||
if (!$this->filesystem->isDirEmpty($path)) {
|
||||
throw new \UnexpectedValueException('Expected empty path to extract '.$package.' into but directory exists: '.$path);
|
||||
}
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
||||
do {
|
||||
$temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8);
|
||||
} while (is_dir($temporaryDir));
|
||||
|
||||
$this->addCleanupPath($package, $temporaryDir);
|
||||
$this->addCleanupPath($package, $path);
|
||||
|
||||
$this->filesystem->ensureDirectoryExists($temporaryDir);
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
try {
|
||||
$this->filesystem->ensureDirectoryExists($temporaryDir);
|
||||
try {
|
||||
$this->extract($package, $fileName, $temporaryDir);
|
||||
} catch (\Exception $e) {
|
||||
// remove cache if the file was corrupted
|
||||
parent::clearLastCacheWrite($package);
|
||||
throw $e;
|
||||
}
|
||||
$filesystem = $this->filesystem;
|
||||
$self = $this;
|
||||
|
||||
$this->filesystem->unlink($fileName);
|
||||
$cleanup = function () use ($path, $filesystem, $temporaryDir, $package, $self) {
|
||||
// remove cache if the file was corrupted
|
||||
$self->clearLastCacheWrite($package);
|
||||
|
||||
// clean up
|
||||
$filesystem->removeDirectory($path);
|
||||
$filesystem->removeDirectory($temporaryDir);
|
||||
$self->removeCleanupPath($package, $temporaryDir);
|
||||
$self->removeCleanupPath($package, $path);
|
||||
};
|
||||
|
||||
$promise = null;
|
||||
try {
|
||||
$promise = $this->extract($package, $fileName, $temporaryDir);
|
||||
} catch (\Exception $e) {
|
||||
$cleanup();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!$promise instanceof PromiseInterface) {
|
||||
$promise = \React\Promise\resolve();
|
||||
}
|
||||
|
||||
return $promise->then(function () use ($self, $package, $filesystem, $fileName, $temporaryDir, $path) {
|
||||
$filesystem->unlink($fileName);
|
||||
|
||||
/**
|
||||
* Returns the folder content, excluding .DS_Store
|
||||
*
|
||||
* @param string $dir Directory
|
||||
* @return \SplFileInfo[]
|
||||
*/
|
||||
$getFolderContent = function ($dir) {
|
||||
$finder = Finder::create()
|
||||
->ignoreVCS(false)
|
||||
->ignoreDotFiles(false)
|
||||
->notName('.DS_Store')
|
||||
->depth(0)
|
||||
->in($dir);
|
||||
|
||||
return iterator_to_array($finder);
|
||||
};
|
||||
|
||||
$renameAsOne = false;
|
||||
if (!file_exists($path) || ($this->filesystem->isDirEmpty($path) && $this->filesystem->removeDirectory($path))) {
|
||||
if (!file_exists($path) || ($filesystem->isDirEmpty($path) && $filesystem->removeDirectory($path))) {
|
||||
$renameAsOne = true;
|
||||
}
|
||||
|
||||
$contentDir = $this->getFolderContent($temporaryDir);
|
||||
$contentDir = $getFolderContent($temporaryDir);
|
||||
$singleDirAtTopLevel = 1 === count($contentDir) && is_dir(reset($contentDir));
|
||||
|
||||
if ($renameAsOne) {
|
||||
|
@ -89,28 +119,28 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
} else {
|
||||
$extractedDir = $temporaryDir;
|
||||
}
|
||||
$this->filesystem->rename($extractedDir, $path);
|
||||
$filesystem->rename($extractedDir, $path);
|
||||
} else {
|
||||
// only one dir in the archive, extract its contents out of it
|
||||
if ($singleDirAtTopLevel) {
|
||||
$contentDir = $this->getFolderContent((string) reset($contentDir));
|
||||
$contentDir = $getFolderContent((string) reset($contentDir));
|
||||
}
|
||||
|
||||
// move files back out of the temp dir
|
||||
foreach ($contentDir as $file) {
|
||||
$file = (string) $file;
|
||||
$this->filesystem->rename($file, $path . '/' . basename($file));
|
||||
$filesystem->rename($file, $path . '/' . basename($file));
|
||||
}
|
||||
}
|
||||
|
||||
$this->filesystem->removeDirectory($temporaryDir);
|
||||
} catch (\Exception $e) {
|
||||
// clean up
|
||||
$this->filesystem->removeDirectory($path);
|
||||
$this->filesystem->removeDirectory($temporaryDir);
|
||||
$filesystem->removeDirectory($temporaryDir);
|
||||
$self->removeCleanupPath($package, $temporaryDir);
|
||||
$self->removeCleanupPath($package, $path);
|
||||
}, function ($e) use ($cleanup) {
|
||||
$cleanup();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,25 +149,8 @@ abstract class ArchiveDownloader extends FileDownloader
|
|||
* @param string $file Extracted file
|
||||
* @param string $path Directory
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
* @throws \UnexpectedValueException If can not extract downloaded file to path
|
||||
*/
|
||||
abstract protected function extract(PackageInterface $package, $file, $path);
|
||||
|
||||
/**
|
||||
* Returns the folder content, excluding dotfiles
|
||||
*
|
||||
* @param string $dir Directory
|
||||
* @return \SplFileInfo[]
|
||||
*/
|
||||
private function getFolderContent($dir)
|
||||
{
|
||||
$finder = Finder::create()
|
||||
->ignoreVCS(false)
|
||||
->ignoreDotFiles(false)
|
||||
->notName('.DS_Store')
|
||||
->depth(0)
|
||||
->in($dir);
|
||||
|
||||
return iterator_to_array($finder);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ use Composer\Factory;
|
|||
use Composer\IO\IOInterface;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Package\Comparer\Comparer;
|
||||
use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
use Composer\DependencyResolver\Operation\UninstallOperation;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
|
@ -27,7 +30,9 @@ use Composer\EventDispatcher\EventDispatcher;
|
|||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Util\Url as UrlUtil;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Downloader\TransportException;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* Base downloader for files
|
||||
|
@ -51,10 +56,13 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
protected $cache;
|
||||
/** @var EventDispatcher */
|
||||
protected $eventDispatcher;
|
||||
/** @var ProcessExecutor */
|
||||
protected $process;
|
||||
/**
|
||||
* @private this is only public for php 5.3 support in closures
|
||||
*/
|
||||
public $lastCacheWrites = array();
|
||||
private $additionalCleanupPaths = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -66,14 +74,15 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
* @param Cache $cache Cache instance
|
||||
* @param Filesystem $filesystem The filesystem
|
||||
*/
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $filesystem = null)
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $httpDownloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $filesystem = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->httpDownloader = $httpDownloader;
|
||||
$this->filesystem = $filesystem ?: new Filesystem();
|
||||
$this->cache = $cache;
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
$this->filesystem = $filesystem ?: new Filesystem($this->process);
|
||||
|
||||
if ($this->cache && $this->cache->gcIsNecessary()) {
|
||||
$this->cache->gc($config->get('cache-files-ttl'), $config->get('cache-files-maxsize'));
|
||||
|
@ -125,8 +134,9 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
$url = reset($urls);
|
||||
|
||||
if ($eventDispatcher) {
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $httpDownloader, $url['processed']);
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $httpDownloader, $url['processed'], 'package', $package);
|
||||
$eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
$url['processed'] = $preFileDownloadEvent->getProcessedUrl();
|
||||
}
|
||||
|
||||
$checksum = $package->getDistSha1Checksum();
|
||||
|
@ -258,6 +268,12 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
$path,
|
||||
);
|
||||
|
||||
if (isset($this->additionalCleanupPaths[$package->getName()])) {
|
||||
foreach ($this->additionalCleanupPaths[$package->getName()] as $path) {
|
||||
$this->filesystem->remove($path);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($dirsToCleanUp as $dir) {
|
||||
if (is_dir($dir) && $this->filesystem->isDirEmpty($dir)) {
|
||||
$this->filesystem->removeDirectory($dir);
|
||||
|
@ -271,7 +287,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
public function install(PackageInterface $package, $path, $output = true)
|
||||
{
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
$this->io->writeError(" - " . InstallOperation::format($package));
|
||||
}
|
||||
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
@ -291,22 +307,49 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO mark private in v3
|
||||
* @protected This is public due to PHP 5.3
|
||||
*/
|
||||
public function addCleanupPath(PackageInterface $package, $path)
|
||||
{
|
||||
$this->additionalCleanupPaths[$package->getName()][] = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO mark private in v3
|
||||
* @protected This is public due to PHP 5.3
|
||||
*/
|
||||
public function removeCleanupPath(PackageInterface $package, $path)
|
||||
{
|
||||
if (isset($this->additionalCleanupPaths[$package->getName()])) {
|
||||
$idx = array_search($path, $this->additionalCleanupPaths[$package->getName()]);
|
||||
if (false !== $idx) {
|
||||
unset($this->additionalCleanupPaths[$package->getName()][$idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function update(PackageInterface $initial, PackageInterface $target, $path)
|
||||
{
|
||||
$name = $target->getName();
|
||||
$from = $initial->getFullPrettyVersion();
|
||||
$to = $target->getFullPrettyVersion();
|
||||
$this->io->writeError(" - " . UpdateOperation::format($initial, $target) . ": ", false);
|
||||
|
||||
$actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Upgrading' : 'Downgrading';
|
||||
$this->io->writeError(" - " . $actionName . " <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>): ", false);
|
||||
$promise = $this->remove($initial, $path, false);
|
||||
if (!$promise instanceof PromiseInterface) {
|
||||
$promise = \React\Promise\resolve();
|
||||
}
|
||||
$self = $this;
|
||||
$io = $this->io;
|
||||
|
||||
$this->remove($initial, $path, false);
|
||||
$this->install($target, $path, false);
|
||||
return $promise->then(function () use ($self, $target, $path, $io) {
|
||||
$promise = $self->install($target, $path, false);
|
||||
$io->writeError('');
|
||||
|
||||
$this->io->writeError('');
|
||||
return $promise;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -315,7 +358,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
public function remove(PackageInterface $package, $path, $output = true)
|
||||
{
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
$this->io->writeError(" - " . UninstallOperation::format($package));
|
||||
}
|
||||
if (!$this->filesystem->removeDirectory($path)) {
|
||||
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
|
||||
|
@ -380,9 +423,14 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
$output = '';
|
||||
|
||||
try {
|
||||
$res = $this->download($package, $targetDir.'_compare', null, false);
|
||||
if (is_dir($targetDir.'_compare')) {
|
||||
$this->filesystem->removeDirectory($targetDir.'_compare');
|
||||
}
|
||||
|
||||
$this->download($package, $targetDir.'_compare', null, false);
|
||||
$this->httpDownloader->wait();
|
||||
$res = $this->install($package, $targetDir.'_compare', false);
|
||||
$this->install($package, $targetDir.'_compare', false);
|
||||
$this->process->wait();
|
||||
|
||||
$comparer = new Comparer();
|
||||
$comparer->setSource($targetDir.'_compare');
|
||||
|
|
|
@ -61,7 +61,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
GitUtil::cleanEnv();
|
||||
|
||||
$cachePath = $this->config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $url).'/';
|
||||
$gitVersion = $this->gitUtil->getVersion();
|
||||
$gitVersion = GitUtil::getVersion($this->process);
|
||||
|
||||
// --dissociate option is only available since git 2.3.0-rc0
|
||||
if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=') && Cache::isUsable($cachePath)) {
|
||||
|
@ -479,7 +479,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
protected function getCommitLogs($fromReference, $toReference, $path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
$command = sprintf('git log %s..%s --pretty=format:"%%h - %%an: %%s"', ProcessExecutor::escape($fromReference), ProcessExecutor::escape($toReference));
|
||||
$command = sprintf('git log %s..%s --pretty=format:"%%h - %%an: %%s"'.GitUtil::getNoShowSignatureFlag($this->process), ProcessExecutor::escape($fromReference), ProcessExecutor::escape($toReference));
|
||||
|
||||
if (0 !== $this->process->execute($command, $output, $path)) {
|
||||
throw new \RuntimeException('Failed to execute ' . $command . "\n\n" . $this->process->getErrorOutput());
|
||||
|
@ -495,7 +495,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
protected function discardChanges($path)
|
||||
{
|
||||
$path = $this->normalizePath($path);
|
||||
if (0 !== $this->process->execute('git reset --hard', $output, $path)) {
|
||||
if (0 !== $this->process->execute('git clean -df && git reset --hard', $output, $path)) {
|
||||
throw new \RuntimeException("Could not reset changes\n\n:".$this->process->getErrorOutput());
|
||||
}
|
||||
|
||||
|
|
|
@ -29,15 +29,6 @@ use Composer\Util\Filesystem;
|
|||
*/
|
||||
class GzipDownloader extends ArchiveDownloader
|
||||
{
|
||||
/** @var ProcessExecutor */
|
||||
protected $process;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
|
||||
}
|
||||
|
||||
protected function extract(PackageInterface $package, $file, $path)
|
||||
{
|
||||
$filename = pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_FILENAME);
|
||||
|
|
|
@ -27,6 +27,9 @@ use Composer\Util\Filesystem;
|
|||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
use Symfony\Component\Filesystem\Filesystem as SymfonyFilesystem;
|
||||
use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
use Composer\DependencyResolver\Operation\UninstallOperation;
|
||||
|
||||
/**
|
||||
* Download a package from a local path.
|
||||
|
@ -39,15 +42,6 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
const STRATEGY_SYMLINK = 10;
|
||||
const STRATEGY_MIRROR = 20;
|
||||
|
||||
/** @var ProcessExecutor */
|
||||
private $process;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
|
@ -91,11 +85,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
|
||||
if (realpath($path) === $realUrl) {
|
||||
if ($output) {
|
||||
$this->io->writeError(sprintf(
|
||||
' - Installing <info>%s</info> (<comment>%s</comment>): Source already present',
|
||||
$package->getName(),
|
||||
$package->getFullPrettyVersion()
|
||||
));
|
||||
$this->io->writeError(" - " . InstallOperation::format($package).': Source already present');
|
||||
} else {
|
||||
$this->io->writeError('Source already present', false);
|
||||
}
|
||||
|
@ -133,11 +123,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
$this->filesystem->removeDirectory($path);
|
||||
|
||||
if ($output) {
|
||||
$this->io->writeError(sprintf(
|
||||
' - Installing <info>%s</info> (<comment>%s</comment>): ',
|
||||
$package->getName(),
|
||||
$package->getFullPrettyVersion()
|
||||
), false);
|
||||
$this->io->writeError(" - " . InstallOperation::format($package).': ', false);
|
||||
}
|
||||
|
||||
$isFallback = false;
|
||||
|
@ -196,7 +182,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
|
||||
if ($path === $realUrl) {
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>), source is still present in $path");
|
||||
$this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path");
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -209,7 +195,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
*/
|
||||
if (Platform::isWindows() && $this->filesystem->isJunction($path)) {
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Removing junction for <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
$this->io->writeError(" - " . UninstallOperation::format($package).", source is still present in $path");
|
||||
}
|
||||
if (!$this->filesystem->removeJunction($path)) {
|
||||
$this->io->writeError(" <warning>Could not remove junction at " . $path . " - is another process locking it?</warning>");
|
||||
|
|
|
@ -33,15 +33,6 @@ use RarArchive;
|
|||
*/
|
||||
class RarDownloader extends ArchiveDownloader
|
||||
{
|
||||
/** @var ProcessExecutor */
|
||||
protected $process;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
|
||||
}
|
||||
|
||||
protected function extract(PackageInterface $package, $file, $path)
|
||||
{
|
||||
$processError = null;
|
||||
|
|
|
@ -21,6 +21,9 @@ use Composer\Util\ProcessExecutor;
|
|||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use React\Promise\PromiseInterface;
|
||||
use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
use Composer\DependencyResolver\Operation\UninstallOperation;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -120,7 +123,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
|
||||
}
|
||||
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>): ", false);
|
||||
$this->io->writeError(" - " . InstallOperation::format($package).': ', false);
|
||||
|
||||
$urls = $this->prepareUrls($package->getSourceUrls());
|
||||
while ($url = array_shift($urls)) {
|
||||
|
@ -153,23 +156,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
throw new \InvalidArgumentException('Package '.$target->getPrettyName().' is missing reference information');
|
||||
}
|
||||
|
||||
$name = $target->getName();
|
||||
if ($initial->getPrettyVersion() == $target->getPrettyVersion()) {
|
||||
if ($target->getSourceType() === 'svn') {
|
||||
$from = $initial->getSourceReference();
|
||||
$to = $target->getSourceReference();
|
||||
} else {
|
||||
$from = substr($initial->getSourceReference(), 0, 7);
|
||||
$to = substr($target->getSourceReference(), 0, 7);
|
||||
}
|
||||
$name .= ' '.$initial->getPrettyVersion();
|
||||
} else {
|
||||
$from = $initial->getFullPrettyVersion();
|
||||
$to = $target->getFullPrettyVersion();
|
||||
}
|
||||
|
||||
$actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Upgrading' : 'Downgrading';
|
||||
$this->io->writeError(" - " . $actionName . " <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>): ", false);
|
||||
$this->io->writeError(" - " . UpdateOperation::format($initial, $target).': ', false);
|
||||
|
||||
$urls = $this->prepareUrls($target->getSourceUrls());
|
||||
|
||||
|
@ -227,7 +214,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
*/
|
||||
public function remove(PackageInterface $package, $path)
|
||||
{
|
||||
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getPrettyVersion() . "</comment>)");
|
||||
$this->io->writeError(" - " . UninstallOperation::format($package));
|
||||
if (!$this->filesystem->removeDirectory($path)) {
|
||||
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
|
||||
}
|
||||
|
|
|
@ -29,16 +29,6 @@ use Composer\Util\Filesystem;
|
|||
*/
|
||||
class XzDownloader extends ArchiveDownloader
|
||||
{
|
||||
/** @var ProcessExecutor */
|
||||
protected $process;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
|
||||
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
|
||||
}
|
||||
|
||||
protected function extract(PackageInterface $package, $file, $path)
|
||||
{
|
||||
$command = 'tar -xJf ' . ProcessExecutor::escape($file) . ' -C ' . ProcessExecutor::escape($path);
|
||||
|
|
|
@ -34,17 +34,9 @@ class ZipDownloader extends ArchiveDownloader
|
|||
private static $hasZipArchive;
|
||||
private static $isWindows;
|
||||
|
||||
/** @var ProcessExecutor */
|
||||
protected $process;
|
||||
/** @var ZipArchive|null */
|
||||
private $zipArchiveObject;
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, HttpDownloader $downloader, EventDispatcher $eventDispatcher = null, Cache $cache = null, Filesystem $fs = null, ProcessExecutor $process = null)
|
||||
{
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
parent::__construct($io, $config, $downloader, $eventDispatcher, $cache, $fs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -86,9 +78,8 @@ class ZipDownloader extends ArchiveDownloader
|
|||
* @param string $file File to extract
|
||||
* @param string $path Path where to extract file
|
||||
* @param bool $isLastChance If true it is called as a fallback and should throw an exception
|
||||
* @return bool Success status
|
||||
*/
|
||||
protected function extractWithSystemUnzip($file, $path, $isLastChance)
|
||||
private function extractWithSystemUnzip(PackageInterface $package, $file, $path, $isLastChance, $async = false)
|
||||
{
|
||||
if (!self::$hasZipArchive) {
|
||||
// Force Exception throwing if the Other alternative is not available
|
||||
|
@ -98,18 +89,56 @@ class ZipDownloader extends ArchiveDownloader
|
|||
if (!self::$hasSystemUnzip && !$isLastChance) {
|
||||
// This was call as the favorite extract way, but is not available
|
||||
// We switch to the alternative
|
||||
return $this->extractWithZipArchive($file, $path, true);
|
||||
return $this->extractWithZipArchive($package, $file, $path, true);
|
||||
}
|
||||
|
||||
// When called after a ZipArchive failed, perhaps there is some files to overwrite
|
||||
$overwrite = $isLastChance ? '-o' : '';
|
||||
$command = 'unzip -qq '.$overwrite.' '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path);
|
||||
|
||||
if ($async) {
|
||||
$self = $this;
|
||||
$io = $this->io;
|
||||
$tryFallback = function ($processError) use ($isLastChance, $io, $self, $file, $path, $package) {
|
||||
if ($isLastChance) {
|
||||
throw $processError;
|
||||
}
|
||||
|
||||
if (!is_file($file)) {
|
||||
$io->writeError(' <warning>'.$processError->getMessage().'</warning>');
|
||||
$io->writeError(' <warning>This most likely is due to a custom installer plugin not handling the returned Promise from the downloader</warning>');
|
||||
$io->writeError(' <warning>See https://github.com/composer/installers/commit/5006d0c28730ade233a8f42ec31ac68fb1c5c9bb for an example fix</warning>');
|
||||
} else {
|
||||
$io->writeError(' <warning>'.$processError->getMessage().'</warning>');
|
||||
$io->writeError(' The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)');
|
||||
$io->writeError(' Unzip with unzip command failed, falling back to ZipArchive class');
|
||||
}
|
||||
|
||||
return $self->extractWithZipArchive($package, $file, $path, true);
|
||||
};
|
||||
|
||||
try {
|
||||
$promise = $this->process->executeAsync($command);
|
||||
|
||||
return $promise->then(function ($process) use ($tryFallback, $command, $package, $file) {
|
||||
if (!$process->isSuccessful()) {
|
||||
$output = $process->getErrorOutput();
|
||||
$output = str_replace(', '.$file.'.zip or '.$file.'.ZIP', '', $output);
|
||||
|
||||
return $tryFallback(new \RuntimeException('Failed to extract '.$package->getName().': ('.$process->getExitCode().') '.$command."\n\n".$output));
|
||||
}
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
return $tryFallback($e);
|
||||
} catch (\Throwable $e) {
|
||||
return $tryFallback($e);
|
||||
}
|
||||
}
|
||||
|
||||
$processError = null;
|
||||
// When called after a ZipArchive failed, perhaps there is some files to overwrite
|
||||
$overwrite = $isLastChance ? '-o' : '';
|
||||
|
||||
$command = 'unzip -qq '.$overwrite.' '.ProcessExecutor::escape($file).' -d '.ProcessExecutor::escape($path);
|
||||
|
||||
try {
|
||||
if (0 === $exitCode = $this->process->execute($command, $ignoredOutput)) {
|
||||
return true;
|
||||
return \React\Promise\resolve();
|
||||
}
|
||||
|
||||
$processError = new \RuntimeException('Failed to execute ('.$exitCode.') '.$command."\n\n".$this->process->getErrorOutput());
|
||||
|
@ -121,11 +150,11 @@ class ZipDownloader extends ArchiveDownloader
|
|||
throw $processError;
|
||||
}
|
||||
|
||||
$this->io->writeError(' '.$processError->getMessage());
|
||||
$this->io->writeError(' <warning>'.$processError->getMessage().'</warning>');
|
||||
$this->io->writeError(' The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems)');
|
||||
$this->io->writeError(' Unzip with unzip command failed, falling back to ZipArchive class');
|
||||
|
||||
return $this->extractWithZipArchive($file, $path, true);
|
||||
return $this->extractWithZipArchive($package, $file, $path, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,9 +163,11 @@ class ZipDownloader extends ArchiveDownloader
|
|||
* @param string $file File to extract
|
||||
* @param string $path Path where to extract file
|
||||
* @param bool $isLastChance If true it is called as a fallback and should throw an exception
|
||||
* @return bool Success status
|
||||
*
|
||||
* TODO v3 should make this private once we can drop PHP 5.3 support
|
||||
* @protected
|
||||
*/
|
||||
protected function extractWithZipArchive($file, $path, $isLastChance)
|
||||
public function extractWithZipArchive(PackageInterface $package, $file, $path, $isLastChance)
|
||||
{
|
||||
if (!self::$hasSystemUnzip) {
|
||||
// Force Exception throwing if the Other alternative is not available
|
||||
|
@ -146,7 +177,7 @@ class ZipDownloader extends ArchiveDownloader
|
|||
if (!self::$hasZipArchive && !$isLastChance) {
|
||||
// This was call as the favorite extract way, but is not available
|
||||
// We switch to the alternative
|
||||
return $this->extractWithSystemUnzip($file, $path, true);
|
||||
return $this->extractWithSystemUnzip($package, $file, $path, true);
|
||||
}
|
||||
|
||||
$processError = null;
|
||||
|
@ -159,7 +190,7 @@ class ZipDownloader extends ArchiveDownloader
|
|||
if (true === $extractResult) {
|
||||
$zipArchive->close();
|
||||
|
||||
return true;
|
||||
return \React\Promise\resolve();
|
||||
}
|
||||
|
||||
$processError = new \RuntimeException(rtrim("There was an error extracting the ZIP file, it is either corrupted or using an invalid format.\n"));
|
||||
|
@ -170,16 +201,18 @@ class ZipDownloader extends ArchiveDownloader
|
|||
$processError = new \RuntimeException('The archive may contain identical file names with different capitalization (which fails on case insensitive filesystems): '.$e->getMessage(), 0, $e);
|
||||
} catch (\Exception $e) {
|
||||
$processError = $e;
|
||||
} catch (\Throwable $e) {
|
||||
$processError = $e;
|
||||
}
|
||||
|
||||
if ($isLastChance) {
|
||||
throw $processError;
|
||||
}
|
||||
|
||||
$this->io->writeError(' '.$processError->getMessage());
|
||||
$this->io->writeError(' <warning>'.$processError->getMessage().'</warning>');
|
||||
$this->io->writeError(' Unzip with ZipArchive class failed, falling back to unzip command');
|
||||
|
||||
return $this->extractWithSystemUnzip($file, $path, true);
|
||||
return $this->extractWithSystemUnzip($package, $file, $path, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -192,10 +225,10 @@ class ZipDownloader extends ArchiveDownloader
|
|||
{
|
||||
// Each extract calls its alternative if not available or fails
|
||||
if (self::$isWindows) {
|
||||
$this->extractWithZipArchive($file, $path, false);
|
||||
} else {
|
||||
$this->extractWithSystemUnzip($file, $path, false);
|
||||
return $this->extractWithZipArchive($package, $file, $path, false);
|
||||
}
|
||||
|
||||
return $this->extractWithSystemUnzip($package, $file, $path, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -336,7 +336,7 @@ class Factory
|
|||
|
||||
$httpDownloader = self::createHttpDownloader($io, $config);
|
||||
$process = new ProcessExecutor($io);
|
||||
$loop = new Loop($httpDownloader);
|
||||
$loop = new Loop($httpDownloader, $process);
|
||||
$composer->setLoop($loop);
|
||||
|
||||
// initialize event dispatcher
|
||||
|
@ -356,7 +356,7 @@ class Factory
|
|||
// load package
|
||||
$parser = new VersionParser;
|
||||
$guesser = new VersionGuesser($config, $process, $parser);
|
||||
$loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser, $io);
|
||||
$loader = $this->loadRootPackage($rm, $config, $parser, $guesser, $io);
|
||||
$package = $loader->load($localConfig, 'Composer\Package\RootPackage', $cwd);
|
||||
$composer->setPackage($package);
|
||||
|
||||
|
@ -495,11 +495,11 @@ class Factory
|
|||
$dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config, $process, $fs));
|
||||
$dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
$dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
$dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs));
|
||||
$dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
$dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
$dm->setDownloader('xz', new Downloader\XzDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
$dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs));
|
||||
$dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs));
|
||||
$dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
$dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
$dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $httpDownloader, $eventDispatcher, $cache, $fs, $process));
|
||||
|
||||
return $dm;
|
||||
|
@ -567,6 +567,11 @@ class Factory
|
|||
}
|
||||
}
|
||||
|
||||
protected function loadRootPackage(RepositoryManager $rm, Config $config, VersionParser $parser, VersionGuesser $guesser, IOInterface $io)
|
||||
{
|
||||
return new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser, $io);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IOInterface $io IO instance
|
||||
* @param mixed $config either a configuration array or a filename to read from, if null it will read from
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\IO;
|
|||
|
||||
use Composer\Question\StrictConfirmationQuestion;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
@ -253,6 +254,15 @@ class ConsoleIO extends BaseIO
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $max
|
||||
* @return ProgressBar
|
||||
*/
|
||||
public function getProgressBar($max = 0)
|
||||
{
|
||||
return new ProgressBar($this->getErrorOutput(), $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -685,7 +685,7 @@ class Installer
|
|||
}
|
||||
|
||||
if ($this->executeOperations) {
|
||||
$this->installationManager->execute($localRepo, $localRepoTransaction->getOperations(), $this->devMode);
|
||||
$this->installationManager->execute($localRepo, $localRepoTransaction->getOperations(), $this->devMode, $this->runScripts);
|
||||
} else {
|
||||
foreach ($localRepoTransaction->getOperations() as $operation) {
|
||||
// output op, but alias op only in debug verbosity
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Installer;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\IO\ConsoleIO;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Repository\RepositoryInterface;
|
||||
|
@ -49,6 +50,8 @@ class InstallationManager
|
|||
private $io;
|
||||
/** @var EventDispatcher */
|
||||
private $eventDispatcher;
|
||||
/** @var bool */
|
||||
private $outputProgress;
|
||||
|
||||
public function __construct(Loop $loop, IOInterface $io, EventDispatcher $eventDispatcher = null)
|
||||
{
|
||||
|
@ -173,7 +176,7 @@ class InstallationManager
|
|||
* @param RepositoryInterface $repo repository in which to add/remove/update packages
|
||||
* @param OperationInterface[] $operations operations to execute
|
||||
* @param bool $devMode whether the install is being run in dev mode
|
||||
* @param bool $operation whether to dispatch script events
|
||||
* @param bool $runScripts whether to dispatch script events
|
||||
*/
|
||||
public function execute(RepositoryInterface $repo, array $operations, $devMode = true, $runScripts = true)
|
||||
{
|
||||
|
@ -184,6 +187,8 @@ class InstallationManager
|
|||
$runCleanup = function () use (&$cleanupPromises, $loop) {
|
||||
$promises = array();
|
||||
|
||||
$loop->abortJobs();
|
||||
|
||||
foreach ($cleanupPromises as $cleanup) {
|
||||
$promises[] = new \React\Promise\Promise(function ($resolve, $reject) use ($cleanup) {
|
||||
$promise = $cleanup();
|
||||
|
@ -266,69 +271,44 @@ class InstallationManager
|
|||
|
||||
// execute all downloads first
|
||||
if (!empty($promises)) {
|
||||
$this->loop->wait($promises);
|
||||
$progress = null;
|
||||
if ($this->outputProgress && $this->io instanceof ConsoleIO && !$this->io->isDebug() && count($promises) > 1) {
|
||||
$progress = $this->io->getProgressBar();
|
||||
}
|
||||
$this->loop->wait($promises, $progress);
|
||||
if ($progress) {
|
||||
$progress->clear();
|
||||
}
|
||||
}
|
||||
|
||||
// execute operations in batches to make sure every plugin is installed in the
|
||||
// right order and activated before the packages depending on it are installed
|
||||
$batches = array();
|
||||
$batch = array();
|
||||
foreach ($operations as $index => $operation) {
|
||||
$opType = $operation->getOperationType();
|
||||
if (in_array($operation->getOperationType(), array('update', 'install'), true)) {
|
||||
$package = $operation->getOperationType() === 'update' ? $operation->getTargetPackage() : $operation->getPackage();
|
||||
if ($package->getType() === 'composer-plugin' || $package->getType() === 'composer-installer') {
|
||||
if ($batch) {
|
||||
$batches[] = $batch;
|
||||
}
|
||||
unset($operations[$index]);
|
||||
$batches[] = array($index => $operation);
|
||||
$batch = array();
|
||||
|
||||
// ignoring alias ops as they don't need to execute anything
|
||||
if (!in_array($opType, array('update', 'install', 'uninstall'))) {
|
||||
// output alias ops in debug verbosity as they have no output otherwise
|
||||
if ($this->io->isDebug()) {
|
||||
$this->io->writeError(' - ' . $operation->show(false));
|
||||
continue;
|
||||
}
|
||||
$this->$opType($repo, $operation);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($opType === 'update') {
|
||||
$package = $operation->getTargetPackage();
|
||||
$initialPackage = $operation->getInitialPackage();
|
||||
} else {
|
||||
$package = $operation->getPackage();
|
||||
$initialPackage = null;
|
||||
}
|
||||
$installer = $this->getInstaller($package->getType());
|
||||
|
||||
$event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($opType);
|
||||
if (defined($event) && $runScripts && $this->eventDispatcher) {
|
||||
$this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
|
||||
}
|
||||
|
||||
$dispatcher = $this->eventDispatcher;
|
||||
$installManager = $this;
|
||||
$loop = $this->loop;
|
||||
$io = $this->io;
|
||||
|
||||
$promise = $installer->prepare($opType, $package, $initialPackage);
|
||||
if (!$promise instanceof PromiseInterface) {
|
||||
$promise = \React\Promise\resolve();
|
||||
}
|
||||
|
||||
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
|
||||
return $installManager->$opType($repo, $operation);
|
||||
})->then($cleanupPromises[$index])
|
||||
->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) {
|
||||
$repo->write($devMode, $installManager);
|
||||
|
||||
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($opType);
|
||||
if (defined($event) && $runScripts && $dispatcher) {
|
||||
$dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
|
||||
}
|
||||
}, function ($e) use ($opType, $package, $io) {
|
||||
$io->writeError(' <error>' . ucfirst($opType) .' of '.$package->getPrettyName().' failed</error>');
|
||||
|
||||
throw $e;
|
||||
});
|
||||
|
||||
$promises[] = $promise;
|
||||
unset($operations[$index]);
|
||||
$batch[$index] = $operation;
|
||||
}
|
||||
|
||||
// execute all prepare => installs/updates/removes => cleanup steps
|
||||
if (!empty($promises)) {
|
||||
$this->loop->wait($promises);
|
||||
if ($batch) {
|
||||
$batches[] = $batch;
|
||||
}
|
||||
|
||||
foreach ($batches as $batch) {
|
||||
$this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$runCleanup();
|
||||
|
@ -356,6 +336,77 @@ class InstallationManager
|
|||
$repo->write($devMode, $this);
|
||||
}
|
||||
|
||||
private function executeBatch(RepositoryInterface $repo, array $operations, array $cleanupPromises, $devMode, $runScripts)
|
||||
{
|
||||
foreach ($operations as $index => $operation) {
|
||||
$opType = $operation->getOperationType();
|
||||
|
||||
// ignoring alias ops as they don't need to execute anything
|
||||
if (!in_array($opType, array('update', 'install', 'uninstall'))) {
|
||||
// output alias ops in debug verbosity as they have no output otherwise
|
||||
if ($this->io->isDebug()) {
|
||||
$this->io->writeError(' - ' . $operation->show(false));
|
||||
}
|
||||
$this->$opType($repo, $operation);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($opType === 'update') {
|
||||
$package = $operation->getTargetPackage();
|
||||
$initialPackage = $operation->getInitialPackage();
|
||||
} else {
|
||||
$package = $operation->getPackage();
|
||||
$initialPackage = null;
|
||||
}
|
||||
$installer = $this->getInstaller($package->getType());
|
||||
|
||||
$event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($opType);
|
||||
if (defined($event) && $runScripts && $this->eventDispatcher) {
|
||||
$this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
|
||||
}
|
||||
|
||||
$dispatcher = $this->eventDispatcher;
|
||||
$installManager = $this;
|
||||
$io = $this->io;
|
||||
|
||||
$promise = $installer->prepare($opType, $package, $initialPackage);
|
||||
if (!$promise instanceof PromiseInterface) {
|
||||
$promise = \React\Promise\resolve();
|
||||
}
|
||||
|
||||
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
|
||||
return $installManager->$opType($repo, $operation);
|
||||
})->then($cleanupPromises[$index])
|
||||
->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) {
|
||||
$repo->write($devMode, $installManager);
|
||||
|
||||
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($opType);
|
||||
if (defined($event) && $runScripts && $dispatcher) {
|
||||
$dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
|
||||
}
|
||||
}, function ($e) use ($opType, $package, $io) {
|
||||
$io->writeError(' <error>' . ucfirst($opType) .' of '.$package->getPrettyName().' failed</error>');
|
||||
|
||||
throw $e;
|
||||
});
|
||||
|
||||
$promises[] = $promise;
|
||||
}
|
||||
|
||||
// execute all prepare => installs/updates/removes => cleanup steps
|
||||
if (!empty($promises)) {
|
||||
$progress = null;
|
||||
if ($this->outputProgress && $this->io instanceof ConsoleIO && !$this->io->isDebug() && count($promises) > 1) {
|
||||
$progress = $this->io->getProgressBar();
|
||||
}
|
||||
$this->loop->wait($promises, $progress);
|
||||
if ($progress) {
|
||||
$progress->clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes install operation.
|
||||
*
|
||||
|
@ -454,6 +505,11 @@ class InstallationManager
|
|||
return $installer->getInstallPath($package);
|
||||
}
|
||||
|
||||
public function setOutputProgress($outputProgress)
|
||||
{
|
||||
$this->outputProgress = $outputProgress;
|
||||
}
|
||||
|
||||
public function notifyInstalls(IOInterface $io)
|
||||
{
|
||||
foreach ($this->notifiablePackages as $repoUrl => $packages) {
|
||||
|
|
|
@ -16,6 +16,9 @@ use Composer\Repository\InstalledRepositoryInterface;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\DependencyResolver\Operation\UpdateOperation;
|
||||
use Composer\DependencyResolver\Operation\InstallOperation;
|
||||
use Composer\DependencyResolver\Operation\UninstallOperation;
|
||||
|
||||
/**
|
||||
* Metapackage installation manager.
|
||||
|
@ -76,7 +79,7 @@ class MetapackageInstaller implements InstallerInterface
|
|||
*/
|
||||
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||
{
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
$this->io->writeError(" - " . InstallOperation::format($package));
|
||||
|
||||
$repo->addPackage(clone $package);
|
||||
}
|
||||
|
@ -90,11 +93,7 @@ class MetapackageInstaller implements InstallerInterface
|
|||
throw new \InvalidArgumentException('Package is not installed: '.$initial);
|
||||
}
|
||||
|
||||
$name = $target->getName();
|
||||
$from = $initial->getFullPrettyVersion();
|
||||
$to = $target->getFullPrettyVersion();
|
||||
$actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Upgrading' : 'Downgrading';
|
||||
$this->io->writeError(" - " . $actionName . " <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
|
||||
$this->io->writeError(" - " . UpdateOperation::format($initial, $target));
|
||||
|
||||
$repo->removePackage($initial);
|
||||
$repo->addPackage(clone $target);
|
||||
|
@ -109,7 +108,7 @@ class MetapackageInstaller implements InstallerInterface
|
|||
throw new \InvalidArgumentException('Package is not installed: '.$package);
|
||||
}
|
||||
|
||||
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
$this->io->writeError(" - " . UninstallOperation::format($package));
|
||||
|
||||
$repo->removePackage($package);
|
||||
}
|
||||
|
|
|
@ -414,6 +414,11 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
|
|||
return $this->aliasOf->getArchiveExcludes();
|
||||
}
|
||||
|
||||
public function isDefaultBranch()
|
||||
{
|
||||
return $this->aliasOf->isDefaultBranch();
|
||||
}
|
||||
|
||||
public function isAbandoned()
|
||||
{
|
||||
return $this->aliasOf->isAbandoned();
|
||||
|
|
|
@ -233,7 +233,7 @@ abstract class BasePackage implements PackageInterface
|
|||
}
|
||||
|
||||
// if source reference is a sha1 hash -- truncate
|
||||
if ($truncate && \strlen($reference) === 40) {
|
||||
if ($truncate && \strlen($reference) === 40 && $this->getSourceType() !== 'svn') {
|
||||
return $this->getPrettyVersion() . ' ' . substr($reference, 0, 7);
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,10 @@ class ArrayDumper
|
|||
$data['time'] = $package->getReleaseDate()->format(DATE_RFC3339);
|
||||
}
|
||||
|
||||
if ($package->isDefaultBranch()) {
|
||||
$data['default-branch'] = true;
|
||||
}
|
||||
|
||||
$data = $this->dumpValues($package, $keys, $data);
|
||||
|
||||
if ($package instanceof CompletePackageInterface) {
|
||||
|
|
|
@ -124,6 +124,10 @@ class ArrayLoader implements LoaderInterface
|
|||
$package->setInstallationSource($config['installation-source']);
|
||||
}
|
||||
|
||||
if (isset($config['default-branch']) && $config['default-branch'] === true) {
|
||||
$package->setIsDefaultBranch(true);
|
||||
}
|
||||
|
||||
if (isset($config['source'])) {
|
||||
if (!isset($config['source']['type']) || !isset($config['source']['url']) || !isset($config['source']['reference'])) {
|
||||
throw new \UnexpectedValueException(sprintf(
|
||||
|
@ -364,7 +368,7 @@ class ArrayLoader implements LoaderInterface
|
|||
}
|
||||
}
|
||||
|
||||
if (\in_array($config['version'], array('dev-master', 'dev-default', 'dev-trunk'), true)) {
|
||||
if (isset($config['default-branch']) && $config['default-branch'] === true) {
|
||||
return VersionParser::DEV_MASTER_ALIAS;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,11 @@ class RootPackageLoader extends ArrayLoader
|
|||
}
|
||||
}
|
||||
|
||||
$defaultBranch = $this->versionGuesser->getDefaultBranchName($cwd ?: getcwd());
|
||||
if ($defaultBranch && $config['version'] === 'dev-'.$defaultBranch) {
|
||||
$config['default-branch'] = true;
|
||||
}
|
||||
|
||||
$realPackage = $package = parent::load($config, $class);
|
||||
if ($realPackage instanceof AliasPackage) {
|
||||
$realPackage = $package->getAliasOf();
|
||||
|
|
|
@ -432,7 +432,7 @@ class Locker
|
|||
case 'git':
|
||||
GitUtil::cleanEnv();
|
||||
|
||||
if (0 === $this->process->execute('git log -n1 --pretty=%ct '.ProcessExecutor::escape($sourceRef), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) {
|
||||
if (0 === $this->process->execute('git log -n1 --pretty=%ct '.ProcessExecutor::escape($sourceRef).GitUtil::getNoShowSignatureFlag($this->process), $output, $path) && preg_match('{^\s*\d+\s*$}', $output)) {
|
||||
$datetime = new \DateTime('@'.trim($output), new \DateTimeZone('UTC'));
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -59,6 +59,7 @@ class Package extends BasePackage
|
|||
protected $includePaths = array();
|
||||
protected $archiveName;
|
||||
protected $archiveExcludes = array();
|
||||
protected $isDefaultBranch = false;
|
||||
|
||||
/**
|
||||
* Creates a new in memory package.
|
||||
|
@ -588,6 +589,22 @@ class Package extends BasePackage
|
|||
return $this->archiveExcludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $defaultBranch
|
||||
*/
|
||||
public function setIsDefaultBranch($defaultBranch)
|
||||
{
|
||||
$this->isDefaultBranch = $defaultBranch;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isDefaultBranch()
|
||||
{
|
||||
return $this->isDefaultBranch;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -371,6 +371,11 @@ interface PackageInterface
|
|||
*/
|
||||
public function getArchiveExcludes();
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isDefaultBranch();
|
||||
|
||||
/**
|
||||
* Returns a list of options to download package dist files
|
||||
*
|
||||
|
|
|
@ -20,6 +20,7 @@ use Composer\Util\Git as GitUtil;
|
|||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\Svn as SvnUtil;
|
||||
use Composer\Util\Platform;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
|
||||
|
||||
|
@ -110,6 +111,43 @@ class VersionGuesser
|
|||
return $versionData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to find name of default branch from VCS info
|
||||
*
|
||||
* @param string $path Path to guess into
|
||||
*/
|
||||
public function getDefaultBranchName($path)
|
||||
{
|
||||
if (version_compare(GitUtil::getVersion($this->process), '2.3.0-rc0', '>=')) {
|
||||
GitUtil::cleanEnv();
|
||||
$oldVal = getenv('GIT_SSH_COMMAND');
|
||||
putenv("GIT_SSH_COMMAND=ssh".(Platform::isWindows() ? '.exe' : '')." -o StrictHostKeyChecking=yes");
|
||||
$hasGitRemote = 0 === $this->process->execute('git remote show origin', $output, $path);
|
||||
if ($oldVal) {
|
||||
putenv("GIT_SSH_COMMAND=$oldVal");
|
||||
} else {
|
||||
putenv("GIT_SSH_COMMAND");
|
||||
}
|
||||
if ($hasGitRemote && preg_match('{^ HEAD branch: (.+)$}m', $output, $match)) {
|
||||
return trim($match[1]);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_dir($path.'/.git')) {
|
||||
return 'master';
|
||||
}
|
||||
|
||||
if (is_dir($path.'/.hg')) {
|
||||
return 'default';
|
||||
}
|
||||
|
||||
if (is_dir($path.'/.svn')) {
|
||||
return 'trunk';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function guessGitVersion(array $packageConfig, $path)
|
||||
{
|
||||
GitUtil::cleanEnv();
|
||||
|
@ -154,6 +192,7 @@ class VersionGuesser
|
|||
if ($isFeatureBranch) {
|
||||
$featureVersion = $version;
|
||||
$featurePrettyVersion = $prettyVersion;
|
||||
|
||||
// try to find the best (nearest) version branch to assume this feature's version
|
||||
$result = $this->guessFeatureVersion($packageConfig, $version, $branches, 'git rev-list %candidate%..%branch%', $path);
|
||||
$version = $result['version'];
|
||||
|
@ -172,7 +211,7 @@ class VersionGuesser
|
|||
}
|
||||
|
||||
if (!$commit) {
|
||||
$command = 'git log --pretty="%H" -n1 HEAD';
|
||||
$command = 'git log --pretty="%H" -n1 HEAD'.GitUtil::getNoShowSignatureFlag($this->process);
|
||||
if (0 === $this->process->execute($command, $output, $path)) {
|
||||
$commit = trim($output) ?: null;
|
||||
}
|
||||
|
@ -248,14 +287,16 @@ class VersionGuesser
|
|||
$nonFeatureBranches = implode('|', $packageConfig['non-feature-branches']);
|
||||
}
|
||||
|
||||
foreach ($branches as $candidate) {
|
||||
// return directly, if branch is configured to be non-feature branch
|
||||
if ($candidate === $branch && preg_match('{^(' . $nonFeatureBranches . ')$}', $candidate)) {
|
||||
break;
|
||||
}
|
||||
// return directly, if branch is configured to be non-feature branch
|
||||
if (preg_match('{^(' . $nonFeatureBranches . ')$}', $branch)) {
|
||||
return array('version' => $version, 'pretty_version' => $prettyVersion);
|
||||
}
|
||||
|
||||
$defaultBranch = $this->getDefaultBranchName($path);
|
||||
|
||||
foreach ($branches as $candidate) {
|
||||
// do not compare against itself or other feature branches
|
||||
if ($candidate === $branch || !preg_match('{^(' . $nonFeatureBranches . '|master|trunk|default|develop|\d+\..+)$}', $candidate, $match)) {
|
||||
if ($candidate === $branch || !preg_match('{^(' . $nonFeatureBranches . ($defaultBranch ? '|'.preg_quote($defaultBranch) : '').'|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}', $candidate, $match)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,18 +32,32 @@ class PreFileDownloadEvent extends Event
|
|||
*/
|
||||
private $processedUrl;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
private $context;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $name The event name
|
||||
* @param HttpDownloader $httpDownloader
|
||||
* @param string $processedUrl
|
||||
* @param string $name The event name
|
||||
* @param HttpDownloader $httpDownloader
|
||||
* @param string $processedUrl
|
||||
* @param string $type
|
||||
* @param mixed $context
|
||||
*/
|
||||
public function __construct($name, HttpDownloader $httpDownloader, $processedUrl)
|
||||
public function __construct($name, HttpDownloader $httpDownloader, $processedUrl, $type, $context = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
$this->httpDownloader = $httpDownloader;
|
||||
$this->processedUrl = $processedUrl;
|
||||
$this->type = $type;
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +69,7 @@ class PreFileDownloadEvent extends Event
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves the processed URL this remote filesystem will be used for
|
||||
* Retrieves the processed URL that will be downloaded.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
@ -63,4 +77,36 @@ class PreFileDownloadEvent extends Event
|
|||
{
|
||||
return $this->processedUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the processed URL that will be downloaded.
|
||||
*
|
||||
* @param string $processedUrl New processed URL
|
||||
*/
|
||||
public function setProcessedUrl($processedUrl)
|
||||
{
|
||||
$this->processedUrl = $processedUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this download (package, metadata).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context of this download, if any.
|
||||
*
|
||||
* If this download is of type package, the package object is returned.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ use Composer\Config;
|
|||
use Composer\Composer;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Semver\CompilingMatcher;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Util\Loop;
|
||||
use Composer\Plugin\PluginEvents;
|
||||
|
@ -764,7 +765,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
continue;
|
||||
}
|
||||
|
||||
if ($constraint && !$constraint->matches(new Constraint('==', $version))) {
|
||||
if ($constraint && !CompilingMatcher::match($constraint, Constraint::OP_EQ, $version)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1013,8 +1014,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
while ($retries--) {
|
||||
try {
|
||||
if ($this->eventDispatcher) {
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename);
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename, 'metadata');
|
||||
$this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
$filename = $preFileDownloadEvent->getProcessedUrl();
|
||||
}
|
||||
|
||||
$response = $this->httpDownloader->get($filename, $this->options);
|
||||
|
@ -1099,8 +1101,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
while ($retries--) {
|
||||
try {
|
||||
if ($this->eventDispatcher) {
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename);
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename, 'metadata');
|
||||
$this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
$filename = $preFileDownloadEvent->getProcessedUrl();
|
||||
}
|
||||
|
||||
$options = $this->options;
|
||||
|
@ -1165,8 +1168,9 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
|||
|
||||
$httpDownloader = $this->httpDownloader;
|
||||
if ($this->eventDispatcher) {
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename);
|
||||
$preFileDownloadEvent = new PreFileDownloadEvent(PluginEvents::PRE_FILE_DOWNLOAD, $this->httpDownloader, $filename, 'metadata');
|
||||
$this->eventDispatcher->dispatch($preFileDownloadEvent->getName(), $preFileDownloadEvent);
|
||||
$filename = $preFileDownloadEvent->getProcessedUrl();
|
||||
}
|
||||
|
||||
$options = $lastModifiedTime ? array('http' => array('header' => array('If-Modified-Since: '.$lastModifiedTime))) : array();
|
||||
|
|
|
@ -22,6 +22,7 @@ use Composer\Util\Platform;
|
|||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Url;
|
||||
use Composer\Util\Git as GitUtil;
|
||||
|
||||
/**
|
||||
* This repository allows installing local packages that are not necessarily under their own VCS.
|
||||
|
@ -182,7 +183,7 @@ class PathRepository extends ArrayRepository implements ConfigurableRepositoryIn
|
|||
}
|
||||
|
||||
$output = '';
|
||||
if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H', $output, $path)) {
|
||||
if (is_dir($path . DIRECTORY_SEPARATOR . '.git') && 0 === $this->process->execute('git log -n1 --pretty=%H'.GitUtil::getNoShowSignatureFlag($this->process), $output, $path)) {
|
||||
$package['dist']['reference'] = trim($output);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ use Composer\EventDispatcher\EventDispatcher;
|
|||
use Composer\IO\IOInterface;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Repository\CompositeRepository;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
|
@ -44,7 +45,7 @@ class RepositorySet
|
|||
|
||||
/**
|
||||
* @var array[]
|
||||
* @psalm-var list<array{package: string, version: string, alias: string, alias_normalized: string}>
|
||||
* @psalm-var array<string, array<string, array{alias: string, alias_normalized: string}>>
|
||||
*/
|
||||
private $rootAliases;
|
||||
|
||||
|
@ -91,7 +92,7 @@ class RepositorySet
|
|||
*/
|
||||
public function __construct($minimumStability = 'stable', array $stabilityFlags = array(), array $rootAliases = array(), array $rootReferences = array(), array $rootRequires = array())
|
||||
{
|
||||
$this->rootAliases = $rootAliases;
|
||||
$this->rootAliases = self::getRootAliasesPerPackage($rootAliases);
|
||||
$this->rootReferences = $rootReferences;
|
||||
|
||||
$this->acceptableStabilities = array();
|
||||
|
@ -249,8 +250,22 @@ class RepositorySet
|
|||
|
||||
$packages = array();
|
||||
foreach ($this->repositories as $repository) {
|
||||
$packages = array_merge($packages, $repository->getPackages());
|
||||
foreach ($repository->getPackages() as $package) {
|
||||
$packages[] = $package;
|
||||
|
||||
if (isset($this->rootAliases[$package->getName()][$package->getVersion()])) {
|
||||
$alias = $this->rootAliases[$package->getName()][$package->getVersion()];
|
||||
while ($package instanceof AliasPackage) {
|
||||
$package = $package->getAliasOf();
|
||||
}
|
||||
$aliasPackage = new AliasPackage($package, $alias['alias_normalized'], $alias['alias']);
|
||||
$aliasPackage->setRootPackageAlias(true);
|
||||
$packages[] = $aliasPackage;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return new Pool($packages);
|
||||
}
|
||||
|
||||
|
@ -270,4 +285,18 @@ class RepositorySet
|
|||
|
||||
return $this->createPool($request, new NullIO());
|
||||
}
|
||||
|
||||
private static function getRootAliasesPerPackage(array $aliases)
|
||||
{
|
||||
$normalizedAliases = array();
|
||||
|
||||
foreach ($aliases as $alias) {
|
||||
$normalizedAliases[$alias['package']][$alias['version']] = array(
|
||||
'alias' => $alias['alias'],
|
||||
'alias_normalized' => $alias['alias_normalized'],
|
||||
);
|
||||
}
|
||||
|
||||
return $normalizedAliases;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,14 +338,12 @@ class GitHubDriver extends VcsDriver
|
|||
$this->branches = array();
|
||||
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100';
|
||||
|
||||
$branchBlacklist = array('gh-pages');
|
||||
|
||||
do {
|
||||
$response = $this->getContents($resource);
|
||||
$branchData = $response->decodeJson();
|
||||
foreach ($branchData as $branch) {
|
||||
$name = substr($branch['ref'], 11);
|
||||
if (!in_array($name, $branchBlacklist)) {
|
||||
if ($name !== 'gh-pages') {
|
||||
$this->branches[$name] = $branch['object']['sha'];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,8 +167,10 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
|||
$this->loader = new ArrayLoader($this->versionParser);
|
||||
}
|
||||
|
||||
$hasRootIdentifierComposerJson = false;
|
||||
try {
|
||||
if ($driver->hasComposerFile($driver->getRootIdentifier())) {
|
||||
$hasRootIdentifierComposerJson = $driver->hasComposerFile($driver->getRootIdentifier());
|
||||
if ($hasRootIdentifierComposerJson) {
|
||||
$data = $driver->getComposerInformation($driver->getRootIdentifier());
|
||||
$this->packageName = !empty($data['name']) ? $data['name'] : null;
|
||||
}
|
||||
|
@ -229,10 +231,17 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
|||
$data['version'] = preg_replace('{[.-]?dev$}i', '', $data['version']);
|
||||
$data['version_normalized'] = preg_replace('{(^dev-|[.-]?dev$)}i', '', $data['version_normalized']);
|
||||
|
||||
// make sure tag do not contain the default-branch marker
|
||||
unset($data['default-branch']);
|
||||
|
||||
// broken package, version doesn't match tag
|
||||
if ($data['version_normalized'] !== $parsedTag) {
|
||||
if ($isVeryVerbose) {
|
||||
$this->io->writeError('<warning>Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json</warning>');
|
||||
if (preg_match('{(^dev-|[.-]?dev$)}i', $parsedTag)) {
|
||||
$this->io->writeError('<warning>Skipped tag '.$tag.', invalid tag name, tags can not use dev prefixes or suffixes</warning>');
|
||||
} else {
|
||||
$this->io->writeError('<warning>Skipped tag '.$tag.', tag ('.$parsedTag.') does not match version ('.$data['version_normalized'].') in composer.json</warning>');
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -269,6 +278,11 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
|||
}
|
||||
|
||||
$branches = $driver->getBranches();
|
||||
// make sure the root identifier branch gets loaded first
|
||||
if ($hasRootIdentifierComposerJson && isset($branches[$driver->getRootIdentifier()])) {
|
||||
$branches = array($driver->getRootIdentifier() => $branches[$driver->getRootIdentifier()]) + $branches;
|
||||
}
|
||||
|
||||
foreach ($branches as $branch => $identifier) {
|
||||
$msg = 'Reading composer.json of <info>' . ($this->packageName ?: $this->url) . '</info> (<comment>' . $branch . '</comment>)';
|
||||
if ($isVeryVerbose) {
|
||||
|
@ -299,7 +313,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
|||
$version = $prefix . preg_replace('{(\.9{7})+}', '.x', $parsedBranch);
|
||||
}
|
||||
|
||||
$cachedPackage = $this->getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose);
|
||||
$cachedPackage = $this->getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose, $driver->getRootIdentifier() === $branch);
|
||||
if ($cachedPackage) {
|
||||
$this->addPackage($cachedPackage);
|
||||
|
||||
|
@ -323,6 +337,11 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
|||
$data['version'] = $version;
|
||||
$data['version_normalized'] = $parsedBranch;
|
||||
|
||||
unset($data['default-branch']);
|
||||
if ($driver->getRootIdentifier() === $branch) {
|
||||
$data['default-branch'] = true;
|
||||
}
|
||||
|
||||
if ($isVeryVerbose) {
|
||||
$this->io->writeError('Importing branch '.$branch.' ('.$data['version'].')');
|
||||
}
|
||||
|
@ -404,7 +423,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
|||
return false;
|
||||
}
|
||||
|
||||
private function getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose)
|
||||
private function getCachedPackageVersion($version, $identifier, $isVerbose, $isVeryVerbose, $isDefaultBranch = false)
|
||||
{
|
||||
if (!$this->versionCache) {
|
||||
return;
|
||||
|
@ -427,6 +446,11 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt
|
|||
$this->io->overwriteError($msg, false);
|
||||
}
|
||||
|
||||
unset($cachedPackage['default-branch']);
|
||||
if ($isDefaultBranch) {
|
||||
$cachedPackage['default-branch'] = true;
|
||||
}
|
||||
|
||||
if ($existingPackage = $this->findPackage($cachedPackage['name'], new Constraint('=', $cachedPackage['version_normalized']))) {
|
||||
if ($isVeryVerbose) {
|
||||
$this->io->writeError('<warning>Skipped cached version '.$version.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$cachedPackage['version_normalized'].' internally</warning>');
|
||||
|
|
|
@ -63,7 +63,11 @@ class Versions
|
|||
|
||||
public function getLatest($channel = null)
|
||||
{
|
||||
$protocol = extension_loaded('openssl') ? 'https' : 'http';
|
||||
if ($this->config->get('disable-tls') === true) {
|
||||
$protocol = 'http';
|
||||
} else {
|
||||
$protocol = 'https';
|
||||
}
|
||||
$versions = $this->httpDownloader->get($protocol . '://getcomposer.org/versions')->decodeJson();
|
||||
|
||||
foreach ($versions[$channel ?: $this->getChannel()] as $version) {
|
||||
|
|
|
@ -20,7 +20,7 @@ use Composer\IO\IOInterface;
|
|||
*/
|
||||
class Git
|
||||
{
|
||||
private static $version;
|
||||
private static $version = false;
|
||||
|
||||
/** @var IOInterface */
|
||||
protected $io;
|
||||
|
@ -297,6 +297,16 @@ class Git
|
|||
return false;
|
||||
}
|
||||
|
||||
public static function getNoShowSignatureFlag(ProcessExecutor $process)
|
||||
{
|
||||
$gitVersion = self::getVersion($process);
|
||||
if ($gitVersion && version_compare($gitVersion, '2.10.0-rc0', '>=')) {
|
||||
return ' --no-show-signature';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
private function checkRefIsInMirror($url, $dir, $ref)
|
||||
{
|
||||
if (is_dir($dir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $dir) && trim($output) === '.') {
|
||||
|
@ -393,16 +403,18 @@ class Git
|
|||
*
|
||||
* @return string|null The git version number.
|
||||
*/
|
||||
public function getVersion()
|
||||
public static function getVersion(ProcessExecutor $process)
|
||||
{
|
||||
if (isset(self::$version)) {
|
||||
return self::$version;
|
||||
}
|
||||
if (0 !== $this->process->execute('git --version', $output)) {
|
||||
return;
|
||||
}
|
||||
if (preg_match('/^git version (\d+(?:\.\d+)+)/m', $output, $matches)) {
|
||||
return self::$version = $matches[1];
|
||||
if (false === self::$version) {
|
||||
self::$version = null;
|
||||
if (!$process) {
|
||||
$process = new ProcessExecutor;
|
||||
}
|
||||
if (0 === $process->execute('git --version', $output) && preg_match('/^git version (\d+(?:\.\d+)+)/m', $output, $matches)) {
|
||||
self::$version = $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return self::$version;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ use Composer\Util\HttpDownloader;
|
|||
use React\Promise\Promise;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
|
@ -90,6 +91,9 @@ class CurlDownloader
|
|||
$this->authHelper = new AuthHelper($io, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int internal job id
|
||||
*/
|
||||
public function download($resolve, $reject, $origin, $url, $options, $copyTo = null)
|
||||
{
|
||||
$attributes = array();
|
||||
|
@ -101,6 +105,9 @@ class CurlDownloader
|
|||
return $this->initDownload($resolve, $reject, $origin, $url, $options, $copyTo, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int internal job id
|
||||
*/
|
||||
private function initDownload($resolve, $reject, $origin, $url, $options, $copyTo = null, array $attributes = array())
|
||||
{
|
||||
$attributes = array_merge(array(
|
||||
|
@ -199,8 +206,29 @@ class CurlDownloader
|
|||
}
|
||||
|
||||
$this->checkCurlResult(curl_multi_add_handle($this->multiHandle, $curlHandle));
|
||||
// TODO progress
|
||||
// TODO progress
|
||||
//$params['notification'](STREAM_NOTIFY_RESOLVE, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0, false);
|
||||
|
||||
return (int) $curlHandle;
|
||||
}
|
||||
|
||||
public function abortRequest($id)
|
||||
{
|
||||
if (isset($this->jobs[$id]) && isset($this->jobs[$id]['handle'])) {
|
||||
$job = $this->jobs[$id];
|
||||
curl_multi_remove_handle($this->multiHandle, $job['handle']);
|
||||
curl_close($job['handle']);
|
||||
if (is_resource($job['headerHandle'])) {
|
||||
fclose($job['headerHandle']);
|
||||
}
|
||||
if (is_resource($job['bodyHandle'])) {
|
||||
fclose($job['bodyHandle']);
|
||||
}
|
||||
if ($job['filename']) {
|
||||
@unlink($job['filename'].'~');
|
||||
}
|
||||
unset($this->jobs[$id]);
|
||||
}
|
||||
}
|
||||
|
||||
public function tick()
|
||||
|
@ -235,7 +263,7 @@ class CurlDownloader
|
|||
$statusCode = null;
|
||||
$response = null;
|
||||
try {
|
||||
// TODO progress
|
||||
// TODO progress
|
||||
//$this->onProgress($curlHandle, $job['callback'], $progress, $job['progress']);
|
||||
if (CURLE_OK !== $errno || $error) {
|
||||
throw new TransportException($error);
|
||||
|
@ -285,8 +313,6 @@ class CurlDownloader
|
|||
// fail 4xx and 5xx responses and capture the response
|
||||
if ($statusCode >= 400 && $statusCode <= 599) {
|
||||
throw $this->failResponse($job, $response, $response->getStatusMessage());
|
||||
// TODO progress
|
||||
// $this->io->overwriteError("Downloading (<error>failed</error>)", false);
|
||||
}
|
||||
|
||||
if ($job['attributes']['storeAuth']) {
|
||||
|
|
|
@ -31,6 +31,7 @@ class HttpDownloader
|
|||
const STATUS_STARTED = 2;
|
||||
const STATUS_COMPLETED = 3;
|
||||
const STATUS_FAILED = 4;
|
||||
const STATUS_ABORTED = 5;
|
||||
|
||||
private $io;
|
||||
private $config;
|
||||
|
@ -44,6 +45,7 @@ class HttpDownloader
|
|||
private $rfs;
|
||||
private $idGen = 0;
|
||||
private $disabled;
|
||||
private $allowAsync = false;
|
||||
|
||||
/**
|
||||
* @param IOInterface $io The IO instance
|
||||
|
@ -139,6 +141,10 @@ class HttpDownloader
|
|||
'origin' => Url::getOrigin($this->config, $request['url']),
|
||||
);
|
||||
|
||||
if (!$sync && !$this->allowAsync) {
|
||||
throw new \LogicException('You must use the HttpDownloader instance which is part of a Composer\Loop instance to be able to run async http requests');
|
||||
}
|
||||
|
||||
// capture username/password from URL if there is one
|
||||
if (preg_match('{^https?://([^:/]+):([^@/]+)@([^/]+)}i', $request['url'], $match)) {
|
||||
$this->io->setAuthentication($job['origin'], rawurldecode($match[1]), rawurldecode($match[2]));
|
||||
|
@ -179,8 +185,20 @@ class HttpDownloader
|
|||
|
||||
$downloader = $this;
|
||||
$io = $this->io;
|
||||
$curl = $this->curl;
|
||||
|
||||
$canceler = function () {};
|
||||
$canceler = function () use (&$job, $curl) {
|
||||
if ($job['status'] === self::STATUS_QUEUED) {
|
||||
$job['status'] = self::STATUS_ABORTED;
|
||||
}
|
||||
if ($job['status'] !== self::STATUS_STARTED) {
|
||||
return;
|
||||
}
|
||||
$job['status'] = self::STATUS_ABORTED;
|
||||
if (isset($job['curl_id'])) {
|
||||
$curl->abortRequest($job['curl_id']);
|
||||
}
|
||||
};
|
||||
|
||||
$promise = new Promise($resolver, $canceler);
|
||||
$promise->then(function ($response) use (&$job, $downloader) {
|
||||
|
@ -189,7 +207,6 @@ class HttpDownloader
|
|||
|
||||
// TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped
|
||||
$downloader->markJobDone();
|
||||
$downloader->scheduleNextJob();
|
||||
|
||||
return $response;
|
||||
}, function ($e) use (&$job, $downloader) {
|
||||
|
@ -197,7 +214,6 @@ class HttpDownloader
|
|||
$job['exception'] = $e;
|
||||
|
||||
$downloader->markJobDone();
|
||||
$downloader->scheduleNextJob();
|
||||
|
||||
throw $e;
|
||||
});
|
||||
|
@ -239,9 +255,9 @@ class HttpDownloader
|
|||
}
|
||||
|
||||
if ($job['request']['copyTo']) {
|
||||
$this->curl->download($resolve, $reject, $origin, $url, $options, $job['request']['copyTo']);
|
||||
$job['curl_id'] = $this->curl->download($resolve, $reject, $origin, $url, $options, $job['request']['copyTo']);
|
||||
} else {
|
||||
$this->curl->download($resolve, $reject, $origin, $url, $options);
|
||||
$job['curl_id'] = $this->curl->download($resolve, $reject, $origin, $url, $options);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -253,51 +269,60 @@ class HttpDownloader
|
|||
$this->runningJobs--;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function scheduleNextJob()
|
||||
{
|
||||
foreach ($this->jobs as $job) {
|
||||
if ($job['status'] === self::STATUS_QUEUED) {
|
||||
$this->startJob($job['id']);
|
||||
if ($this->runningJobs >= $this->maxJobs) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function wait($index = null, $progress = false)
|
||||
public function wait($index = null)
|
||||
{
|
||||
while (true) {
|
||||
if ($this->curl) {
|
||||
$this->curl->tick();
|
||||
}
|
||||
|
||||
if (null !== $index) {
|
||||
if ($this->jobs[$index]['status'] === self::STATUS_COMPLETED || $this->jobs[$index]['status'] === self::STATUS_FAILED) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$done = true;
|
||||
foreach ($this->jobs as $job) {
|
||||
if (!in_array($job['status'], array(self::STATUS_COMPLETED, self::STATUS_FAILED), true)) {
|
||||
$done = false;
|
||||
break;
|
||||
} elseif (!$job['sync']) {
|
||||
unset($this->jobs[$job['id']]);
|
||||
}
|
||||
}
|
||||
if ($done) {
|
||||
return;
|
||||
}
|
||||
if (!$this->countActiveJobs($index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function enableAsync()
|
||||
{
|
||||
$this->allowAsync = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @return int number of active (queued or started) jobs
|
||||
*/
|
||||
public function countActiveJobs($index = null)
|
||||
{
|
||||
if ($this->runningJobs < $this->maxJobs) {
|
||||
foreach ($this->jobs as $job) {
|
||||
if ($job['status'] === self::STATUS_QUEUED && $this->runningJobs < $this->maxJobs) {
|
||||
$this->startJob($job['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->curl) {
|
||||
$this->curl->tick();
|
||||
}
|
||||
|
||||
if (null !== $index) {
|
||||
return $this->jobs[$index]['status'] < self::STATUS_COMPLETED ? 1 : 0;
|
||||
}
|
||||
|
||||
$active = 0;
|
||||
foreach ($this->jobs as $job) {
|
||||
if ($job['status'] < self::STATUS_COMPLETED) {
|
||||
$active++;
|
||||
} elseif (!$job['sync']) {
|
||||
unset($this->jobs[$job['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
return $active;
|
||||
}
|
||||
|
||||
private function getResponse($index)
|
||||
{
|
||||
if (!isset($this->jobs[$index])) {
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Util;
|
|||
|
||||
use Composer\Util\HttpDownloader;
|
||||
use React\Promise\Promise;
|
||||
use Symfony\Component\Console\Helper\ProgressBar;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -21,13 +22,22 @@ use React\Promise\Promise;
|
|||
class Loop
|
||||
{
|
||||
private $httpDownloader;
|
||||
private $processExecutor;
|
||||
private $currentPromises;
|
||||
|
||||
public function __construct(HttpDownloader $httpDownloader)
|
||||
public function __construct(HttpDownloader $httpDownloader = null, ProcessExecutor $processExecutor = null)
|
||||
{
|
||||
$this->httpDownloader = $httpDownloader;
|
||||
if ($this->httpDownloader) {
|
||||
$this->httpDownloader->enableAsync();
|
||||
}
|
||||
$this->processExecutor = $processExecutor;
|
||||
if ($this->processExecutor) {
|
||||
$this->processExecutor->enableAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public function wait(array $promises)
|
||||
public function wait(array $promises, ProgressBar $progress = null)
|
||||
{
|
||||
/** @var \Exception|null */
|
||||
$uncaught = null;
|
||||
|
@ -39,10 +49,52 @@ class Loop
|
|||
}
|
||||
);
|
||||
|
||||
$this->httpDownloader->wait();
|
||||
$this->currentPromises = $promises;
|
||||
|
||||
if ($progress) {
|
||||
$totalJobs = 0;
|
||||
if ($this->httpDownloader) {
|
||||
$totalJobs += $this->httpDownloader->countActiveJobs();
|
||||
}
|
||||
if ($this->processExecutor) {
|
||||
$totalJobs += $this->processExecutor->countActiveJobs();
|
||||
}
|
||||
$progress->start($totalJobs);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
$activeJobs = 0;
|
||||
|
||||
if ($this->httpDownloader) {
|
||||
$activeJobs += $this->httpDownloader->countActiveJobs();
|
||||
}
|
||||
if ($this->processExecutor) {
|
||||
$activeJobs += $this->processExecutor->countActiveJobs();
|
||||
}
|
||||
|
||||
if ($progress) {
|
||||
$progress->setProgress($progress->getMaxSteps() - $activeJobs);
|
||||
}
|
||||
|
||||
if (!$activeJobs) {
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(5000);
|
||||
}
|
||||
|
||||
$this->currentPromises = null;
|
||||
if ($uncaught) {
|
||||
throw $uncaught;
|
||||
}
|
||||
}
|
||||
|
||||
public function abortJobs()
|
||||
{
|
||||
if ($this->currentPromises) {
|
||||
foreach ($this->currentPromises as $promise) {
|
||||
$promise->cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,32 @@ use Composer\IO\IOInterface;
|
|||
use Symfony\Component\Process\Process;
|
||||
use Symfony\Component\Process\ProcessUtils;
|
||||
use Symfony\Component\Process\Exception\RuntimeException;
|
||||
use React\Promise\Promise;
|
||||
|
||||
/**
|
||||
* @author Robert Schönthal <seroscho@googlemail.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
*/
|
||||
class ProcessExecutor
|
||||
{
|
||||
const STATUS_QUEUED = 1;
|
||||
const STATUS_STARTED = 2;
|
||||
const STATUS_COMPLETED = 3;
|
||||
const STATUS_FAILED = 4;
|
||||
const STATUS_ABORTED = 5;
|
||||
|
||||
protected static $timeout = 300;
|
||||
|
||||
protected $captureOutput;
|
||||
protected $errorOutput;
|
||||
protected $io;
|
||||
|
||||
private $jobs = array();
|
||||
private $runningJobs = 0;
|
||||
private $maxJobs = 10;
|
||||
private $idGen = 0;
|
||||
private $allowAsync = false;
|
||||
|
||||
public function __construct(IOInterface $io = null)
|
||||
{
|
||||
$this->io = $io;
|
||||
|
@ -112,6 +126,192 @@ class ProcessExecutor
|
|||
return $process->getExitCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* starts a process on the commandline in async mode
|
||||
*
|
||||
* @param string $command the command to execute
|
||||
* @param mixed $output the output will be written into this var if passed by ref
|
||||
* if a callable is passed it will be used as output handler
|
||||
* @param string $cwd the working directory
|
||||
* @return int statuscode
|
||||
*/
|
||||
public function executeAsync($command, $cwd = null)
|
||||
{
|
||||
if (!$this->allowAsync) {
|
||||
throw new \LogicException('You must use the ProcessExecutor instance which is part of a Composer\Loop instance to be able to run async processes');
|
||||
}
|
||||
|
||||
$job = array(
|
||||
'id' => $this->idGen++,
|
||||
'status' => self::STATUS_QUEUED,
|
||||
'command' => $command,
|
||||
'cwd' => $cwd,
|
||||
);
|
||||
|
||||
$resolver = function ($resolve, $reject) use (&$job) {
|
||||
$job['status'] = ProcessExecutor::STATUS_QUEUED;
|
||||
$job['resolve'] = $resolve;
|
||||
$job['reject'] = $reject;
|
||||
};
|
||||
|
||||
$self = $this;
|
||||
$io = $this->io;
|
||||
|
||||
$canceler = function () use (&$job) {
|
||||
if ($job['status'] === self::STATUS_QUEUED) {
|
||||
$job['status'] = self::STATUS_ABORTED;
|
||||
}
|
||||
if ($job['status'] !== self::STATUS_STARTED) {
|
||||
return;
|
||||
}
|
||||
$job['status'] = self::STATUS_ABORTED;
|
||||
try {
|
||||
if (defined('SIGINT')) {
|
||||
$job['process']->signal(SIGINT);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// signal can throw in various conditions, but we don't care if it fails
|
||||
}
|
||||
$job['process']->stop(1);
|
||||
};
|
||||
|
||||
$promise = new Promise($resolver, $canceler);
|
||||
$promise = $promise->then(function () use (&$job, $self) {
|
||||
if ($job['process']->isSuccessful()) {
|
||||
$job['status'] = ProcessExecutor::STATUS_COMPLETED;
|
||||
} else {
|
||||
$job['status'] = ProcessExecutor::STATUS_FAILED;
|
||||
}
|
||||
|
||||
// TODO 3.0 this should be done directly on $this when PHP 5.3 is dropped
|
||||
$self->markJobDone();
|
||||
|
||||
return $job['process'];
|
||||
}, function ($e) use (&$job, $self) {
|
||||
$job['status'] = ProcessExecutor::STATUS_FAILED;
|
||||
|
||||
$self->markJobDone();
|
||||
|
||||
throw $e;
|
||||
});
|
||||
$this->jobs[$job['id']] =& $job;
|
||||
|
||||
if ($this->runningJobs < $this->maxJobs) {
|
||||
$this->startJob($job['id']);
|
||||
}
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
private function startJob($id)
|
||||
{
|
||||
$job =& $this->jobs[$id];
|
||||
if ($job['status'] !== self::STATUS_QUEUED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// start job
|
||||
$job['status'] = self::STATUS_STARTED;
|
||||
$this->runningJobs++;
|
||||
|
||||
$command = $job['command'];
|
||||
$cwd = $job['cwd'];
|
||||
|
||||
if ($this->io && $this->io->isDebug()) {
|
||||
$safeCommand = preg_replace_callback('{://(?P<user>[^:/\s]+):(?P<password>[^@\s/]+)@}i', function ($m) {
|
||||
if (preg_match('{^[a-f0-9]{12,}$}', $m['user'])) {
|
||||
return '://***:***@';
|
||||
}
|
||||
|
||||
return '://'.$m['user'].':***@';
|
||||
}, $command);
|
||||
$safeCommand = preg_replace("{--password (.*[^\\\\]\') }", '--password \'***\' ', $safeCommand);
|
||||
$this->io->writeError('Executing async command ('.($cwd ?: 'CWD').'): '.$safeCommand);
|
||||
}
|
||||
|
||||
// make sure that null translate to the proper directory in case the dir is a symlink
|
||||
// and we call a git command, because msysgit does not handle symlinks properly
|
||||
if (null === $cwd && Platform::isWindows() && false !== strpos($command, 'git') && getcwd()) {
|
||||
$cwd = realpath(getcwd());
|
||||
}
|
||||
|
||||
// TODO in v3, commands should be passed in as arrays of cmd + args
|
||||
if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandline')) {
|
||||
$process = Process::fromShellCommandline($command, $cwd, null, null, static::getTimeout());
|
||||
} else {
|
||||
$process = new Process($command, $cwd, null, null, static::getTimeout());
|
||||
}
|
||||
|
||||
$job['process'] = $process;
|
||||
|
||||
$process->start();
|
||||
}
|
||||
|
||||
public function wait($index = null)
|
||||
{
|
||||
while (true) {
|
||||
if (!$this->countActiveJobs($index)) {
|
||||
return;
|
||||
}
|
||||
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function enableAsync()
|
||||
{
|
||||
$this->allowAsync = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @return int number of active (queued or started) jobs
|
||||
*/
|
||||
public function countActiveJobs($index = null)
|
||||
{
|
||||
// tick
|
||||
foreach ($this->jobs as $job) {
|
||||
if ($job['status'] === self::STATUS_STARTED) {
|
||||
if (!$job['process']->isRunning()) {
|
||||
call_user_func($job['resolve'], $job['process']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->runningJobs < $this->maxJobs) {
|
||||
if ($job['status'] === self::STATUS_QUEUED) {
|
||||
$this->startJob($job['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $index) {
|
||||
return $this->jobs[$index]['status'] < self::STATUS_COMPLETED ? 1 : 0;
|
||||
}
|
||||
|
||||
$active = 0;
|
||||
foreach ($this->jobs as $job) {
|
||||
if ($job['status'] < self::STATUS_COMPLETED) {
|
||||
$active++;
|
||||
} else {
|
||||
unset($this->jobs[$job['id']]);
|
||||
}
|
||||
}
|
||||
|
||||
return $active;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
public function markJobDone()
|
||||
{
|
||||
$this->runningJobs--;
|
||||
}
|
||||
|
||||
public function splitLines($output)
|
||||
{
|
||||
$output = trim($output);
|
||||
|
|
|
@ -20,6 +20,7 @@ use Composer\Util\HttpDownloader;
|
|||
use Composer\Util\Http\Response;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @author François Pluchino <francois.pluchino@opendisplay.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
|
@ -54,8 +55,9 @@ class RemoteFilesystem
|
|||
* @param Config $config The config
|
||||
* @param array $options The options
|
||||
* @param bool $disableTls
|
||||
* @param AuthHelper $authHelper
|
||||
*/
|
||||
public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false)
|
||||
public function __construct(IOInterface $io, Config $config, array $options = array(), $disableTls = false, AuthHelper $authHelper = null)
|
||||
{
|
||||
$this->io = $io;
|
||||
|
||||
|
@ -70,7 +72,7 @@ class RemoteFilesystem
|
|||
// handle the other externally set options normally.
|
||||
$this->options = array_replace_recursive($this->options, $options);
|
||||
$this->config = $config;
|
||||
$this->authHelper = new AuthHelper($io, $config);
|
||||
$this->authHelper = isset($authHelper) ? $authHelper : new AuthHelper($io, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -139,8 +139,8 @@ class FileDownloaderTest extends TestCase
|
|||
->will($this->returnValue($path.'/vendor'));
|
||||
|
||||
try {
|
||||
$promise = $downloader->download($packageMock, $path);
|
||||
$loop = new Loop($this->httpDownloader);
|
||||
$promise = $downloader->download($packageMock, $path);
|
||||
$loop->wait(array($promise));
|
||||
|
||||
$this->fail('Download was expected to throw');
|
||||
|
@ -225,8 +225,8 @@ class FileDownloaderTest extends TestCase
|
|||
touch($dlFile);
|
||||
|
||||
try {
|
||||
$promise = $downloader->download($packageMock, $path);
|
||||
$loop = new Loop($this->httpDownloader);
|
||||
$promise = $downloader->download($packageMock, $path);
|
||||
$loop->wait(array($promise));
|
||||
|
||||
$this->fail('Download was expected to throw');
|
||||
|
@ -296,8 +296,8 @@ class FileDownloaderTest extends TestCase
|
|||
mkdir(dirname($dlFile), 0777, true);
|
||||
touch($dlFile);
|
||||
|
||||
$promise = $downloader->download($newPackage, $path, $oldPackage);
|
||||
$loop = new Loop($this->httpDownloader);
|
||||
$promise = $downloader->download($newPackage, $path, $oldPackage);
|
||||
$loop->wait(array($promise));
|
||||
|
||||
$downloader->update($oldPackage, $newPackage, $path);
|
||||
|
|
|
@ -18,6 +18,8 @@ use Composer\Test\TestCase;
|
|||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Platform;
|
||||
use Prophecy\Argument;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Util\Git as GitUtil;
|
||||
|
||||
class GitDownloaderTest extends TestCase
|
||||
{
|
||||
|
@ -30,6 +32,8 @@ class GitDownloaderTest extends TestCase
|
|||
{
|
||||
$this->skipIfNotExecutable('git');
|
||||
|
||||
$this->initGitVersion('1.0.0');
|
||||
|
||||
$this->fs = new Filesystem;
|
||||
$this->workingDir = $this->getUniqueTmpDirectory();
|
||||
}
|
||||
|
@ -40,10 +44,15 @@ class GitDownloaderTest extends TestCase
|
|||
$this->fs->removeDirectory($this->workingDir);
|
||||
}
|
||||
|
||||
$this->initGitVersion(false);
|
||||
}
|
||||
|
||||
private function initGitVersion($version)
|
||||
{
|
||||
// reset the static version cache
|
||||
$refl = new \ReflectionProperty('Composer\Util\Git', 'version');
|
||||
$refl->setAccessible(true);
|
||||
$refl->setValue(null, null);
|
||||
$refl->setValue(null, $version);
|
||||
}
|
||||
|
||||
protected function setupConfig($config = null)
|
||||
|
@ -103,32 +112,23 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue('dev-master'));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat('git --version')))
|
||||
->will($this->returnCallback(function ($command, &$output = null) {
|
||||
$output = 'git version 1.0.0';
|
||||
|
||||
return 0;
|
||||
}));
|
||||
|
||||
$expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://example.com/composer/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://example.com/composer/composer' && git fetch composer && git remote set-url origin 'https://example.com/composer/composer' && git remote set-url composer 'https://example.com/composer/composer'");
|
||||
$processExecutor->expects($this->at(1))
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(2))
|
||||
$processExecutor->expects($this->at(1))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git branch -r")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(3))
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git checkout 'master' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(4))
|
||||
$processExecutor->expects($this->at(3))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
@ -157,14 +157,7 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue('dev-master'));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat('git --version')))
|
||||
->will($this->returnCallback(function ($command, &$output = null) {
|
||||
$output = 'git version 2.3.1';
|
||||
|
||||
return 0;
|
||||
}));
|
||||
$this->initGitVersion('2.17.0');
|
||||
|
||||
$config = new Config;
|
||||
$this->setupConfig($config);
|
||||
|
@ -174,7 +167,7 @@ class GitDownloaderTest extends TestCase
|
|||
$filesystem->removeDirectory($cachePath);
|
||||
|
||||
$expectedGitCommand = $this->winCompat(sprintf("git clone --mirror 'https://example.com/composer/composer' '%s'", $cachePath));
|
||||
$processExecutor->expects($this->at(1))
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnCallback(function () use ($cachePath) {
|
||||
|
@ -182,7 +175,7 @@ class GitDownloaderTest extends TestCase
|
|||
|
||||
return 0;
|
||||
}));
|
||||
$processExecutor->expects($this->at(2))
|
||||
$processExecutor->expects($this->at(1))
|
||||
->method('execute')
|
||||
->with($this->equalTo('git rev-parse --git-dir'), $this->anything(), $this->equalTo($this->winCompat($cachePath)))
|
||||
->will($this->returnCallback(function ($command, &$output = null) {
|
||||
|
@ -190,28 +183,28 @@ class GitDownloaderTest extends TestCase
|
|||
|
||||
return 0;
|
||||
}));
|
||||
$processExecutor->expects($this->at(3))
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat('git rev-parse --quiet --verify \'1234567890123456789012345678901234567890^{commit}\'')), $this->equalTo(null), $this->equalTo($this->winCompat($cachePath)))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$expectedGitCommand = $this->winCompat(sprintf("git clone --no-checkout '%1\$s' 'composerPath' --dissociate --reference '%1\$s' && cd 'composerPath' && git remote set-url origin 'https://example.com/composer/composer' && git remote add composer 'https://example.com/composer/composer'", $cachePath));
|
||||
$processExecutor->expects($this->at(4))
|
||||
$processExecutor->expects($this->at(3))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(5))
|
||||
$processExecutor->expects($this->at(4))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git branch -r")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(6))
|
||||
$processExecutor->expects($this->at(5))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git checkout 'master' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(7))
|
||||
$processExecutor->expects($this->at(6))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git reset --hard '1234567890123456789012345678901234567890' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
@ -241,50 +234,41 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue('1.0.0'));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat('git --version')))
|
||||
->will($this->returnCallback(function ($command, &$output = null) {
|
||||
$output = 'git version 1.0.0';
|
||||
|
||||
return 0;
|
||||
}));
|
||||
|
||||
$expectedGitCommand = $this->winCompat("git clone --no-checkout 'https://github.com/mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'https://github.com/mirrors/composer' && git fetch composer && git remote set-url origin 'https://github.com/mirrors/composer' && git remote set-url composer 'https://github.com/mirrors/composer'");
|
||||
$processExecutor->expects($this->at(1))
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnValue(1));
|
||||
|
||||
$processExecutor->expects($this->at(2))
|
||||
$processExecutor->expects($this->at(1))
|
||||
->method('getErrorOutput')
|
||||
->with()
|
||||
->will($this->returnValue('Error1'));
|
||||
|
||||
$expectedGitCommand = $this->winCompat("git clone --no-checkout 'git@github.com:mirrors/composer' 'composerPath' && cd 'composerPath' && git remote add composer 'git@github.com:mirrors/composer' && git fetch composer && git remote set-url origin 'git@github.com:mirrors/composer' && git remote set-url composer 'git@github.com:mirrors/composer'");
|
||||
$processExecutor->expects($this->at(3))
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$expectedGitCommand = $this->winCompat("git remote set-url origin 'https://github.com/composer/composer'");
|
||||
$processExecutor->expects($this->at(4))
|
||||
$processExecutor->expects($this->at(3))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$expectedGitCommand = $this->winCompat("git remote set-url --push origin 'git@github.com:composer/composer.git'");
|
||||
$processExecutor->expects($this->at(5))
|
||||
$processExecutor->expects($this->at(4))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(6))
|
||||
$processExecutor->expects($this->at(5))
|
||||
->method('execute')
|
||||
->with($this->equalTo('git branch -r'))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->at(7))
|
||||
$processExecutor->expects($this->at(6))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
@ -328,28 +312,19 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue('1.0.0'));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat('git --version')))
|
||||
->will($this->returnCallback(function ($command, &$output = null) {
|
||||
$output = 'git version 1.0.0';
|
||||
|
||||
return 0;
|
||||
}));
|
||||
|
||||
$expectedGitCommand = $this->winCompat("git clone --no-checkout '{$url}' 'composerPath' && cd 'composerPath' && git remote add composer '{$url}' && git fetch composer && git remote set-url origin '{$url}' && git remote set-url composer '{$url}'");
|
||||
$processExecutor->expects($this->at(1))
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$expectedGitCommand = $this->winCompat("git remote set-url --push origin '{$pushUrl}'");
|
||||
$processExecutor->expects($this->at(2))
|
||||
$processExecutor->expects($this->at(1))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand), $this->equalTo(null), $this->equalTo($this->winCompat('composerPath')))
|
||||
->will($this->returnValue(0));
|
||||
|
||||
$processExecutor->expects($this->exactly(5))
|
||||
$processExecutor->expects($this->exactly(4))
|
||||
->method('execute')
|
||||
->will($this->returnValue(0));
|
||||
|
||||
|
@ -375,14 +350,6 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue(array('https://example.com/composer/composer')));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat('git --version')))
|
||||
->will($this->returnCallback(function ($command, &$output = null) {
|
||||
$output = 'git version 1.0.0';
|
||||
|
||||
return 0;
|
||||
}));
|
||||
$processExecutor->expects($this->at(1))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnValue(1));
|
||||
|
@ -437,7 +404,6 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue('1.0.0.0'));
|
||||
|
||||
$process = $this->prophesize('Composer\Util\ProcessExecutor');
|
||||
$process->execute($this->winCompat('git --version'), Argument::cetera())->willReturn(0);
|
||||
$process->execute($this->winCompat('git show-ref --head -d'), Argument::cetera())->willReturn(0);
|
||||
$process->execute($this->winCompat('git status --porcelain --untracked-files=no'), Argument::cetera())->willReturn(0);
|
||||
$process->execute($this->winCompat('git remote -v'), Argument::cetera())->willReturn(0);
|
||||
|
@ -474,33 +440,29 @@ class GitDownloaderTest extends TestCase
|
|||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git --version")))
|
||||
->with($this->equalTo($this->winCompat("git show-ref --head -d")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(1))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git show-ref --head -d")))
|
||||
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(3))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
->with($this->equalTo($this->winCompat($expectedGitUpdateCommand)), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(4))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat($expectedGitUpdateCommand)), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
|
||||
->with($this->equalTo('git branch -r'))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(5))
|
||||
->method('execute')
|
||||
->with($this->equalTo('git branch -r'))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(6))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(7))
|
||||
$processExecutor->expects($this->at(6))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
->will($this->returnCallback(function ($cmd, &$output, $cwd) {
|
||||
|
@ -512,11 +474,11 @@ composer https://github.com/old/url (push)
|
|||
|
||||
return 0;
|
||||
}));
|
||||
$processExecutor->expects($this->at(8))
|
||||
$processExecutor->expects($this->at(7))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote set-url origin 'https://github.com/composer/composer'")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(9))
|
||||
$processExecutor->expects($this->at(8))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote set-url --push origin 'git@github.com:composer/composer.git'")), $this->equalTo(null), $this->equalTo($this->winCompat($this->workingDir)))
|
||||
->will($this->returnValue(0));
|
||||
|
|
|
@ -70,8 +70,8 @@ class XzDownloaderTest extends TestCase
|
|||
$downloader = new XzDownloader($io, $config, $httpDownloader = new HttpDownloader($io, $this->getMockBuilder('Composer\Config')->getMock()), null, null, null);
|
||||
|
||||
try {
|
||||
$promise = $downloader->download($packageMock, $this->testDir.'/install-path');
|
||||
$loop = new Loop($httpDownloader);
|
||||
$promise = $downloader->download($packageMock, $this->testDir.'/install-path');
|
||||
$loop->wait(array($promise));
|
||||
$downloader->install($packageMock, $this->testDir.'/install-path');
|
||||
|
||||
|
|
|
@ -60,9 +60,6 @@ class ZipDownloaderTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @group only
|
||||
*/
|
||||
public function testErrorMessages()
|
||||
{
|
||||
if (!class_exists('ZipArchive')) {
|
||||
|
@ -92,8 +89,8 @@ class ZipDownloaderTest extends TestCase
|
|||
$this->setPrivateProperty('hasSystemUnzip', false);
|
||||
|
||||
try {
|
||||
$promise = $downloader->download($this->package, $path = sys_get_temp_dir().'/composer-zip-test');
|
||||
$loop = new Loop($this->httpDownloader);
|
||||
$promise = $downloader->download($this->package, $path = sys_get_temp_dir().'/composer-zip-test');
|
||||
$loop->wait(array($promise));
|
||||
$downloader->install($this->package, $path);
|
||||
|
||||
|
@ -125,7 +122,8 @@ class ZipDownloaderTest extends TestCase
|
|||
->will($this->returnValue(false));
|
||||
|
||||
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,12 +148,10 @@ class ZipDownloaderTest extends TestCase
|
|||
->will($this->throwException(new \ErrorException('Not a directory')));
|
||||
|
||||
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group only
|
||||
*/
|
||||
public function testZipArchiveOnlyGood()
|
||||
{
|
||||
if (!class_exists('ZipArchive')) {
|
||||
|
@ -174,45 +170,66 @@ class ZipDownloaderTest extends TestCase
|
|||
->will($this->returnValue(true));
|
||||
|
||||
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage Failed to execute (1) unzip
|
||||
* @expectedExceptionMessage Failed to extract : (1) unzip
|
||||
*/
|
||||
public function testSystemUnzipOnlyFailed()
|
||||
{
|
||||
if (!class_exists('ZipArchive')) {
|
||||
$this->markTestSkipped('zip extension missing');
|
||||
}
|
||||
|
||||
$this->setPrivateProperty('isWindows', false);
|
||||
$this->setPrivateProperty('hasSystemUnzip', true);
|
||||
$this->setPrivateProperty('hasZipArchive', false);
|
||||
|
||||
$procMock = $this->getMockBuilder('Symfony\Component\Process\Process')->disableOriginalConstructor()->getMock();
|
||||
$procMock->expects($this->any())
|
||||
->method('getExitCode')
|
||||
->will($this->returnValue(1));
|
||||
$procMock->expects($this->any())
|
||||
->method('isSuccessful')
|
||||
->will($this->returnValue(false));
|
||||
$procMock->expects($this->any())
|
||||
->method('getErrorOutput')
|
||||
->will($this->returnValue('output'));
|
||||
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->will($this->returnValue(1));
|
||||
->method('executeAsync')
|
||||
->will($this->returnValue(\React\Promise\resolve($procMock)));
|
||||
|
||||
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
public function testSystemUnzipOnlyGood()
|
||||
{
|
||||
if (!class_exists('ZipArchive')) {
|
||||
$this->markTestSkipped('zip extension missing');
|
||||
}
|
||||
|
||||
$this->setPrivateProperty('isWindows', false);
|
||||
$this->setPrivateProperty('hasSystemUnzip', true);
|
||||
$this->setPrivateProperty('hasZipArchive', false);
|
||||
|
||||
$procMock = $this->getMockBuilder('Symfony\Component\Process\Process')->disableOriginalConstructor()->getMock();
|
||||
$procMock->expects($this->any())
|
||||
->method('getExitCode')
|
||||
->will($this->returnValue(0));
|
||||
$procMock->expects($this->any())
|
||||
->method('isSuccessful')
|
||||
->will($this->returnValue(true));
|
||||
$procMock->expects($this->any())
|
||||
->method('getErrorOutput')
|
||||
->will($this->returnValue('output'));
|
||||
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->will($this->returnValue(0));
|
||||
->method('executeAsync')
|
||||
->will($this->returnValue(\React\Promise\resolve($procMock)));
|
||||
|
||||
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
public function testNonWindowsFallbackGood()
|
||||
|
@ -225,10 +242,21 @@ class ZipDownloaderTest extends TestCase
|
|||
$this->setPrivateProperty('hasSystemUnzip', true);
|
||||
$this->setPrivateProperty('hasZipArchive', true);
|
||||
|
||||
$procMock = $this->getMockBuilder('Symfony\Component\Process\Process')->disableOriginalConstructor()->getMock();
|
||||
$procMock->expects($this->any())
|
||||
->method('getExitCode')
|
||||
->will($this->returnValue(1));
|
||||
$procMock->expects($this->any())
|
||||
->method('isSuccessful')
|
||||
->will($this->returnValue(false));
|
||||
$procMock->expects($this->any())
|
||||
->method('getErrorOutput')
|
||||
->will($this->returnValue('output'));
|
||||
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->will($this->returnValue(1));
|
||||
->method('executeAsync')
|
||||
->will($this->returnValue(\React\Promise\resolve($procMock)));
|
||||
|
||||
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
|
||||
$zipArchive->expects($this->at(0))
|
||||
|
@ -240,7 +268,8 @@ class ZipDownloaderTest extends TestCase
|
|||
|
||||
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
|
||||
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -257,10 +286,21 @@ class ZipDownloaderTest extends TestCase
|
|||
$this->setPrivateProperty('hasSystemUnzip', true);
|
||||
$this->setPrivateProperty('hasZipArchive', true);
|
||||
|
||||
$procMock = $this->getMockBuilder('Symfony\Component\Process\Process')->disableOriginalConstructor()->getMock();
|
||||
$procMock->expects($this->any())
|
||||
->method('getExitCode')
|
||||
->will($this->returnValue(1));
|
||||
$procMock->expects($this->any())
|
||||
->method('isSuccessful')
|
||||
->will($this->returnValue(false));
|
||||
$procMock->expects($this->any())
|
||||
->method('getErrorOutput')
|
||||
->will($this->returnValue('output'));
|
||||
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->will($this->returnValue(1));
|
||||
->method('executeAsync')
|
||||
->will($this->returnValue(\React\Promise\resolve($procMock)));
|
||||
|
||||
$zipArchive = $this->getMockBuilder('ZipArchive')->getMock();
|
||||
$zipArchive->expects($this->at(0))
|
||||
|
@ -272,7 +312,8 @@ class ZipDownloaderTest extends TestCase
|
|||
|
||||
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
|
||||
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
public function testWindowsFallbackGood()
|
||||
|
@ -300,7 +341,8 @@ class ZipDownloaderTest extends TestCase
|
|||
|
||||
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
|
||||
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -332,7 +374,26 @@ class ZipDownloaderTest extends TestCase
|
|||
|
||||
$downloader = new MockedZipDownloader($this->io, $this->config, $this->httpDownloader, null, null, null, $processExecutor);
|
||||
$this->setPrivateProperty('zipArchiveObject', $zipArchive, $downloader);
|
||||
$downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$promise = $downloader->extract($this->package, 'testfile.zip', 'vendor/dir');
|
||||
$this->wait($promise);
|
||||
}
|
||||
|
||||
private function wait($promise)
|
||||
{
|
||||
if (null === $promise) {
|
||||
return;
|
||||
}
|
||||
|
||||
$e = null;
|
||||
$promise->then(function () {
|
||||
// noop
|
||||
}, function ($ex) use (&$e) {
|
||||
$e = $ex;
|
||||
});
|
||||
|
||||
if ($e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,6 +411,6 @@ class MockedZipDownloader extends ZipDownloader
|
|||
|
||||
public function extract(PackageInterface $package, $file, $path)
|
||||
{
|
||||
parent::extract($package, $file, $path);
|
||||
return parent::extract($package, $file, $path);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ Test the error output of solver problems with dev-master aliases.
|
|||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{"name": "a/a", "version": "dev-master", "require": {"d/d": "1.0.0"}},
|
||||
{"name": "b/b", "version": "dev-master", "require": {"d/d": "2.0.0"}},
|
||||
{"name": "a/a", "version": "dev-master", "require": {"d/d": "1.0.0"}, "default-branch": true},
|
||||
{"name": "b/b", "version": "dev-master", "require": {"d/d": "2.0.0"}, "default-branch": true},
|
||||
{"name": "d/d", "version": "1.0.0"},
|
||||
{"name": "d/d", "version": "2.0.0"}
|
||||
]
|
||||
|
|
|
@ -6,7 +6,7 @@ Test the error output of solver problems with dev-master aliases.
|
|||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "locked/pkg", "version": "dev-master", "require": {"locked/dependency": "1.0.0"} }
|
||||
{ "name": "locked/pkg", "version": "dev-master", "require": {"locked/dependency": "1.0.0"}, "default-branch": true }
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -18,7 +18,7 @@ Test the error output of solver problems with dev-master aliases.
|
|||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "locked/pkg", "version": "dev-master", "require": {"locked/dependency": "1.0.0"} },
|
||||
{ "name": "locked/pkg", "version": "dev-master", "require": {"locked/dependency": "1.0.0"}, "default-branch": true },
|
||||
{ "name": "locked/dependency", "version": "1.0.0" }
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
|
@ -8,7 +8,8 @@ Aliases of referenced packages work
|
|||
"package": [
|
||||
{
|
||||
"name": "a/aliased", "version": "dev-master",
|
||||
"source": { "reference": "orig", "type": "git", "url": "" }
|
||||
"source": { "reference": "orig", "type": "git", "url": "" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/requirer", "version": "1.0.0",
|
||||
|
@ -31,7 +32,8 @@ update
|
|||
{
|
||||
"name": "a/aliased", "version": "dev-master",
|
||||
"source": { "reference": "abcd", "type": "git", "url": "" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/requirer", "version": "1.0.0",
|
||||
|
|
|
@ -13,20 +13,24 @@ Aliases take precedence over default package even if default is selected
|
|||
{
|
||||
"name": "a/req", "version": "dev-master",
|
||||
"extra": { "branch-alias": { "dev-master": "1.0.x-dev" } },
|
||||
"source": { "reference": "forked", "type": "git", "url": "" }
|
||||
"source": { "reference": "forked", "type": "git", "url": "" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/req", "version": "dev-master",
|
||||
"extra": { "branch-alias": { "dev-master": "1.0.x-dev" } },
|
||||
"source": { "reference": "master", "type": "git", "url": "" }
|
||||
"source": { "reference": "master", "type": "git", "url": "" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"require": { "a/req": "dev-master" }
|
||||
"require": { "a/req": "dev-master" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/b", "version": "dev-master",
|
||||
"require": { "a/req": "dev-master" }
|
||||
"require": { "a/req": "dev-master" },
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -44,12 +48,14 @@ Aliases take precedence over default package even if default is selected
|
|||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"require": { "a/req": "dev-master" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/b", "version": "dev-master",
|
||||
"require": { "a/req": "dev-master" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/req", "version": "dev-feature-foo",
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
--TEST--
|
||||
Aliases are loaded when splitting require-dev from require (https://github.com/composer/composer/issues/8954)
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{
|
||||
"name": "a/aliased", "version": "dev-next", "replace": { "a/aliased-replaced": "self.version" }
|
||||
},
|
||||
{
|
||||
"name": "b/requirer", "version": "2.3.0",
|
||||
"require": { "a/aliased-replaced": "^4.0" }
|
||||
},
|
||||
{
|
||||
"name": "a/aliased2", "version": "dev-next", "replace": { "a/aliased-replaced2": "self.version" }
|
||||
},
|
||||
{
|
||||
"name": "b/requirer2", "version": "2.3.0",
|
||||
"require": { "a/aliased-replaced": "^4.0", "a/aliased-replaced2": "^4.0" }
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"a/aliased": "dev-next as 4.1.0-RC2",
|
||||
"b/requirer": "2.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"a/aliased2": "dev-next as 4.1.0-RC2",
|
||||
"b/requirer2": "2.3.0"
|
||||
}
|
||||
}
|
||||
--RUN--
|
||||
update
|
||||
--EXPECT-LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{
|
||||
"name": "a/aliased", "version": "dev-next",
|
||||
"type": "library",
|
||||
"replace": { "a/aliased-replaced": "self.version" }
|
||||
},
|
||||
{
|
||||
"name": "b/requirer", "version": "2.3.0",
|
||||
"require": { "a/aliased-replaced": "^4.0" },
|
||||
"type": "library"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "a/aliased2", "version": "dev-next",
|
||||
"type": "library",
|
||||
"replace": { "a/aliased-replaced2": "self.version" }
|
||||
},
|
||||
{
|
||||
"name": "b/requirer2", "version": "2.3.0",
|
||||
"require": { "a/aliased-replaced": "^4.0", "a/aliased-replaced2": "^4.0" },
|
||||
"type": "library"
|
||||
}
|
||||
],
|
||||
"aliases": [{
|
||||
"package": "a/aliased2",
|
||||
"version": "dev-next",
|
||||
"alias": "4.1.0-RC2",
|
||||
"alias_normalized": "4.1.0.0-RC2"
|
||||
}, {
|
||||
"package": "a/aliased",
|
||||
"version": "dev-next",
|
||||
"alias": "4.1.0-RC2",
|
||||
"alias_normalized": "4.1.0.0-RC2"
|
||||
}],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"a/aliased": 20,
|
||||
"a/aliased2": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
||||
--EXPECT--
|
||||
Installing a/aliased (dev-next)
|
||||
Marking a/aliased (4.1.0-RC2) as installed, alias of a/aliased (dev-next)
|
||||
Installing b/requirer (2.3.0)
|
||||
Installing a/aliased2 (dev-next)
|
||||
Marking a/aliased2 (4.1.0-RC2) as installed, alias of a/aliased2 (dev-next)
|
||||
Installing b/requirer2 (2.3.0)
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
See Github issue #4795 ( github.com/composer/composer/issues/4795 ).
|
||||
|
||||
Composer\Installer::whitelistUpdateDependencies should not output a warning for dependencies that need to be updated
|
||||
that are also a root package, when that root package is also explicitly whitelisted.
|
||||
Composer\Installer::allowListUpdateDependencies should not output a warning for dependencies that need to be updated
|
||||
that are also a root package, when that root package is also explicitly allowed.
|
||||
|
||||
--COMPOSER--
|
||||
{
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
See Github issue #4795 ( github.com/composer/composer/issues/4795 ).
|
||||
|
||||
Composer\Installer::whitelistUpdateDependencies intentionally ignores root requirements even if said package is also a
|
||||
dependency of one the requirements that is whitelisted for update.
|
||||
Composer\Installer::allowListUpdateDependencies intentionally ignores root requirements even if said package is also a
|
||||
dependency of one the requirements that is allowed for update.
|
||||
|
||||
--COMPOSER--
|
||||
{
|
||||
|
|
|
@ -11,7 +11,8 @@ Installing double aliased package
|
|||
"dist": { "type": "file", "url": "" },
|
||||
"require": {
|
||||
"b/b": "dev-master"
|
||||
}
|
||||
},
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "dev-foo",
|
||||
|
|
|
@ -14,7 +14,8 @@ Installs a dev package from lock using dist
|
|||
"type": "zip",
|
||||
"url": "http://www.example.com/dist.zip",
|
||||
"reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6"
|
||||
}
|
||||
},
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -25,7 +26,7 @@ Installs a dev package from lock using dist
|
|||
"minimum-stability": "dev"
|
||||
}
|
||||
--RUN--
|
||||
install --prefer-dist
|
||||
install
|
||||
--EXPECT-LOCK--
|
||||
{
|
||||
"packages": [
|
||||
|
@ -37,7 +38,8 @@ install --prefer-dist
|
|||
"url": "http://www.example.com/dist.zip",
|
||||
"reference": "459720ff3b74ee0c0d159277c6f2f5df89d8a4f6"
|
||||
},
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
|
@ -6,8 +6,8 @@ Install from a lock file that deleted a package
|
|||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "whitelisted/pkg", "version": "1.1.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "fixed/dependency": "1.0.0", "old/dependency": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "fixed/dependency": "1.0.0", "old/dependency": "1.0.0" } },
|
||||
{ "name": "fixed/dependency", "version": "1.1.0" },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0" },
|
||||
{ "name": "old/dependency", "version": "1.0.0" }
|
||||
|
@ -15,14 +15,14 @@ Install from a lock file that deleted a package
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"whitelisted/pkg": "1.*",
|
||||
"allowed/pkg": "1.*",
|
||||
"fixed/dependency": "1.*"
|
||||
}
|
||||
}
|
||||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "whitelisted/pkg", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0" }
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
@ -33,7 +33,7 @@ Install from a lock file that deleted a package
|
|||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0" },
|
||||
{ "name": "old/dependency", "version": "1.0.0" }
|
||||
]
|
||||
|
@ -41,4 +41,4 @@ Install from a lock file that deleted a package
|
|||
install
|
||||
--EXPECT--
|
||||
Removing old/dependency (1.0.0)
|
||||
Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -7,18 +7,19 @@ Installs a dev package forcing it's reference
|
|||
"type": "package",
|
||||
"package": [
|
||||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"source": { "reference": "abc123", "url": "", "type": "git" }
|
||||
"name": "a/a", "version": "dev-main",
|
||||
"source": { "reference": "abc123", "url": "", "type": "git" },
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"a/a": "dev-master#def000"
|
||||
"a/a": "dev-main#def000"
|
||||
}
|
||||
}
|
||||
--RUN--
|
||||
install
|
||||
--EXPECT--
|
||||
Installing a/a (dev-master def000)
|
||||
Marking a/a (9999999-dev def000) as installed, alias of a/a (dev-master def000)
|
||||
Installing a/a (dev-main def000)
|
||||
Marking a/a (9999999-dev def000) as installed, alias of a/a (dev-main def000)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Partial update forces updates dev reference from lock file for non whitelisted packages
|
||||
Partial update forces updates dev reference from lock file for non allowed packages
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
|
|
@ -7,7 +7,7 @@ Test that a conflict against >=5 does not include dev-master or other dev-x
|
|||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "conflicter/pkg", "version": "1.0.0", "conflict": { "victim/pkg": ">=5", "victim/pkg2": ">=5" } },
|
||||
{ "name": "victim/pkg", "version": "dev-master" },
|
||||
{ "name": "victim/pkg", "version": "dev-master", "default-branch": true },
|
||||
{ "name": "victim/pkg2", "version": "dev-foo" }
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates those packages if they are not present in composer.json
|
||||
Update with a package allow list only updates those packages if they are not present in composer.json
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "fixed/dependency", "version": "1.1.0", "require": { "fixed/sub-dependency": "1.*" } },
|
||||
|
@ -18,13 +18,13 @@ Update with a package whitelist only updates those packages if they are not pres
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"whitelisted/pkg": "1.*",
|
||||
"allowed/pkg": "1.*",
|
||||
"fixed/dependency": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0", "require": { "fixed/sub-dependency": "1.*" } },
|
||||
{ "name": "fixed/sub-dependency", "version": "1.0.0" }
|
||||
|
@ -32,7 +32,7 @@ Update with a package whitelist only updates those packages if they are not pres
|
|||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0", "fixed/dependency": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0", "require": { "fixed/sub-dependency": "1.*" } },
|
||||
{ "name": "fixed/sub-dependency", "version": "1.0.0" }
|
||||
|
@ -47,7 +47,7 @@ Update with a package whitelist only updates those packages if they are not pres
|
|||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg dependency/pkg
|
||||
update allowed/pkg dependency/pkg
|
||||
--EXPECT--
|
||||
Upgrading dependency/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist pattern and all-dependencies flag updates packages and their dependencies, even if defined as root dependency, matching the pattern
|
||||
Update with a package allow list pattern and all-dependencies flag updates packages and their dependencies, even if defined as root dependency, matching the pattern
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -8,10 +8,10 @@ Update with a package whitelist pattern and all-dependencies flag updates packag
|
|||
"package": [
|
||||
{ "name": "fixed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.1.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -23,8 +23,8 @@ Update with a package whitelist pattern and all-dependencies flag updates packag
|
|||
],
|
||||
"require": {
|
||||
"fixed/pkg": "1.*",
|
||||
"whitelisted/pkg-component1": "1.*",
|
||||
"whitelisted/pkg-component2": "1.*",
|
||||
"allowed/pkg-component1": "1.*",
|
||||
"allowed/pkg-component2": "1.*",
|
||||
"dependency/pkg": "1.*",
|
||||
"unrelated/pkg": "1.*"
|
||||
}
|
||||
|
@ -32,8 +32,8 @@ Update with a package whitelist pattern and all-dependencies flag updates packag
|
|||
--INSTALLED--
|
||||
[
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -42,8 +42,8 @@ Update with a package whitelist pattern and all-dependencies flag updates packag
|
|||
{
|
||||
"packages": [
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -58,8 +58,8 @@ Update with a package whitelist pattern and all-dependencies flag updates packag
|
|||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg-* --with-all-dependencies
|
||||
update allowed/pkg-* --with-all-dependencies
|
||||
--EXPECT--
|
||||
Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component1 (1.0.0 => 1.1.0)
|
||||
Upgrading dependency/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component2 (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates those packages and their dependencies matching the pattern but no dependencies defined as roo package
|
||||
Update with a package allow list only updates those packages and their dependencies matching the pattern but no dependencies defined as roo package
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -8,10 +8,10 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
"package": [
|
||||
{ "name": "fixed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.1.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*", "root/pkg-dependency": "1.*" } },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*", "root/pkg-dependency": "1.*" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*", "root/pkg-dependency": "1.*" } },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*", "root/pkg-dependency": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "root/pkg-dependency", "version": "1.1.0" },
|
||||
|
@ -25,8 +25,8 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
],
|
||||
"require": {
|
||||
"fixed/pkg": "1.*",
|
||||
"whitelisted/pkg-component1": "1.*",
|
||||
"whitelisted/pkg-component2": "1.*",
|
||||
"allowed/pkg-component1": "1.*",
|
||||
"allowed/pkg-component2": "1.*",
|
||||
"root/pkg-dependency": "1.*",
|
||||
"unrelated/pkg": "1.*"
|
||||
}
|
||||
|
@ -34,8 +34,8 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
--INSTALLED--
|
||||
[
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "root/pkg-dependency", "version": "1.0.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -45,8 +45,8 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
{
|
||||
"packages": [
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "root/pkg-dependency", "version": "1.0.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -60,8 +60,8 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
"prefer-lowest": false
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg-* --with-dependencies
|
||||
update allowed/pkg-* --with-dependencies
|
||||
--EXPECT--
|
||||
Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component1 (1.0.0 => 1.1.0)
|
||||
Upgrading dependency/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component2 (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates those packages and their dependencies matching the pattern
|
||||
Update with a package allow list only updates those packages and their dependencies matching the pattern
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -8,16 +8,16 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
"package": [
|
||||
{ "name": "fixed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.1.0", "require": { "whitelisted/pkg-component2": "1.1.0" } },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0", "require": { "whitelisted/pkg-component2": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0", "whitelisted/pkg-component5": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component3", "version": "1.1.0", "require": { "whitelisted/pkg-component4": "1.1.0" } },
|
||||
{ "name": "whitelisted/pkg-component3", "version": "1.0.0", "require": { "whitelisted/pkg-component4": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component4", "version": "1.1.0" },
|
||||
{ "name": "whitelisted/pkg-component4", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component5", "version": "1.1.0" },
|
||||
{ "name": "whitelisted/pkg-component5", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.1.0", "require": { "allowed/pkg-component2": "1.1.0" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0", "require": { "allowed/pkg-component2": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0", "allowed/pkg-component5": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component3", "version": "1.1.0", "require": { "allowed/pkg-component4": "1.1.0" } },
|
||||
{ "name": "allowed/pkg-component3", "version": "1.0.0", "require": { "allowed/pkg-component4": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component4", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg-component4", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component5", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg-component5", "version": "1.0.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -29,20 +29,20 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
],
|
||||
"require": {
|
||||
"fixed/pkg": "1.*",
|
||||
"whitelisted/pkg-component1": "1.*",
|
||||
"whitelisted/pkg-component2": "1.*",
|
||||
"whitelisted/pkg-component3": "1.0.0",
|
||||
"allowed/pkg-component1": "1.*",
|
||||
"allowed/pkg-component2": "1.*",
|
||||
"allowed/pkg-component3": "1.0.0",
|
||||
"unrelated/pkg": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0", "require": { "whitelisted/pkg-component2": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component3", "version": "1.0.0", "require": { "whitelisted/pkg-component4": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component4", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component5", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0", "require": { "allowed/pkg-component2": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component3", "version": "1.0.0", "require": { "allowed/pkg-component4": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component4", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component5", "version": "1.0.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -51,11 +51,11 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
{
|
||||
"packages": [
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0", "require": { "whitelisted/pkg-component2": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component3", "version": "1.0.0", "require": { "whitelisted/pkg-component4": "1.0.0" } },
|
||||
{ "name": "whitelisted/pkg-component4", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component5", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0", "require": { "allowed/pkg-component2": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component3", "version": "1.0.0", "require": { "allowed/pkg-component4": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component4", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component5", "version": "1.0.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -70,8 +70,8 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg-* foobar --with-dependencies
|
||||
update allowed/pkg-* foobar --with-dependencies
|
||||
--EXPECT--
|
||||
Upgrading dependency/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component2 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component1 (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates those packages matching the pattern
|
||||
Update with a package allow list only updates those packages matching the pattern
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -8,10 +8,10 @@ Update with a package whitelist only updates those packages matching the pattern
|
|||
"package": [
|
||||
{ "name": "fixed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.1.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -23,16 +23,16 @@ Update with a package whitelist only updates those packages matching the pattern
|
|||
],
|
||||
"require": {
|
||||
"fixed/pkg": "1.*",
|
||||
"whitelisted/pkg-component1": "1.*",
|
||||
"whitelisted/pkg-component2": "1.*",
|
||||
"allowed/pkg-component1": "1.*",
|
||||
"allowed/pkg-component2": "1.*",
|
||||
"unrelated/pkg": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -41,8 +41,8 @@ Update with a package whitelist only updates those packages matching the pattern
|
|||
{
|
||||
"packages": [
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg-component1", "version": "1.0.0" },
|
||||
{ "name": "allowed/pkg-component2", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -55,7 +55,7 @@ Update with a package whitelist only updates those packages matching the pattern
|
|||
"prefer-lowest": false
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg-*
|
||||
update allowed/pkg-*
|
||||
--EXPECT--
|
||||
Upgrading whitelisted/pkg-component1 (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg-component2 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component1 (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg-component2 (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates those corresponding to the pattern
|
||||
Update with a package allow list only updates those corresponding to the pattern
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
--TEST--
|
||||
Update with a package whitelist removes unused packages
|
||||
Update with a package allow list removes unused packages
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
{
|
||||
"type": "package",
|
||||
"package": [
|
||||
{ "name": "whitelisted/pkg", "version": "1.1.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "fixed/dependency": "1.0.0", "old/dependency": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.1.0" },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "fixed/dependency": "1.0.0", "old/dependency": "1.0.0" } },
|
||||
{ "name": "fixed/dependency", "version": "1.1.0" },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0" },
|
||||
{ "name": "old/dependency", "version": "1.0.0" }
|
||||
|
@ -15,20 +15,20 @@ Update with a package whitelist removes unused packages
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"whitelisted/pkg": "1.*",
|
||||
"allowed/pkg": "1.*",
|
||||
"fixed/dependency": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0" },
|
||||
{ "name": "old/dependency", "version": "1.0.0" }
|
||||
]
|
||||
--LOCK--
|
||||
{
|
||||
"packages": [
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "old/dependency": "1.0.0", "fixed/dependency": "1.0.0" } },
|
||||
{ "name": "fixed/dependency", "version": "1.0.0" },
|
||||
{ "name": "old/dependency", "version": "1.0.0" }
|
||||
],
|
||||
|
@ -42,7 +42,7 @@ Update with a package whitelist removes unused packages
|
|||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update --with-dependencies whitelisted/pkg
|
||||
update --with-dependencies allowed/pkg
|
||||
--EXPECT--
|
||||
Removing old/dependency (1.0.0)
|
||||
Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates those packages and their dependencies listed as command arguments
|
||||
Update with a package allow list only updates those packages and their dependencies listed as command arguments
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -8,8 +8,8 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
"package": [
|
||||
{ "name": "fixed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0" } },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -21,14 +21,14 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
],
|
||||
"require": {
|
||||
"fixed/pkg": "1.*",
|
||||
"whitelisted/pkg": "1.*",
|
||||
"allowed/pkg": "1.*",
|
||||
"unrelated/pkg": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -37,7 +37,7 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
{
|
||||
"packages": [
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -50,7 +50,7 @@ Update with a package whitelist only updates those packages and their dependenci
|
|||
"prefer-lowest": false
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg --with-dependencies
|
||||
update allowed/pkg --with-dependencies
|
||||
--EXPECT--
|
||||
Upgrading dependency/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates whitelisted packages if no dependency conflicts
|
||||
Update with a package allow list only updates allowed packages if no dependency conflicts
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -8,8 +8,8 @@ Update with a package whitelist only updates whitelisted packages if no dependen
|
|||
"package": [
|
||||
{ "name": "fixed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0" } },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.1.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -21,14 +21,14 @@ Update with a package whitelist only updates whitelisted packages if no dependen
|
|||
],
|
||||
"require": {
|
||||
"fixed/pkg": "1.*",
|
||||
"whitelisted/pkg": "1.*",
|
||||
"allowed/pkg": "1.*",
|
||||
"unrelated/pkg": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -37,7 +37,7 @@ Update with a package whitelist only updates whitelisted packages if no dependen
|
|||
{
|
||||
"packages": [
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.0.0" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -50,5 +50,5 @@ Update with a package whitelist only updates whitelisted packages if no dependen
|
|||
"prefer-lowest": false
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg
|
||||
update allowed/pkg
|
||||
--EXPECT--
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
--TEST--
|
||||
Update with a package whitelist only updates those packages listed as command arguments
|
||||
Update with a package allow list only updates those packages listed as command arguments
|
||||
--COMPOSER--
|
||||
{
|
||||
"repositories": [
|
||||
|
@ -8,8 +8,8 @@ Update with a package whitelist only updates those packages listed as command ar
|
|||
"package": [
|
||||
{ "name": "fixed/pkg", "version": "1.1.0" },
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.1.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.1.0" },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.1.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
|
@ -21,14 +21,14 @@ Update with a package whitelist only updates those packages listed as command ar
|
|||
],
|
||||
"require": {
|
||||
"fixed/pkg": "1.*",
|
||||
"whitelisted/pkg": "1.*",
|
||||
"allowed/pkg": "1.*",
|
||||
"unrelated/pkg": "1.*"
|
||||
}
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -37,7 +37,7 @@ Update with a package whitelist only updates those packages listed as command ar
|
|||
{
|
||||
"packages": [
|
||||
{ "name": "fixed/pkg", "version": "1.0.0" },
|
||||
{ "name": "whitelisted/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "allowed/pkg", "version": "1.0.0", "require": { "dependency/pkg": "1.*" } },
|
||||
{ "name": "dependency/pkg", "version": "1.0.0" },
|
||||
{ "name": "unrelated/pkg", "version": "1.0.0", "require": { "unrelated/pkg-dependency": "1.*" } },
|
||||
{ "name": "unrelated/pkg-dependency", "version": "1.0.0" }
|
||||
|
@ -52,6 +52,6 @@ Update with a package whitelist only updates those packages listed as command ar
|
|||
"platform-dev": []
|
||||
}
|
||||
--RUN--
|
||||
update whitelisted/pkg
|
||||
update allowed/pkg
|
||||
--EXPECT--
|
||||
Upgrading whitelisted/pkg (1.0.0 => 1.1.0)
|
||||
Upgrading allowed/pkg (1.0.0 => 1.1.0)
|
||||
|
|
|
@ -3,10 +3,10 @@ Update updates URLs for updated packages if they have changed
|
|||
|
||||
a/a is dev and gets everything updated as it updates to a new ref
|
||||
b/b is a tag and gets everything updated by updating the package URL directly
|
||||
c/c is a tag and not whitelisted and remains unchanged
|
||||
c/c is a tag and not allowlisted and remains unchanged
|
||||
d/d is dev but with a #ref so it should get URL updated but not the reference
|
||||
e/e is dev and newly installed with a #ref so it should get the correct URL but with the #111 ref
|
||||
f/f is dev but not whitelisted and remains unchanged
|
||||
f/f is dev but not allowlisted and remains unchanged
|
||||
g/g is dev and installed in a different ref than the #ref, so it gets updated and gets the new URL but not the new ref
|
||||
--COMPOSER--
|
||||
{
|
||||
|
@ -17,7 +17,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
{
|
||||
"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/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
|
@ -32,23 +33,27 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
{
|
||||
"name": "d/d", "version": "dev-master",
|
||||
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/d/newd", "type": "git" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/d/newd/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/d/newd/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "e/e", "version": "dev-master",
|
||||
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/e/newe", "type": "git" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/e/newe/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/e/newe/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "f/f", "version": "dev-master",
|
||||
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/f/newf", "type": "git" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/f/newf/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"transport-options": { "foo": "bar2" }
|
||||
"transport-options": { "foo": "bar2" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "g/g", "version": "dev-master",
|
||||
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/g/newg", "type": "git" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/g/newg/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/g/newg/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -68,7 +73,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/a", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" }
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
|
@ -83,19 +89,22 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
{
|
||||
"name": "d/d", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/d", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" }
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "f/f", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"transport-options": { "foo": "bar" }
|
||||
"transport-options": { "foo": "bar" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "g/g", "version": "dev-master",
|
||||
"source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/g", "type": "git" },
|
||||
"dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip", "shasum": "oldsum" },
|
||||
"transport-options": { "foo": "bar" }
|
||||
"transport-options": { "foo": "bar" },
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
--LOCK--
|
||||
|
@ -105,7 +114,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
"name": "a/a", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/a/a", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
|
@ -123,21 +133,24 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
"name": "d/d", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/d", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "f/f", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library",
|
||||
"transport-options": { "foo": "bar" }
|
||||
"transport-options": { "foo": "bar" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "g/g", "version": "dev-master",
|
||||
"source": { "reference": "0000000000000000000000000000000000000000", "url": "https://github.com/g/g", "type": "git" },
|
||||
"dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library",
|
||||
"transport-options": { "foo": "bar" }
|
||||
"transport-options": { "foo": "bar" },
|
||||
"default-branch": true
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
@ -156,7 +169,8 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
"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/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
|
@ -174,26 +188,30 @@ g/g is dev and installed in a different ref than the #ref, so it gets updated an
|
|||
"name": "d/d", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/d/newd", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"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/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "f/f", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/f/f", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library",
|
||||
"transport-options": { "foo": "bar" }
|
||||
"transport-options": { "foo": "bar" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"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/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
|
@ -7,36 +7,39 @@ Updating a dev package to its latest ref should pick up new dependencies
|
|||
"type": "package",
|
||||
"package": [
|
||||
{
|
||||
"name": "a/devpackage", "version": "dev-master",
|
||||
"name": "a/devpackage", "version": "dev-main",
|
||||
"source": { "reference": "newref", "url": "", "type": "git" },
|
||||
"require": {
|
||||
"a/dependency": "*"
|
||||
}
|
||||
},
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/dependency", "version": "dev-master",
|
||||
"name": "a/dependency", "version": "dev-main",
|
||||
"source": { "reference": "ref", "url": "", "type": "git" },
|
||||
"require": {}
|
||||
"require": {},
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"a/devpackage": "dev-master"
|
||||
"a/devpackage": "dev-main"
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
--INSTALLED--
|
||||
[
|
||||
{
|
||||
"name": "a/devpackage", "version": "dev-master",
|
||||
"name": "a/devpackage", "version": "dev-main",
|
||||
"source": { "reference": "oldref", "url": "", "type": "git" },
|
||||
"require": {}
|
||||
"require": {},
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
--RUN--
|
||||
update
|
||||
--EXPECT--
|
||||
Installing a/dependency (dev-master ref)
|
||||
Marking a/dependency (9999999-dev ref) as installed, alias of a/dependency (dev-master ref)
|
||||
Upgrading a/devpackage (dev-master oldref => dev-master newref)
|
||||
Installing a/dependency (dev-main ref)
|
||||
Marking a/dependency (9999999-dev ref) as installed, alias of a/dependency (dev-main ref)
|
||||
Upgrading a/devpackage (dev-main oldref => dev-main newref)
|
||||
|
|
|
@ -8,7 +8,8 @@ Downgrading from unstable to more stable package should work even if already ins
|
|||
"package": [
|
||||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" }
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/a", "version": "1.0.0",
|
||||
|
@ -17,7 +18,8 @@ Downgrading from unstable to more stable package should work even if already ins
|
|||
},
|
||||
{
|
||||
"name": "b/b", "version": "dev-master",
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" }
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "1.0.0",
|
||||
|
@ -36,11 +38,13 @@ Downgrading from unstable to more stable package should work even if already ins
|
|||
[
|
||||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" }
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" },
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "dev-master",
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" }
|
||||
"source": { "reference": "abcd", "url": "", "type": "git" },
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
--RUN--
|
||||
|
|
|
@ -8,7 +8,8 @@ Update to a state without dependency works well from locked with dependency
|
|||
[
|
||||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"source": { "reference": "1234", "type": "git", "url": "" }
|
||||
"source": { "reference": "1234", "type": "git", "url": "" },
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
--LOCK--
|
||||
|
@ -17,7 +18,8 @@ Update to a state without dependency works well from locked with dependency
|
|||
{
|
||||
"name": "a/a", "version": "dev-master",
|
||||
"source": { "reference": "1234", "type": "git", "url": "" },
|
||||
"type": "library"
|
||||
"type": "library",
|
||||
"default-branch": true
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
See Github issue #6661 ( github.com/composer/composer/issues/6661 ).
|
||||
|
||||
When `--with-all-dependencies` is used, Composer\Installer::whitelistUpdateDependencies should update the dependencies of all whitelisted packages, even if the dependency is a root requirement.
|
||||
When `--with-all-dependencies` is used, Composer should update the dependencies of all allowed packages, even if the dependency is a root requirement.
|
||||
|
||||
--COMPOSER--
|
||||
{
|
||||
|
|
|
@ -13,7 +13,8 @@ Installing locked dev packages should remove old dependencies
|
|||
{
|
||||
"name": "a/devpackage", "version": "dev-master",
|
||||
"source": { "reference": "newref", "url": "", "type": "git" },
|
||||
"require": {}
|
||||
"require": {},
|
||||
"default-branch": true
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
@ -30,12 +31,14 @@ Installing locked dev packages should remove old dependencies
|
|||
"source": { "reference": "oldref", "url": "", "type": "git" },
|
||||
"require": {
|
||||
"a/dependency": "*"
|
||||
}
|
||||
},
|
||||
"default-branch": true
|
||||
},
|
||||
{
|
||||
"name": "a/dependency", "version": "dev-master",
|
||||
"source": { "reference": "ref", "url": "", "type": "git" },
|
||||
"require": {}
|
||||
"require": {},
|
||||
"default-branch": true
|
||||
}
|
||||
]
|
||||
--RUN--
|
||||
|
|
|
@ -14,7 +14,10 @@ namespace Composer\Test;
|
|||
|
||||
use Composer\DependencyResolver\Request;
|
||||
use Composer\Installer;
|
||||
use Composer\Console\Application;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Composer\IO\BufferIO;
|
||||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\Dumper\ArrayDumper;
|
||||
|
@ -263,7 +266,12 @@ class InstallerTest extends TestCase
|
|||
$installer = Installer::create($io, $composer);
|
||||
|
||||
$application = new Application;
|
||||
$application->get('install')->setCode(function ($input, $output) use ($installer) {
|
||||
$install = new Command('install');
|
||||
$install->addOption('ignore-platform-reqs', null, InputOption::VALUE_NONE);
|
||||
$install->addOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY);
|
||||
$install->addOption('no-dev', null, InputOption::VALUE_NONE);
|
||||
$install->addOption('dry-run', null, InputOption::VALUE_NONE);
|
||||
$install->setCode(function ($input, $output) use ($installer) {
|
||||
$ignorePlatformReqs = $input->getOption('ignore-platform-reqs') ?: ($input->getOption('ignore-platform-req') ?: false);
|
||||
|
||||
$installer
|
||||
|
@ -273,8 +281,21 @@ class InstallerTest extends TestCase
|
|||
|
||||
return $installer->run();
|
||||
});
|
||||
$application->add($install);
|
||||
|
||||
$application->get('update')->setCode(function ($input, $output) use ($installer) {
|
||||
$update = new Command('update');
|
||||
$update->addOption('ignore-platform-reqs', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('ignore-platform-req', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY);
|
||||
$update->addOption('no-dev', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('no-install', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('dry-run', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('lock', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('with-all-dependencies', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('with-dependencies', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('prefer-stable', null, InputOption::VALUE_NONE);
|
||||
$update->addOption('prefer-lowest', null, InputOption::VALUE_NONE);
|
||||
$update->addArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL);
|
||||
$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);
|
||||
|
@ -305,6 +326,7 @@ class InstallerTest extends TestCase
|
|||
|
||||
return $installer->run();
|
||||
});
|
||||
$application->add($update);
|
||||
|
||||
if (!preg_match('{^(install|update)\b}', $run)) {
|
||||
throw new \UnexpectedValueException('The run command only supports install and update');
|
||||
|
|
|
@ -17,6 +17,8 @@ use Composer\Config;
|
|||
use Composer\Factory;
|
||||
use Composer\Repository\RepositoryManager;
|
||||
use Composer\Repository\WritableRepositoryInterface;
|
||||
use Composer\Package\Version\VersionGuesser;
|
||||
use Composer\Package\Version\VersionParser;
|
||||
use Composer\Package\RootPackageInterface;
|
||||
use Composer\Installer;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
|
@ -39,6 +41,11 @@ class FactoryMock extends Factory
|
|||
return $config;
|
||||
}
|
||||
|
||||
protected function loadRootPackage(RepositoryManager $rm, Config $config, VersionParser $parser, VersionGuesser $guesser, IOInterface $io)
|
||||
{
|
||||
return new \Composer\Package\Loader\RootPackageLoader($rm, $config, $parser, new VersionGuesserMock(), $io);
|
||||
}
|
||||
|
||||
protected function addLocalRepository(IOInterface $io, RepositoryManager $rm, $vendorDir, RootPackageInterface $rootPackage)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Mock;
|
||||
|
||||
use Composer\Package\Version\VersionGuesser;
|
||||
|
||||
class VersionGuesserMock extends VersionGuesser
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function guessVersion(array $packageConfig, $path)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDefaultBranchName($path)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -81,7 +81,7 @@ class BasePackageTest extends TestCase
|
|||
$createPackage = function ($arr) use ($self) {
|
||||
$package = $self->getMockForAbstractClass('\Composer\Package\BasePackage', array(), '', false);
|
||||
$package->expects($self->once())->method('isDev')->will($self->returnValue(true));
|
||||
$package->expects($self->once())->method('getSourceType')->will($self->returnValue('git'));
|
||||
$package->expects($self->any())->method('getSourceType')->will($self->returnValue('git'));
|
||||
$package->expects($self->once())->method('getPrettyVersion')->will($self->returnValue('PrettyVersion'));
|
||||
$package->expects($self->any())->method('getSourceReference')->will($self->returnValue($arr['sourceReference']));
|
||||
|
||||
|
|
|
@ -105,6 +105,8 @@ class RootPackageLoaderTest extends TestCase
|
|||
'pretty_version' => '3.0-dev',
|
||||
'commit' => 'aabbccddee',
|
||||
));
|
||||
$versionGuesser->getDefaultBranchName(Argument::cetera())
|
||||
->willReturn('main');
|
||||
$config = new Config;
|
||||
$config->merge(array('repositories' => array('packagist' => false)));
|
||||
$loader = new RootPackageLoader($manager->reveal(), $config, null, $versionGuesser->reveal());
|
||||
|
@ -113,6 +115,28 @@ class RootPackageLoaderTest extends TestCase
|
|||
$this->assertEquals('3.0-dev', $package->getPrettyVersion());
|
||||
}
|
||||
|
||||
public function testDefaultBranchIsSetForRootPackageInDefaultBranch()
|
||||
{
|
||||
// see #6845
|
||||
$manager = $this->prophesize('\\Composer\\Repository\\RepositoryManager');
|
||||
$versionGuesser = $this->prophesize('\\Composer\\Package\\Version\\VersionGuesser');
|
||||
$versionGuesser->guessVersion(Argument::cetera())
|
||||
->willReturn(array(
|
||||
'name' => 'A',
|
||||
'version' => 'dev-main',
|
||||
'pretty_version' => 'dev-main',
|
||||
'commit' => 'aabbccddee',
|
||||
));
|
||||
$versionGuesser->getDefaultBranchName(Argument::cetera())
|
||||
->willReturn('main');
|
||||
$config = new Config;
|
||||
$config->merge(array('repositories' => array('packagist' => false)));
|
||||
$loader = new RootPackageLoader($manager->reveal(), $config, null, $versionGuesser->reveal());
|
||||
$package = $loader->load(array());
|
||||
|
||||
$this->assertTrue($package->isDefaultBranch());
|
||||
}
|
||||
|
||||
public function testFeatureBranchPrettyVersion()
|
||||
{
|
||||
if (!function_exists('proc_open')) {
|
||||
|
@ -147,6 +171,17 @@ class RootPackageLoaderTest extends TestCase
|
|||
$executor
|
||||
->expects($this->at(1))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git remote show origin', $command);
|
||||
$output = " HEAD branch: master";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$executor
|
||||
->expects($this->at(2))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git rev-list master..latest-production', $command);
|
||||
$output = "";
|
||||
|
|
|
@ -16,6 +16,8 @@ use Composer\Config;
|
|||
use Composer\Package\Version\VersionGuesser;
|
||||
use Composer\Semver\VersionParser;
|
||||
use Composer\Test\TestCase;
|
||||
use Composer\Util\Git as GitUtil;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
|
||||
class VersionGuesserTest extends TestCase
|
||||
{
|
||||
|
@ -30,7 +32,7 @@ class VersionGuesserTest extends TestCase
|
|||
{
|
||||
$branch = 'default';
|
||||
|
||||
$executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor')
|
||||
$executor = $this->getMockBuilder('Composer\\Util\\ProcessExecutor')
|
||||
->setMethods(array('execute'))
|
||||
->disableArgumentCloning()
|
||||
->disableOriginalConstructor()
|
||||
|
@ -40,6 +42,8 @@ class VersionGuesserTest extends TestCase
|
|||
$self = $this;
|
||||
$step = 0;
|
||||
|
||||
GitUtil::getVersion(new ProcessExecutor);
|
||||
|
||||
$executor
|
||||
->expects($this->at($step))
|
||||
->method('execute')
|
||||
|
@ -65,8 +69,8 @@ class VersionGuesserTest extends TestCase
|
|||
$executor
|
||||
->expects($this->at($step))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git log --pretty="%H" -n1 HEAD', $command);
|
||||
->willReturnCallback(function ($command, &$output) use ($self, $executor) {
|
||||
$self->assertEquals('git log --pretty="%H" -n1 HEAD'.GitUtil::getNoShowSignatureFlag($executor), $command);
|
||||
|
||||
return 128;
|
||||
})
|
||||
|
@ -131,6 +135,64 @@ class VersionGuesserTest extends TestCase
|
|||
$this->assertEquals($commitHash, $versionArray['commit']);
|
||||
}
|
||||
|
||||
public function testGuessVersionReadsAndRespectsDefaultBranchAsNonFeatureBranch()
|
||||
{
|
||||
$commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
|
||||
$anotherCommitHash = '13a15d220da53c52eddd5f32ffca64a7b3801bea';
|
||||
|
||||
$executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor')
|
||||
->setMethods(array('execute'))
|
||||
->disableArgumentCloning()
|
||||
->disableOriginalConstructor()
|
||||
->getMock()
|
||||
;
|
||||
|
||||
$self = $this;
|
||||
|
||||
$executor
|
||||
->expects($this->at(0))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self, $commitHash, $anotherCommitHash) {
|
||||
$self->assertEquals('git branch --no-color --no-abbrev -v', $command);
|
||||
$output = " arbitrary $commitHash Commit message\n* current $anotherCommitHash Another message\n";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$executor
|
||||
->expects($this->at(1))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git remote show origin', $command);
|
||||
$output = " HEAD branch: arbitrary\r\n";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$executor
|
||||
->expects($this->at(2))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) {
|
||||
$self->assertEquals('git rev-list arbitrary..current', $command);
|
||||
$output = "$anotherCommitHash\n";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$config = new Config;
|
||||
$config->merge(array('repositories' => array('packagist' => false)));
|
||||
$guesser = new VersionGuesser($config, $executor, new VersionParser());
|
||||
$versionArray = $guesser->guessVersion(array('version' => 'self.version'), 'dummy/path');
|
||||
|
||||
$this->assertEquals("dev-arbitrary", $versionArray['version']);
|
||||
$this->assertEquals($anotherCommitHash, $versionArray['commit']);
|
||||
$this->assertEquals("dev-current", $versionArray['feature_version']);
|
||||
$this->assertEquals("dev-current", $versionArray['feature_pretty_version']);
|
||||
}
|
||||
|
||||
public function testGuessVersionReadsAndRespectsNonFeatureBranchesConfigurationForArbitraryNaming()
|
||||
{
|
||||
$commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea';
|
||||
|
@ -159,6 +221,17 @@ class VersionGuesserTest extends TestCase
|
|||
$executor
|
||||
->expects($this->at(1))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git remote show origin', $command);
|
||||
$output = " HEAD branch: foo\r\n";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$executor
|
||||
->expects($this->at(2))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) {
|
||||
$self->assertEquals('git rev-list arbitrary..current', $command);
|
||||
$output = "$anotherCommitHash\n";
|
||||
|
@ -202,10 +275,19 @@ class VersionGuesserTest extends TestCase
|
|||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$executor
|
||||
->expects($this->at(1))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git remote show origin', $command);
|
||||
$output = " HEAD branch: foo\r\n";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
$executor
|
||||
->expects($this->at(2))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output, $path) use ($self, $anotherCommitHash) {
|
||||
$self->assertEquals('git rev-list latest-testing..current', $command);
|
||||
$output = "$anotherCommitHash\n";
|
||||
|
@ -374,10 +456,19 @@ class VersionGuesserTest extends TestCase
|
|||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$executor
|
||||
->expects($this->at(1))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git remote show origin', $command);
|
||||
$output = " HEAD branch: foo\r\n";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
$executor
|
||||
->expects($this->at(2))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git describe --exact-match --tags', $command);
|
||||
$output = "v2.0.5-alpha2";
|
||||
|
@ -415,10 +506,19 @@ class VersionGuesserTest extends TestCase
|
|||
return 0;
|
||||
})
|
||||
;
|
||||
|
||||
$executor
|
||||
->expects($this->at(1))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git remote show origin', $command);
|
||||
$output = " HEAD branch: foo\r\n";
|
||||
|
||||
return 0;
|
||||
})
|
||||
;
|
||||
$executor
|
||||
->expects($this->at(2))
|
||||
->method('execute')
|
||||
->willReturnCallback(function ($command, &$output) use ($self) {
|
||||
$self->assertEquals('git describe --exact-match --tags', $command);
|
||||
$output = '1.0.0';
|
||||
|
|
|
@ -189,16 +189,19 @@ class ComposerRepositoryTest extends TestCase
|
|||
->getMock();
|
||||
|
||||
$httpDownloader->expects($this->at(0))
|
||||
->method('enableAsync');
|
||||
|
||||
$httpDownloader->expects($this->at(1))
|
||||
->method('get')
|
||||
->with($url = 'http://example.org/packages.json')
|
||||
->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array('search' => '/search.json?q=%query%&type=%type%'))));
|
||||
|
||||
$httpDownloader->expects($this->at(1))
|
||||
$httpDownloader->expects($this->at(2))
|
||||
->method('get')
|
||||
->with($url = 'http://example.org/search.json?q=foo&type=composer-plugin')
|
||||
->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode($result)));
|
||||
|
||||
$httpDownloader->expects($this->at(2))
|
||||
$httpDownloader->expects($this->at(3))
|
||||
->method('get')
|
||||
->with($url = 'http://example.org/search.json?q=foo&type=library')
|
||||
->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array())));
|
||||
|
@ -291,6 +294,9 @@ class ComposerRepositoryTest extends TestCase
|
|||
->getMock();
|
||||
|
||||
$httpDownloader->expects($this->at(0))
|
||||
->method('enableAsync');
|
||||
|
||||
$httpDownloader->expects($this->at(1))
|
||||
->method('get')
|
||||
->with($url = 'http://example.org/packages.json')
|
||||
->willReturn(new \Composer\Util\Http\Response(array('url' => $url), 200, array(), json_encode(array(
|
||||
|
|
|
@ -12,32 +12,25 @@
|
|||
|
||||
namespace Composer\Test\Util;
|
||||
|
||||
use Composer\Config;
|
||||
use Composer\IO\ConsoleIO;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\AuthHelper;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Test\TestCase;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
class RemoteFilesystemTest extends TestCase
|
||||
{
|
||||
private function getConfigMock()
|
||||
{
|
||||
$config = $this->getMockBuilder('Composer\Config')->getMock();
|
||||
$config->expects($this->any())
|
||||
->method('get')
|
||||
->will($this->returnCallback(function ($key) {
|
||||
if ($key === 'github-domains' || $key === 'gitlab-domains') {
|
||||
return array();
|
||||
}
|
||||
}));
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function testGetOptionsForUrl()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$io = $this->getIOInterfaceMock();
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('hasAuthentication')
|
||||
->will($this->returnValue(false))
|
||||
->willReturn(false)
|
||||
;
|
||||
|
||||
$res = $this->callGetOptionsForUrl($io, array('http://example.org', array()));
|
||||
|
@ -46,16 +39,16 @@ class RemoteFilesystemTest extends TestCase
|
|||
|
||||
public function testGetOptionsForUrlWithAuthorization()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$io = $this->getIOInterfaceMock();
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('hasAuthentication')
|
||||
->will($this->returnValue(true))
|
||||
->willReturn(true)
|
||||
;
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('getAuthentication')
|
||||
->will($this->returnValue(array('username' => 'login', 'password' => 'password')))
|
||||
->willReturn(array('username' => 'login', 'password' => 'password'))
|
||||
;
|
||||
|
||||
$options = $this->callGetOptionsForUrl($io, array('http://example.org', array()));
|
||||
|
@ -71,17 +64,17 @@ class RemoteFilesystemTest extends TestCase
|
|||
|
||||
public function testGetOptionsForUrlWithStreamOptions()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$io = $this->getIOInterfaceMock();
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('hasAuthentication')
|
||||
->will($this->returnValue(true))
|
||||
->willReturn(true)
|
||||
;
|
||||
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('getAuthentication')
|
||||
->will($this->returnValue(array('username' => null, 'password' => null)))
|
||||
->willReturn(array('username' => null, 'password' => null))
|
||||
;
|
||||
|
||||
$streamOptions = array('ssl' => array(
|
||||
|
@ -94,17 +87,17 @@ class RemoteFilesystemTest extends TestCase
|
|||
|
||||
public function testGetOptionsForUrlWithCallOptionsKeepsHeader()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$io = $this->getIOInterfaceMock();
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('hasAuthentication')
|
||||
->will($this->returnValue(true))
|
||||
->willReturn(true)
|
||||
;
|
||||
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('getAuthentication')
|
||||
->will($this->returnValue(array('username' => null, 'password' => null)))
|
||||
->willReturn(array('username' => null, 'password' => null))
|
||||
;
|
||||
|
||||
$streamOptions = array('http' => array(
|
||||
|
@ -127,14 +120,14 @@ class RemoteFilesystemTest extends TestCase
|
|||
|
||||
public function testCallbackGetFileSize()
|
||||
{
|
||||
$fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
|
||||
$fs = new RemoteFilesystem($this->getIOInterfaceMock(), $this->getConfigMock());
|
||||
$this->callCallbackGet($fs, STREAM_NOTIFY_FILE_SIZE_IS, 0, '', 0, 0, 20);
|
||||
$this->assertAttributeEquals(20, 'bytesMax', $fs);
|
||||
}
|
||||
|
||||
public function testCallbackGetNotifyProgress()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$io = $this->getIOInterfaceMock();
|
||||
$io
|
||||
->expects($this->once())
|
||||
->method('overwriteError')
|
||||
|
@ -150,21 +143,21 @@ class RemoteFilesystemTest extends TestCase
|
|||
|
||||
public function testCallbackGetPassesThrough404()
|
||||
{
|
||||
$fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
|
||||
$fs = new RemoteFilesystem($this->getIOInterfaceMock(), $this->getConfigMock());
|
||||
|
||||
$this->assertNull($this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, 'HTTP/1.1 404 Not Found', 404, 0, 0));
|
||||
}
|
||||
|
||||
public function testGetContents()
|
||||
{
|
||||
$fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
|
||||
$fs = new RemoteFilesystem($this->getIOInterfaceMock(), $this->getConfigMock());
|
||||
|
||||
$this->assertContains('testGetContents', $fs->getContents('http://example.org', 'file://'.__FILE__));
|
||||
}
|
||||
|
||||
public function testCopy()
|
||||
{
|
||||
$fs = new RemoteFilesystem($this->getMockBuilder('Composer\IO\IOInterface')->getMock(), $this->getConfigMock());
|
||||
$fs = new RemoteFilesystem($this->getIOInterfaceMock(), $this->getConfigMock());
|
||||
|
||||
$file = tempnam(sys_get_temp_dir(), 'c');
|
||||
$this->assertTrue($fs->copy('http://example.org', 'file://'.__FILE__, $file));
|
||||
|
@ -173,17 +166,96 @@ class RemoteFilesystemTest extends TestCase
|
|||
unlink($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Composer\Downloader\TransportException
|
||||
*/
|
||||
public function testCopyWithNoRetryOnFailure()
|
||||
{
|
||||
$fs = $this->getRemoteFilesystemWithMockedMethods(array('getRemoteContents'));
|
||||
|
||||
$fs->expects($this->once())->method('getRemoteContents')
|
||||
->willReturnCallback(function ($originUrl, $fileUrl, $ctx, &$http_response_header) {
|
||||
|
||||
$http_response_header = array('http/1.1 401 unauthorized');
|
||||
|
||||
return '';
|
||||
|
||||
});
|
||||
|
||||
|
||||
$file = tempnam(sys_get_temp_dir(), 'z');
|
||||
unlink($file);
|
||||
|
||||
$fs->copy(
|
||||
'http://example.org',
|
||||
'file://' . __FILE__,
|
||||
$file,
|
||||
true,
|
||||
array('retry-auth-failure' => false)
|
||||
);
|
||||
}
|
||||
|
||||
public function testCopyWithSuccessOnRetry()
|
||||
{
|
||||
$authHelper = $this->getAuthHelperWithMockedMethods(array('promptAuthIfNeeded'));
|
||||
$fs = $this->getRemoteFilesystemWithMockedMethods(array('getRemoteContents'), $authHelper);
|
||||
|
||||
$authHelper->expects($this->once())
|
||||
->method('promptAuthIfNeeded')
|
||||
->willReturn(array(
|
||||
'storeAuth' => true,
|
||||
'retry' => true
|
||||
));
|
||||
|
||||
$fs->expects($this->at(0))
|
||||
->method('getRemoteContents')
|
||||
->willReturnCallback(function ($originUrl, $fileUrl, $ctx, &$http_response_header) {
|
||||
|
||||
$http_response_header = array('http/1.1 401 unauthorized');
|
||||
|
||||
return '';
|
||||
|
||||
});
|
||||
|
||||
$fs->expects($this->at(1))
|
||||
->method('getRemoteContents')
|
||||
->willReturnCallback(function ($originUrl, $fileUrl, $ctx, &$http_response_header) {
|
||||
|
||||
$http_response_header = array('http/1.1 200 OK');
|
||||
|
||||
return '<?php $copied = "Copied"; ';
|
||||
|
||||
});
|
||||
|
||||
|
||||
$file = tempnam(sys_get_temp_dir(), 'z');
|
||||
|
||||
$copyResult = $fs->copy(
|
||||
'http://example.org',
|
||||
'file://' . __FILE__,
|
||||
$file,
|
||||
true,
|
||||
array('retry-auth-failure' => true)
|
||||
);
|
||||
|
||||
$this->assertTrue($copyResult);
|
||||
$this->assertFileExists($file);
|
||||
$this->assertContains('Copied', file_get_contents($file));
|
||||
|
||||
unlink($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group TLS
|
||||
*/
|
||||
public function testGetOptionsForUrlCreatesSecureTlsDefaults()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$io = $this->getIOInterfaceMock();
|
||||
|
||||
$res = $this->callGetOptionsForUrl($io, array('example.org', array('ssl' => array('cafile' => '/some/path/file.crt'))), array(), 'http://www.example.org');
|
||||
|
||||
$this->assertTrue(isset($res['ssl']['ciphers']));
|
||||
$this->assertRegExp("|!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA|", $res['ssl']['ciphers']);
|
||||
$this->assertRegExp('|!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA|', $res['ssl']['ciphers']);
|
||||
$this->assertTrue($res['ssl']['verify_peer']);
|
||||
$this->assertTrue($res['ssl']['SNI_enabled']);
|
||||
$this->assertEquals(7, $res['ssl']['verify_depth']);
|
||||
|
@ -220,6 +292,7 @@ class RemoteFilesystemTest extends TestCase
|
|||
*/
|
||||
public function testBitBucketPublicDownload($url, $contents)
|
||||
{
|
||||
/** @var ConsoleIO $io */
|
||||
$io = $this
|
||||
->getMockBuilder('Composer\IO\ConsoleIO')
|
||||
->disableOriginalConstructor()
|
||||
|
@ -242,6 +315,7 @@ class RemoteFilesystemTest extends TestCase
|
|||
*/
|
||||
public function testBitBucketPublicDownloadWithAuthConfigured($url, $contents)
|
||||
{
|
||||
/** @var MockObject|ConsoleIO $io */
|
||||
$io = $this
|
||||
->getMockBuilder('Composer\IO\ConsoleIO')
|
||||
->disableOriginalConstructor()
|
||||
|
@ -249,13 +323,12 @@ class RemoteFilesystemTest extends TestCase
|
|||
|
||||
$domains = array();
|
||||
$io
|
||||
->expects($this->any())
|
||||
->method('hasAuthentication')
|
||||
->will($this->returnCallback(function ($arg) use (&$domains) {
|
||||
->willReturnCallback(function ($arg) use (&$domains) {
|
||||
$domains[] = $arg;
|
||||
// first time is called with bitbucket.org, then it redirects to bbuseruploads.s3.amazonaws.com so next time we have no auth configured
|
||||
return $arg === 'bitbucket.org';
|
||||
}));
|
||||
});
|
||||
$io
|
||||
->expects($this->at(1))
|
||||
->method('getAuthentication')
|
||||
|
@ -275,11 +348,11 @@ class RemoteFilesystemTest extends TestCase
|
|||
$this->assertEquals(array('bitbucket.org', 'bbuseruploads.s3.amazonaws.com'), $domains);
|
||||
}
|
||||
|
||||
protected function callGetOptionsForUrl($io, array $args = array(), array $options = array(), $fileUrl = '')
|
||||
private function callGetOptionsForUrl($io, array $args = array(), array $options = array(), $fileUrl = '')
|
||||
{
|
||||
$fs = new RemoteFilesystem($io, $this->getConfigMock(), $options);
|
||||
$ref = new \ReflectionMethod($fs, 'getOptionsForUrl');
|
||||
$prop = new \ReflectionProperty($fs, 'fileUrl');
|
||||
$ref = new ReflectionMethod($fs, 'getOptionsForUrl');
|
||||
$prop = new ReflectionProperty($fs, 'fileUrl');
|
||||
$ref->setAccessible(true);
|
||||
$prop->setAccessible(true);
|
||||
|
||||
|
@ -288,17 +361,80 @@ class RemoteFilesystemTest extends TestCase
|
|||
return $ref->invokeArgs($fs, $args);
|
||||
}
|
||||
|
||||
protected function callCallbackGet(RemoteFilesystem $fs, $notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax)
|
||||
/**
|
||||
* @return MockObject|Config
|
||||
*/
|
||||
private function getConfigMock()
|
||||
{
|
||||
$ref = new \ReflectionMethod($fs, 'callbackGet');
|
||||
$config = $this->getMockBuilder('Composer\Config')->getMock();
|
||||
$config
|
||||
->method('get')
|
||||
->willReturnCallback(function ($key) {
|
||||
if ($key === 'github-domains' || $key === 'gitlab-domains') {
|
||||
return array();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function callCallbackGet(RemoteFilesystem $fs, $notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax)
|
||||
{
|
||||
$ref = new ReflectionMethod($fs, 'callbackGet');
|
||||
$ref->setAccessible(true);
|
||||
$ref->invoke($fs, $notificationCode, $severity, $message, $messageCode, $bytesTransferred, $bytesMax);
|
||||
}
|
||||
|
||||
protected function setAttribute($object, $attribute, $value)
|
||||
private function setAttribute($object, $attribute, $value)
|
||||
{
|
||||
$attr = new \ReflectionProperty($object, $attribute);
|
||||
$attr = new ReflectionProperty($object, $attribute);
|
||||
$attr->setAccessible(true);
|
||||
$attr->setValue($object, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MockObject|IOInterface
|
||||
*/
|
||||
private function getIOInterfaceMock()
|
||||
{
|
||||
return $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $mockedMethods
|
||||
* @param AuthHelper $authHelper
|
||||
*
|
||||
* @return RemoteFilesystem|MockObject
|
||||
*/
|
||||
private function getRemoteFilesystemWithMockedMethods(array $mockedMethods, AuthHelper $authHelper = null)
|
||||
{
|
||||
return $this->getMockBuilder('Composer\Util\RemoteFilesystem')
|
||||
->setConstructorArgs(array(
|
||||
$this->getIOInterfaceMock(),
|
||||
$this->getConfigMock(),
|
||||
array(),
|
||||
false,
|
||||
$authHelper
|
||||
))
|
||||
->setMethods($mockedMethods)
|
||||
->getMock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $mockedMethods
|
||||
*
|
||||
* @return AuthHelper|MockObject
|
||||
*/
|
||||
private function getAuthHelperWithMockedMethods(array $mockedMethods)
|
||||
{
|
||||
return $this->getMockBuilder('Composer\Util\AuthHelper')
|
||||
->setConstructorArgs(array(
|
||||
$this->getIOInterfaceMock(),
|
||||
$this->getConfigMock()
|
||||
))
|
||||
->setMethods($mockedMethods)
|
||||
->getMock();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue