Merge remote-tracking branch 'github-composer/2.0' into solve-without-installed
* github-composer/2.0: (48 commits) Fix missing use/undefined var Split up steps on VCS downloaders to allow doing network operations before touching the filesystem on GitDownloader, fixes #7903 Fix use statement Deduplicate findHeaderValue code Add install-path to the installed.json for every package, fixes #2174, closes #2424 Remove unnecessary config from phpstan Make sure the directory exists and will not block installation later when downloading Avoid wiping the whole target package if download of the new one fails, refs #7929 Only empty dir before actually installing packages, fixes #7929 Improve output when installing packages Show best possible version in diagnose command Remove extra arg Allow path repos to point to their own source dir as install target, resulting in noop, fixes #8254 Fix use of decodeJson Fix update mirrors to also update transport-options, fixes #7672 Fix updating or URLs to include dist type and shasum, fixes #8216 Fix origin computation Improve handling of non-standard ports for GitLab and GitHub installs, fixes #8173 Load packages from the lock file for check-platform-reqs if no dependencies have been installed yet, fixes #8058 Fix error_handler return type declaration ...pull/7936/head
commit
f5e18250e6
|
@ -30,9 +30,11 @@ matrix:
|
|||
env:
|
||||
- deps=high
|
||||
- php: nightly
|
||||
- php: 7.4snapshot
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
- php: 7.4snapshot
|
||||
|
||||
before_install:
|
||||
# disable xdebug if available
|
||||
|
@ -62,7 +64,7 @@ script:
|
|||
- ls -d tests/Composer/Test/* | grep -v TestCase.php | parallel --gnu --keep-order 'echo "Running {} tests"; ./vendor/bin/phpunit -c tests/complete.phpunit.xml --colors=always {} || (echo -e "\e[41mFAILED\e[0m {}" && exit 1);'
|
||||
# Run PHPStan
|
||||
- if [[ $PHPSTAN == "1" ]]; then
|
||||
composer require --dev phpstan/phpstan-shim:^0.11 --ignore-platform-reqs &&
|
||||
bin/composer require --dev phpstan/phpstan-shim:^0.11 --ignore-platform-reqs &&
|
||||
vendor/bin/phpstan.phar analyse src tests --configuration=phpstan/config.neon --autoload-file=phpstan/autoload.php;
|
||||
fi
|
||||
|
||||
|
|
|
@ -259,6 +259,10 @@ match the platform requirements of the installed packages. This can be used
|
|||
to verify that a production server has all the extensions needed to run a
|
||||
project after installing it for example.
|
||||
|
||||
Unlike update/install, this command will ignore config.platform settings and
|
||||
check the real platform packages so you can be certain you have the required
|
||||
platform dependencies.
|
||||
|
||||
## global
|
||||
|
||||
The global command allows you to run other commands like `install`, `remove`, `require`
|
||||
|
|
|
@ -112,6 +112,19 @@ Note that this will still need to pull and scan all of your VCS repositories
|
|||
because any VCS repository might contain (on any branch) one of the selected
|
||||
packages.
|
||||
|
||||
If you want to scan only the selected package and not all VCS repositories you need
|
||||
to declare a *name* for all your package (this only work on VCS repositories type) :
|
||||
|
||||
```json
|
||||
{
|
||||
"repositories": [
|
||||
{ "name": "company/privaterepo", "type": "vcs", "url": "https://github.com/mycompany/privaterepo" },
|
||||
{ "name": "private/repo", "type": "vcs", "url": "http://svn.example.org/private/repo" },
|
||||
{ "name": "mycompany/privaterepo2", "type": "vcs", "url": "https://github.com/mycompany/privaterepo2" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
If you want to scan only a single repository and update all packages found in
|
||||
it, pass the VCS repository URL as an optional argument:
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ parameters:
|
|||
- '~^Anonymous function has an unused use \$io\.$~'
|
||||
- '~^Anonymous function has an unused use \$cache\.$~'
|
||||
- '~^Anonymous function has an unused use \$path\.$~'
|
||||
- '~^Anonymous function has an unused use \$fileName\.$~'
|
||||
|
||||
# ion cube is not installed
|
||||
- '~^Function ioncube_loader_\w+ not found\.$~'
|
||||
|
|
|
@ -34,6 +34,8 @@ class CheckPlatformReqsCommand extends BaseCommand
|
|||
<<<EOT
|
||||
Checks that your PHP and extensions versions match the platform requirements of the installed packages.
|
||||
|
||||
Unlike update/install, this command will ignore config.platform settings and check the real platform packages so you can be certain you have the required platform dependencies.
|
||||
|
||||
<info>php composer.phar check-platform-reqs</info>
|
||||
|
||||
EOT
|
||||
|
@ -49,6 +51,10 @@ EOT
|
|||
$dependencies = $composer->getLocker()->getLockedRepository(!$input->getOption('no-dev'))->getPackages();
|
||||
} else {
|
||||
$dependencies = $composer->getRepositoryManager()->getLocalRepository()->getPackages();
|
||||
// fallback to lockfile if installed repo is empty
|
||||
if (!$dependencies) {
|
||||
$dependencies = $composer->getLocker()->getLockedRepository(true)->getPackages();
|
||||
}
|
||||
$requires += $composer->getPackage()->getDevRequires();
|
||||
}
|
||||
foreach ($requires as $require => $link) {
|
||||
|
|
|
@ -156,7 +156,7 @@ EOT
|
|||
$this->outputResult($this->checkVersion($config));
|
||||
}
|
||||
|
||||
$io->write(sprintf('Composer version: <comment>%s</comment>', Composer::VERSION));
|
||||
$io->write(sprintf('Composer version: <comment>%s</comment>', Composer::getVersion()));
|
||||
|
||||
$platformOverrides = $config->get('platform') ?: array();
|
||||
$platformRepo = new PlatformRepository(array(), $platformOverrides);
|
||||
|
@ -254,7 +254,7 @@ EOT
|
|||
|
||||
$protocol = extension_loaded('openssl') ? 'https' : 'http';
|
||||
try {
|
||||
$json = $this->httpDownloader->get($protocol . '://repo.packagist.org/packages.json')->parseJson();
|
||||
$json = $this->httpDownloader->get($protocol . '://repo.packagist.org/packages.json')->decodeJson();
|
||||
$hash = reset($json['provider-includes']);
|
||||
$hash = $hash['sha256'];
|
||||
$path = str_replace('%hash%', $hash, key($json['provider-includes']));
|
||||
|
@ -375,7 +375,7 @@ EOT
|
|||
}
|
||||
|
||||
$url = $domain === 'github.com' ? 'https://api.'.$domain.'/rate_limit' : 'https://'.$domain.'/api/rate_limit';
|
||||
$data = $this->httpDownloader->get($url, array('retry-auth-failure' => false))->parseJson();
|
||||
$data = $this->httpDownloader->get($url, array('retry-auth-failure' => false))->decodeJson();
|
||||
|
||||
return $data['resources']['core'];
|
||||
}
|
||||
|
|
|
@ -168,13 +168,25 @@ EOT
|
|||
if ($repositories) {
|
||||
$config = Factory::createConfig($io);
|
||||
$repos = array(new PlatformRepository);
|
||||
$createDefaultPackagistRepo = true;
|
||||
foreach ($repositories as $repo) {
|
||||
$repos[] = RepositoryFactory::fromString($io, $config, $repo);
|
||||
$repoConfig = RepositoryFactory::configFromString($io, $config, $repo);
|
||||
if (
|
||||
(isset($repoConfig['packagist']) && $repoConfig === array('packagist' => false))
|
||||
|| (isset($repoConfig['packagist.org']) && $repoConfig === array('packagist.org' => false))
|
||||
) {
|
||||
$createDefaultPackagistRepo = false;
|
||||
continue;
|
||||
}
|
||||
$repos[] = RepositoryFactory::createRepo($io, $config, $repoConfig);
|
||||
}
|
||||
|
||||
if ($createDefaultPackagistRepo) {
|
||||
$repos[] = RepositoryFactory::createRepo($io, $config, array(
|
||||
'type' => 'composer',
|
||||
'url' => 'https://repo.packagist.org',
|
||||
));
|
||||
}
|
||||
$repos[] = RepositoryFactory::createRepo($io, $config, array(
|
||||
'type' => 'composer',
|
||||
'url' => 'https://repo.packagist.org',
|
||||
));
|
||||
|
||||
$this->repos = new CompositeRepository($repos);
|
||||
unset($repos, $config, $repositories);
|
||||
|
|
|
@ -26,6 +26,7 @@ use Composer\Plugin\PluginEvents;
|
|||
use Composer\Repository\CompositeRepository;
|
||||
use Composer\Repository\PlatformRepository;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\Silencer;
|
||||
|
||||
/**
|
||||
* @author Jérémy Romey <jeremy@free-agent.fr>
|
||||
|
@ -103,11 +104,6 @@ EOT
|
|||
|
||||
return 1;
|
||||
}
|
||||
if (!is_writable($this->file)) {
|
||||
$io->writeError('<error>'.$this->file.' is not writable.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (filesize($this->file) === 0) {
|
||||
file_put_contents($this->file, "{\n}\n");
|
||||
|
@ -116,6 +112,14 @@ EOT
|
|||
$this->json = new JsonFile($this->file);
|
||||
$this->composerBackup = file_get_contents($this->json->getPath());
|
||||
|
||||
// check for writability by writing to the file as is_writable can not be trusted on network-mounts
|
||||
// see https://github.com/composer/composer/issues/8231 and https://bugs.php.net/bug.php?id=68926
|
||||
if (!is_writable($this->file) && !Silencer::call('file_put_contents', $this->file, $this->composerBackup)) {
|
||||
$io->writeError('<error>'.$this->file.' is not writable.</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$composer = $this->getComposer(true, $input->getOption('no-plugins'));
|
||||
$repos = $composer->getRepositoryManager()->getRepositories();
|
||||
|
||||
|
@ -141,7 +145,12 @@ EOT
|
|||
|
||||
// validate requirements format
|
||||
$versionParser = new VersionParser();
|
||||
foreach ($requirements as $constraint) {
|
||||
foreach ($requirements as $package => $constraint) {
|
||||
if (strtolower($package) === $composer->getPackage()->getName()) {
|
||||
$io->writeError(sprintf('<error>Root package \'%s\' cannot require itself in its composer.json</error>', $package));
|
||||
|
||||
return 1;
|
||||
}
|
||||
$versionParser->parseConstraints($constraint);
|
||||
}
|
||||
|
||||
|
|
|
@ -379,6 +379,9 @@ class Application extends BaseApplication
|
|||
public function resetComposer()
|
||||
{
|
||||
$this->composer = null;
|
||||
if ($this->getIO() && method_exists($this->getIO(), 'resetAuthentications')) {
|
||||
$this->getIO()->resetAuthentications();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -195,7 +195,7 @@ class RuleSetGenerator
|
|||
}
|
||||
}
|
||||
|
||||
protected function addConflictRules()
|
||||
protected function addConflictRules($ignorePlatformReqs = false)
|
||||
{
|
||||
/** @var PackageInterface $package */
|
||||
foreach ($this->addedPackages as $package) {
|
||||
|
@ -204,6 +204,10 @@ class RuleSetGenerator
|
|||
continue;
|
||||
}
|
||||
|
||||
if ($ignorePlatformReqs && preg_match(PlatformRepository::PLATFORM_PACKAGE_REGEX, $link->getTarget())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var PackageInterface $possibleConflict */
|
||||
foreach ($this->addedPackagesByNames[$link->getTarget()] as $possibleConflict) {
|
||||
$conflictMatch = $this->pool->match($possibleConflict, $link->getTarget(), $link->getConstraint(), true);
|
||||
|
@ -305,7 +309,7 @@ class RuleSetGenerator
|
|||
|
||||
$this->addRulesForRequest($request, $ignorePlatformReqs);
|
||||
|
||||
$this->addConflictRules();
|
||||
$this->addConflictRules($ignorePlatformReqs);
|
||||
|
||||
// Remove references to packages
|
||||
$this->addedPackages = $this->addedPackagesByNames = null;
|
||||
|
|
|
@ -33,16 +33,16 @@ 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>)");
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>): Extracting archive");
|
||||
} else {
|
||||
$this->io->writeError('Extracting archive', false);
|
||||
}
|
||||
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
||||
$temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8);
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
if ($output) {
|
||||
$this->io->writeError(' Extracting archive', true, IOInterface::VERBOSE);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->filesystem->ensureDirectoryExists($temporaryDir);
|
||||
try {
|
||||
|
|
|
@ -165,9 +165,9 @@ class DownloadManager
|
|||
/**
|
||||
* Downloads package into target dir.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
* @param PackageInterface $prevPackage previous package instance in case of updates
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
* @param PackageInterface|null $prevPackage previous package instance in case of updates
|
||||
*
|
||||
* @return PromiseInterface
|
||||
* @throws \InvalidArgumentException if package have no urls to download from
|
||||
|
@ -182,7 +182,7 @@ class DownloadManager
|
|||
$io = $this->io;
|
||||
$self = $this;
|
||||
|
||||
$download = function ($retry = false) use (&$sources, $io, $package, $self, $targetDir, &$download) {
|
||||
$download = function ($retry = false) use (&$sources, $io, $package, $self, $targetDir, &$download, $prevPackage) {
|
||||
$source = array_shift($sources);
|
||||
if ($retry) {
|
||||
$io->writeError(' <warning>Now trying to download from ' . $source . '</warning>');
|
||||
|
@ -214,7 +214,7 @@ class DownloadManager
|
|||
};
|
||||
|
||||
try {
|
||||
$result = $downloader->download($package, $targetDir);
|
||||
$result = $downloader->download($package, $targetDir, $prevPackage);
|
||||
} catch (\Exception $e) {
|
||||
return $handleError($e);
|
||||
}
|
||||
|
@ -232,12 +232,31 @@ class DownloadManager
|
|||
return $download();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an operation execution
|
||||
*
|
||||
* @param string $type one of install/update/uninstall
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
* @param PackageInterface|null $prevPackage previous package instance in case of updates
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, $targetDir, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$downloader = $this->getDownloaderForPackage($package);
|
||||
if ($downloader) {
|
||||
return $downloader->prepare($type, $package, $targetDir, $prevPackage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs package into target dir.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
* @throws \InvalidArgumentException if package have no urls to download from
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
|
@ -245,7 +264,7 @@ class DownloadManager
|
|||
{
|
||||
$downloader = $this->getDownloaderForPackage($package);
|
||||
if ($downloader) {
|
||||
$downloader->install($package, $targetDir);
|
||||
return $downloader->install($package, $targetDir);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,6 +275,7 @@ class DownloadManager
|
|||
* @param PackageInterface $target target package version
|
||||
* @param string $targetDir target dir
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
* @throws \InvalidArgumentException if initial package is not installed
|
||||
*/
|
||||
public function update(PackageInterface $initial, PackageInterface $target, $targetDir)
|
||||
|
@ -270,17 +290,14 @@ class DownloadManager
|
|||
|
||||
// if we have a downloader present before, but not after, the package became a metapackage and its files should be removed
|
||||
if (!$downloader) {
|
||||
$initialDownloader->remove($initial, $targetDir);
|
||||
return;
|
||||
return $initialDownloader->remove($initial, $targetDir);
|
||||
}
|
||||
|
||||
$initialType = $this->getDownloaderType($initialDownloader);
|
||||
$targetType = $this->getDownloaderType($downloader);
|
||||
if ($initialType === $targetType) {
|
||||
try {
|
||||
$downloader->update($initial, $target, $targetDir);
|
||||
|
||||
return;
|
||||
return $downloader->update($initial, $target, $targetDir);
|
||||
} catch (\RuntimeException $e) {
|
||||
if (!$this->io->isInteractive()) {
|
||||
throw $e;
|
||||
|
@ -294,8 +311,15 @@ class DownloadManager
|
|||
|
||||
// if downloader type changed, or update failed and user asks for reinstall,
|
||||
// we wipe the dir and do a new install instead of updating it
|
||||
$initialDownloader->remove($initial, $targetDir);
|
||||
$this->install($target, $targetDir);
|
||||
$promise = $initialDownloader->remove($initial, $targetDir);
|
||||
if ($promise) {
|
||||
$self = $this;
|
||||
return $promise->then(function ($res) use ($self, $target, $targetDir) {
|
||||
return $self->install($target, $targetDir);
|
||||
});
|
||||
}
|
||||
|
||||
return $this->install($target, $targetDir);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -303,12 +327,32 @@ class DownloadManager
|
|||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function remove(PackageInterface $package, $targetDir)
|
||||
{
|
||||
$downloader = $this->getDownloaderForPackage($package);
|
||||
if ($downloader) {
|
||||
$downloader->remove($package, $targetDir);
|
||||
return $downloader->remove($package, $targetDir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up a failed operation
|
||||
*
|
||||
* @param string $type one of install/update/uninstall
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $targetDir target dir
|
||||
* @param PackageInterface|null $prevPackage previous package instance in case of updates
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, $targetDir, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$downloader = $this->getDownloaderForPackage($package);
|
||||
if ($downloader) {
|
||||
return $downloader->cleanup($type, $package, $targetDir, $prevPackage);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,14 +31,30 @@ interface DownloaderInterface
|
|||
public function getInstallationSource();
|
||||
|
||||
/**
|
||||
* This should do any network-related tasks to prepare for install/update
|
||||
* This should do any network-related tasks to prepare for an upcoming install/update
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function download(PackageInterface $package, $path);
|
||||
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null);
|
||||
|
||||
/**
|
||||
* Downloads specific package into specific folder.
|
||||
* Do anything that needs to be done between all downloads have been completed and the actual operation is executed
|
||||
*
|
||||
* All packages get first downloaded, then all together prepared, then all together installed/updated/uninstalled. Therefore
|
||||
* for error recovery it is important to avoid failing during install/update/uninstall as much as possible, and risky things or
|
||||
* user prompts should happen in the prepare step rather. In case of failure, cleanup() will be called so that changes can
|
||||
* be undone as much as possible.
|
||||
*
|
||||
* @param string $type one of install/update/uninstall
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path download path
|
||||
* @param PackageInterface $prevPackage previous package instance in case of an update
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, $path, PackageInterface $prevPackage = null);
|
||||
|
||||
/**
|
||||
* Installs specific package into specific folder.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path download path
|
||||
|
@ -61,4 +77,19 @@ interface DownloaderInterface
|
|||
* @param string $path download path
|
||||
*/
|
||||
public function remove(PackageInterface $package, $path);
|
||||
|
||||
/**
|
||||
* Do anything to cleanup changes applied in the prepare or install/update/uninstall steps
|
||||
*
|
||||
* Note that cleanup will be called for all packages regardless if they failed an operation or not, to give
|
||||
* all installers a change to cleanup things they did previously, so you need to keep track of changes
|
||||
* applied in the installer/downloader themselves.
|
||||
*
|
||||
* @param string $type one of install/update/uninstall
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path download path
|
||||
* @param PackageInterface $prevPackage previous package instance in case of an update
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, $path, PackageInterface $prevPackage = null);
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function download(PackageInterface $package, $path, $output = true)
|
||||
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true)
|
||||
{
|
||||
if (!$package->getDistUrl()) {
|
||||
throw new \InvalidArgumentException('The given package is missing url information');
|
||||
|
@ -101,7 +101,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
);
|
||||
}
|
||||
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
$this->filesystem->ensureDirectoryExists($path);
|
||||
$fileName = $this->getFileName($package, $path);
|
||||
|
||||
$io = $this->io;
|
||||
|
@ -176,7 +176,9 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
|
||||
$reject = function ($e) use ($io, &$urls, $download, $fileName, $path, $package, &$retries, $filesystem, $self) {
|
||||
// clean up
|
||||
$filesystem->removeDirectory($path);
|
||||
if (file_exists($fileName)) {
|
||||
$filesystem->unlink($fileName);
|
||||
}
|
||||
$self->clearLastCacheWrite($package);
|
||||
|
||||
if ($e instanceof TransportException) {
|
||||
|
@ -220,6 +222,20 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
return $download();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, $path, PackageInterface $prevPackage = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, $path, PackageInterface $prevPackage = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -229,6 +245,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>)");
|
||||
}
|
||||
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
$this->filesystem->ensureDirectoryExists($path);
|
||||
$this->filesystem->rename($this->getFileName($package, $path), $path . pathinfo(parse_url($package->getDistUrl(), PHP_URL_PATH), PATHINFO_BASENAME));
|
||||
}
|
||||
|
@ -333,7 +350,7 @@ class FileDownloader implements DownloaderInterface, ChangeReportInterface
|
|||
$e = null;
|
||||
|
||||
try {
|
||||
$res = $this->download($package, $targetDir.'_compare', false);
|
||||
$res = $this->download($package, $targetDir.'_compare', null, false);
|
||||
$this->httpDownloader->wait();
|
||||
$res = $this->install($package, $targetDir.'_compare', false);
|
||||
|
||||
|
|
|
@ -23,7 +23,15 @@ class FossilDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doInstall(PackageInterface $package, $path, $url)
|
||||
protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doInstall(PackageInterface $package, $path, $url)
|
||||
{
|
||||
// Ensure we are allowed to use this URL by config
|
||||
$this->config->prohibitUrlByConfig($url, $this->io);
|
||||
|
@ -49,7 +57,7 @@ class FossilDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
{
|
||||
// Ensure we are allowed to use this URL by config
|
||||
$this->config->prohibitUrlByConfig($url, $this->io);
|
||||
|
|
|
@ -29,6 +29,7 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
private $hasStashedChanges = false;
|
||||
private $hasDiscardedChanges = false;
|
||||
private $gitUtil;
|
||||
private $cachedPackages = array();
|
||||
|
||||
public function __construct(IOInterface $io, Config $config, ProcessExecutor $process = null, Filesystem $fs = null)
|
||||
{
|
||||
|
@ -39,7 +40,28 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doInstall(PackageInterface $package, $path, $url)
|
||||
protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null)
|
||||
{
|
||||
GitUtil::cleanEnv();
|
||||
|
||||
$cachePath = $this->config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $url).'/';
|
||||
$gitVersion = $this->gitUtil->getVersion();
|
||||
|
||||
// --dissociate option is only available since git 2.3.0-rc0
|
||||
if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=') && Cache::isUsable($cachePath)) {
|
||||
$this->io->writeError(" - Syncing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>) into cache");
|
||||
$this->io->writeError(sprintf(' Cloning to cache at %s', ProcessExecutor::escape($cachePath)), true, IOInterface::DEBUG);
|
||||
$ref = $package->getSourceReference();
|
||||
if ($this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref) && is_dir($cachePath)) {
|
||||
$this->cachedPackages[$package->getId()][$ref] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doInstall(PackageInterface $package, $path, $url)
|
||||
{
|
||||
GitUtil::cleanEnv();
|
||||
$path = $this->normalizePath($path);
|
||||
|
@ -47,26 +69,20 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
$ref = $package->getSourceReference();
|
||||
$flag = Platform::isWindows() ? '/D ' : '';
|
||||
|
||||
// --dissociate option is only available since git 2.3.0-rc0
|
||||
$gitVersion = $this->gitUtil->getVersion();
|
||||
$msg = "Cloning ".$this->getShortHash($ref);
|
||||
|
||||
$command = 'git clone --no-checkout %url% %path% && cd '.$flag.'%path% && git remote add composer %url% && git fetch composer';
|
||||
if ($gitVersion && version_compare($gitVersion, '2.3.0-rc0', '>=') && Cache::isUsable($cachePath)) {
|
||||
$this->io->writeError('', true, IOInterface::DEBUG);
|
||||
$this->io->writeError(sprintf(' Cloning to cache at %s', ProcessExecutor::escape($cachePath)), true, IOInterface::DEBUG);
|
||||
try {
|
||||
$this->gitUtil->fetchRefOrSyncMirror($url, $cachePath, $ref);
|
||||
if (is_dir($cachePath)) {
|
||||
$command =
|
||||
'git clone --no-checkout %cachePath% %path% --dissociate --reference %cachePath% '
|
||||
. '&& cd '.$flag.'%path% '
|
||||
. '&& git remote set-url origin %url% && git remote add composer %url%';
|
||||
$msg = "Cloning ".$this->getShortHash($ref).' from cache';
|
||||
}
|
||||
} catch (\RuntimeException $e) {
|
||||
if (!empty($this->cachedPackages[$package->getId()][$ref])) {
|
||||
$msg = "Cloning ".$this->getShortHash($ref).' from cache';
|
||||
$command =
|
||||
'git clone --no-checkout %cachePath% %path% --dissociate --reference %cachePath% '
|
||||
. '&& cd '.$flag.'%path% '
|
||||
. '&& git remote set-url origin %url% && git remote add composer %url%';
|
||||
} else {
|
||||
$msg = "Cloning ".$this->getShortHash($ref);
|
||||
$command = 'git clone --no-checkout %url% %path% && cd '.$flag.'%path% && git remote add composer %url% && git fetch composer';
|
||||
if (getenv('COMPOSER_DISABLE_NETWORK')) {
|
||||
throw new \RuntimeException('The required git reference for '.$package->getName().' is not in cache and network is disabled, aborting');
|
||||
}
|
||||
}
|
||||
|
||||
$this->io->writeError($msg);
|
||||
|
||||
$commandCallable = function ($url) use ($path, $command, $cachePath) {
|
||||
|
@ -99,13 +115,51 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
{
|
||||
GitUtil::cleanEnv();
|
||||
$path = $this->normalizePath($path);
|
||||
if (!$this->hasMetadataRepository($path)) {
|
||||
throw new \RuntimeException('The .git directory is missing from '.$path.', see https://getcomposer.org/commit-deps for more information');
|
||||
}
|
||||
|
||||
$cachePath = $this->config->get('cache-vcs-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $url).'/';
|
||||
$ref = $target->getSourceReference();
|
||||
$flag = Platform::isWindows() ? '/D ' : '';
|
||||
|
||||
if (!empty($this->cachedPackages[$target->getId()][$ref])) {
|
||||
$msg = "Checking out ".$this->getShortHash($ref).' from cache';
|
||||
$command = 'git rev-parse --quiet --verify %ref% || (git remote set-url composer %cachePath% && git fetch composer && git fetch --tags composer); git remote set-url composer %url%';
|
||||
} else {
|
||||
$msg = "Checking out ".$this->getShortHash($ref);
|
||||
$command = 'git remote set-url composer %url% && git rev-parse --quiet --verify %ref% || (git fetch composer && git fetch --tags composer)';
|
||||
if (getenv('COMPOSER_DISABLE_NETWORK')) {
|
||||
throw new \RuntimeException('The required git reference for '.$target->getName().' is not in cache and network is disabled, aborting');
|
||||
}
|
||||
}
|
||||
|
||||
$this->io->writeError($msg);
|
||||
|
||||
$commandCallable = function ($url) use ($ref, $command, $cachePath) {
|
||||
return str_replace(
|
||||
array('%url%', '%ref%', '%cachePath%'),
|
||||
array(
|
||||
ProcessExecutor::escape($url),
|
||||
ProcessExecutor::escape($ref.'^{commit}'),
|
||||
ProcessExecutor::escape($cachePath),
|
||||
),
|
||||
$command
|
||||
);
|
||||
};
|
||||
|
||||
$this->gitUtil->runCommand($commandCallable, $url, $path);
|
||||
if ($newRef = $this->updateToCommit($path, $ref, $target->getPrettyVersion(), $target->getReleaseDate())) {
|
||||
if ($target->getDistReference() === $target->getSourceReference()) {
|
||||
$target->setDistReference($newRef);
|
||||
}
|
||||
$target->setSourceReference($newRef);
|
||||
}
|
||||
|
||||
$updateOriginUrl = false;
|
||||
if (
|
||||
0 === $this->process->execute('git remote -v', $output, $path)
|
||||
|
@ -116,23 +170,6 @@ class GitDownloader extends VcsDownloader implements DvcsDownloaderInterface
|
|||
$updateOriginUrl = true;
|
||||
}
|
||||
}
|
||||
|
||||
$ref = $target->getSourceReference();
|
||||
$this->io->writeError(" Checking out ".$this->getShortHash($ref));
|
||||
$command = 'git remote set-url composer %s && git rev-parse --quiet --verify %s || (git fetch composer && git fetch --tags composer)';
|
||||
|
||||
$commandCallable = function ($url) use ($command, $ref) {
|
||||
return sprintf($command, ProcessExecutor::escape($url), ProcessExecutor::escape($ref.'^{commit}'));
|
||||
};
|
||||
|
||||
$this->gitUtil->runCommand($commandCallable, $url, $path);
|
||||
if ($newRef = $this->updateToCommit($path, $ref, $target->getPrettyVersion(), $target->getReleaseDate())) {
|
||||
if ($target->getDistReference() === $target->getSourceReference()) {
|
||||
$target->setDistReference($newRef);
|
||||
}
|
||||
$target->setSourceReference($newRef);
|
||||
}
|
||||
|
||||
if ($updateOriginUrl) {
|
||||
$this->updateOriginUrl($path, $target->getSourceUrl());
|
||||
}
|
||||
|
|
|
@ -24,7 +24,15 @@ class HgDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doInstall(PackageInterface $package, $path, $url)
|
||||
protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doInstall(PackageInterface $package, $path, $url)
|
||||
{
|
||||
$hgUtils = new HgUtils($this->io, $this->config, $this->process);
|
||||
|
||||
|
@ -44,7 +52,7 @@ class HgDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
{
|
||||
$hgUtils = new HgUtils($this->io, $this->config, $this->process);
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function download(PackageInterface $package, $path, $output = true)
|
||||
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true)
|
||||
{
|
||||
$url = $package->getDistUrl();
|
||||
$realUrl = realpath($url);
|
||||
|
@ -49,6 +49,10 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
));
|
||||
}
|
||||
|
||||
if (realpath($path) === $realUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos(realpath($path) . DIRECTORY_SEPARATOR, $realUrl . DIRECTORY_SEPARATOR) === 0) {
|
||||
// IMPORTANT NOTICE: If you wish to change this, don't. You are wasting your time and ours.
|
||||
//
|
||||
|
@ -71,6 +75,20 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
$url = $package->getDistUrl();
|
||||
$realUrl = realpath($url);
|
||||
|
||||
if (realpath($path) === $realUrl) {
|
||||
if ($output) {
|
||||
$this->io->writeError(sprintf(
|
||||
' - Installing <info>%s</info> (<comment>%s</comment>): Source already present',
|
||||
$package->getName(),
|
||||
$package->getFullPrettyVersion()
|
||||
));
|
||||
} else {
|
||||
$this->io->writeError('Source already present', false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the transport options with default values
|
||||
$transportOptions = $package->getTransportOptions() + array('symlink' => null);
|
||||
|
||||
|
@ -147,7 +165,9 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
$fileSystem->mirror($realUrl, $path, $iterator);
|
||||
}
|
||||
|
||||
$this->io->writeError('');
|
||||
if ($output) {
|
||||
$this->io->writeError('');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -155,6 +175,16 @@ class PathDownloader extends FileDownloader implements VcsCapableDownloaderInter
|
|||
*/
|
||||
public function remove(PackageInterface $package, $path, $output = true)
|
||||
{
|
||||
$realUrl = realpath($package->getDistUrl());
|
||||
|
||||
if ($path === $realUrl) {
|
||||
if ($output) {
|
||||
$this->io->writeError(" - Removing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>), source is still present in $path");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* For junctions don't blindly rely on Filesystem::removeDirectory as it may be overzealous. If a process
|
||||
* inadvertently locks the file the removal will fail, but it would fall back to recursive delete which
|
||||
|
|
|
@ -24,6 +24,14 @@ class PerforceDownloader extends VcsDownloader
|
|||
/** @var Perforce */
|
||||
protected $perforce;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -76,7 +84,7 @@ class PerforceDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
{
|
||||
$this->doInstall($target, $path, $url);
|
||||
}
|
||||
|
@ -87,8 +95,6 @@ class PerforceDownloader extends VcsDownloader
|
|||
public function getLocalChanges(PackageInterface $package, $path)
|
||||
{
|
||||
$this->io->writeError('Perforce driver does not check for local changes before overriding', true);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,7 +28,15 @@ class SvnDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doInstall(PackageInterface $package, $path, $url)
|
||||
protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function doInstall(PackageInterface $package, $path, $url)
|
||||
{
|
||||
SvnUtil::cleanEnv();
|
||||
$ref = $package->getSourceReference();
|
||||
|
@ -48,7 +56,7 @@ class SvnDownloader extends VcsDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
|
||||
{
|
||||
SvnUtil::cleanEnv();
|
||||
$ref = $target->getSourceReference();
|
||||
|
|
|
@ -20,6 +20,7 @@ use Composer\Package\Version\VersionParser;
|
|||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -54,9 +55,57 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function download(PackageInterface $package, $path)
|
||||
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null)
|
||||
{
|
||||
// noop for now, ideally we would do a git fetch already here, or make sure the cached git repo is synced, etc.
|
||||
if (!$package->getSourceReference()) {
|
||||
throw new \InvalidArgumentException('Package '.$package->getPrettyName().' is missing reference information');
|
||||
}
|
||||
|
||||
$urls = $this->prepareUrls($package->getSourceUrls());
|
||||
|
||||
while ($url = array_shift($urls)) {
|
||||
try {
|
||||
return $this->doDownload($package, $path, $url, $prevPackage);
|
||||
} catch (\Exception $e) {
|
||||
// rethrow phpunit exceptions to avoid hard to debug bug failures
|
||||
if ($e instanceof \PHPUnit_Framework_Exception) {
|
||||
throw $e;
|
||||
}
|
||||
if ($this->io->isDebug()) {
|
||||
$this->io->writeError('Failed: ['.get_class($e).'] '.$e->getMessage());
|
||||
} elseif (count($urls)) {
|
||||
$this->io->writeError(' Failed, trying the next URL');
|
||||
}
|
||||
if (!count($urls)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, $path, PackageInterface $prevPackage = null)
|
||||
{
|
||||
if ($type === 'update') {
|
||||
$this->cleanChanges($prevPackage, $path, true);
|
||||
} elseif ($type === 'install') {
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
} elseif ($type === 'uninstall') {
|
||||
$this->cleanChanges($package, $path, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, $path, PackageInterface $prevPackage = null)
|
||||
{
|
||||
if ($type === 'update') {
|
||||
// TODO keep track of whether prepare was called for this package
|
||||
$this->reapplyChanges($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,32 +118,10 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
}
|
||||
|
||||
$this->io->writeError(" - Installing <info>" . $package->getName() . "</info> (<comment>" . $package->getFullPrettyVersion() . "</comment>): ", false);
|
||||
$this->filesystem->emptyDirectory($path);
|
||||
|
||||
$urls = $package->getSourceUrls();
|
||||
$urls = $this->prepareUrls($package->getSourceUrls());
|
||||
while ($url = array_shift($urls)) {
|
||||
try {
|
||||
if (Filesystem::isLocalPath($url)) {
|
||||
// realpath() below will not understand
|
||||
// url that starts with "file://"
|
||||
$needle = 'file://';
|
||||
$isFileProtocol = false;
|
||||
if (0 === strpos($url, $needle)) {
|
||||
$url = substr($url, strlen($needle));
|
||||
$isFileProtocol = true;
|
||||
}
|
||||
|
||||
// realpath() below will not understand %20 spaces etc.
|
||||
if (false !== strpos($url, '%')) {
|
||||
$url = rawurldecode($url);
|
||||
}
|
||||
|
||||
$url = realpath($url);
|
||||
|
||||
if ($isFileProtocol) {
|
||||
$url = $needle . $url;
|
||||
}
|
||||
}
|
||||
$this->doInstall($package, $path, $url);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
|
@ -141,15 +168,11 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
$actionName = VersionParser::isUpgrade($initial->getVersion(), $target->getVersion()) ? 'Updating' : 'Downgrading';
|
||||
$this->io->writeError(" - " . $actionName . " <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>): ", false);
|
||||
|
||||
$this->cleanChanges($initial, $path, true);
|
||||
$urls = $target->getSourceUrls();
|
||||
$urls = $this->prepareUrls($target->getSourceUrls());
|
||||
|
||||
$exception = null;
|
||||
while ($url = array_shift($urls)) {
|
||||
try {
|
||||
if (Filesystem::isLocalPath($url)) {
|
||||
$url = realpath($url);
|
||||
}
|
||||
$this->doUpdate($initial, $target, $path, $url);
|
||||
|
||||
$exception = null;
|
||||
|
@ -167,8 +190,6 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
}
|
||||
}
|
||||
|
||||
$this->reapplyChanges($path);
|
||||
|
||||
// print the commit logs if in verbose mode and VCS metadata is present
|
||||
// because in case of missing metadata code would trigger another exception
|
||||
if (!$exception && $this->io->isVerbose() && $this->hasMetadataRepository($path)) {
|
||||
|
@ -204,7 +225,6 @@ 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->cleanChanges($package, $path, false);
|
||||
if (!$this->filesystem->removeDirectory($path)) {
|
||||
throw new \RuntimeException('Could not completely delete '.$path.', aborting.');
|
||||
}
|
||||
|
@ -243,7 +263,7 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
}
|
||||
|
||||
/**
|
||||
* Guarantee that no changes have been made to the local copy
|
||||
* Reapply previously stashes changes if applicable, only called after an update (regardless if successful or not)
|
||||
*
|
||||
* @param string $path
|
||||
* @throws \RuntimeException in case the operation must be aborted or the patch does not apply cleanly
|
||||
|
@ -252,12 +272,26 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads data needed to run an install/update later
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path download path
|
||||
* @param string $url package url
|
||||
* @param PackageInterface|null $prevPackage previous package (in case of an update)
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
abstract protected function doDownload(PackageInterface $package, $path, $url, PackageInterface $prevPackage = null);
|
||||
|
||||
/**
|
||||
* Downloads specific package into specific folder.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param string $path download path
|
||||
* @param string $url package url
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
abstract protected function doInstall(PackageInterface $package, $path, $url);
|
||||
|
||||
|
@ -268,6 +302,8 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
* @param PackageInterface $target updated package
|
||||
* @param string $path download path
|
||||
* @param string $url package url
|
||||
*
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
abstract protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url);
|
||||
|
||||
|
@ -289,4 +325,33 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
|
|||
* @return bool
|
||||
*/
|
||||
abstract protected function hasMetadataRepository($path);
|
||||
|
||||
private function prepareUrls(array $urls)
|
||||
{
|
||||
foreach ($urls as $index => $url) {
|
||||
if (Filesystem::isLocalPath($url)) {
|
||||
// realpath() below will not understand
|
||||
// url that starts with "file://"
|
||||
$fileProtocol = 'file://';
|
||||
$isFileProtocol = false;
|
||||
if (0 === strpos($url, $fileProtocol)) {
|
||||
$url = substr($url, strlen($fileProtocol));
|
||||
$isFileProtocol = true;
|
||||
}
|
||||
|
||||
// realpath() below will not understand %20 spaces etc.
|
||||
if (false !== strpos($url, '%')) {
|
||||
$url = rawurldecode($url);
|
||||
}
|
||||
|
||||
$urls[$index] = realpath($url);
|
||||
|
||||
if ($isFileProtocol) {
|
||||
$urls[$index] = $fileProtocol . $urls[$index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $urls;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class ZipDownloader extends ArchiveDownloader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function download(PackageInterface $package, $path, $output = true)
|
||||
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true)
|
||||
{
|
||||
if (null === self::$hasSystemUnzip) {
|
||||
$finder = new ExecutableFinder;
|
||||
|
@ -76,7 +76,7 @@ class ZipDownloader extends ArchiveDownloader
|
|||
}
|
||||
}
|
||||
|
||||
return parent::download($package, $path, $output);
|
||||
return parent::download($package, $path, $prevPackage, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -201,7 +201,9 @@ class EventDispatcher
|
|||
|
||||
try {
|
||||
/** @var InstallerEvent $event */
|
||||
$return = $this->dispatch($scriptName, new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags));
|
||||
$scriptEvent = new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags);
|
||||
$scriptEvent->setOriginatingEvent($event);
|
||||
$return = $this->dispatch($scriptName, $scriptEvent);
|
||||
} catch (ScriptExecutionException $e) {
|
||||
$this->io->writeError(sprintf('<error>Script %s was called via %s</error>', $callable, $event->getName()), true, IOInterface::QUIET);
|
||||
throw $e;
|
||||
|
|
|
@ -392,7 +392,7 @@ class Factory
|
|||
? substr($composerFile, 0, -4).'lock'
|
||||
: $composerFile . '.lock';
|
||||
|
||||
$locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $rm, $im, file_get_contents($composerFile));
|
||||
$locker = new Package\Locker($io, new JsonFile($lockFile, null, $io), $im, file_get_contents($composerFile));
|
||||
$composer->setLocker($locker);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,14 @@ abstract class BaseIO implements IOInterface
|
|||
return $this->authentications;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function resetAuthentications()
|
||||
{
|
||||
$this->authentications = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -632,7 +632,7 @@ class Installer
|
|||
$this->installationManager->execute($localRepo, $operation);
|
||||
|
||||
if ($this->executeOperations) {
|
||||
$localRepo->write();
|
||||
$localRepo->write($this->devMode, $this->installationManager);
|
||||
}
|
||||
|
||||
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($jobType);
|
||||
|
|
|
@ -177,11 +177,52 @@ class InstallationManager
|
|||
$promise = $installer->download($target, $operation->getInitialPackage());
|
||||
}
|
||||
|
||||
if (isset($promise)) {
|
||||
if (!empty($promise)) {
|
||||
$this->loop->wait(array($promise));
|
||||
}
|
||||
|
||||
$this->$method($repo, $operation);
|
||||
$e = null;
|
||||
try {
|
||||
if ($method === 'install' || $method === 'uninstall') {
|
||||
$package = $operation->getPackage();
|
||||
$installer = $this->getInstaller($package->getType());
|
||||
$promise = $installer->prepare($method, $package);
|
||||
} elseif ($method === 'update') {
|
||||
$target = $operation->getTargetPackage();
|
||||
$targetType = $target->getType();
|
||||
$installer = $this->getInstaller($targetType);
|
||||
$promise = $installer->prepare('update', $target, $operation->getInitialPackage());
|
||||
}
|
||||
|
||||
if (!empty($promise)) {
|
||||
$this->loop->wait(array($promise));
|
||||
}
|
||||
|
||||
$promise = $this->$method($repo, $operation);
|
||||
if (!empty($promise)) {
|
||||
$this->loop->wait(array($promise));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
if ($method === 'install' || $method === 'uninstall') {
|
||||
$package = $operation->getPackage();
|
||||
$installer = $this->getInstaller($package->getType());
|
||||
$promise = $installer->cleanup($method, $package);
|
||||
} elseif ($method === 'update') {
|
||||
$target = $operation->getTargetPackage();
|
||||
$targetType = $target->getType();
|
||||
$installer = $this->getInstaller($targetType);
|
||||
$promise = $installer->cleanup('update', $target, $operation->getInitialPackage());
|
||||
}
|
||||
|
||||
if (!empty($promise)) {
|
||||
$this->loop->wait(array($promise));
|
||||
}
|
||||
|
||||
if ($e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -194,8 +235,10 @@ class InstallationManager
|
|||
{
|
||||
$package = $operation->getPackage();
|
||||
$installer = $this->getInstaller($package->getType());
|
||||
$installer->install($repo, $package);
|
||||
$promise = $installer->install($repo, $package);
|
||||
$this->markForNotification($package);
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -214,13 +257,15 @@ class InstallationManager
|
|||
|
||||
if ($initialType === $targetType) {
|
||||
$installer = $this->getInstaller($initialType);
|
||||
$installer->update($repo, $initial, $target);
|
||||
$promise = $installer->update($repo, $initial, $target);
|
||||
$this->markForNotification($target);
|
||||
} else {
|
||||
$this->getInstaller($initialType)->uninstall($repo, $initial);
|
||||
$installer = $this->getInstaller($targetType);
|
||||
$installer->install($repo, $target);
|
||||
$promise = $installer->install($repo, $target);
|
||||
}
|
||||
|
||||
return $promise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -233,7 +278,8 @@ class InstallationManager
|
|||
{
|
||||
$package = $operation->getPackage();
|
||||
$installer = $this->getInstaller($package->getType());
|
||||
$installer->uninstall($repo, $package);
|
||||
|
||||
return $installer->uninstall($repo, $package);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -46,26 +46,43 @@ interface InstallerInterface
|
|||
/**
|
||||
* Downloads the files needed to later install the given package.
|
||||
*
|
||||
* @param PackageInterface $package package instance
|
||||
* @param PackageInterface $prevPackage previous package instance in case of an update
|
||||
* @param PackageInterface $package package instance
|
||||
* @param PackageInterface $prevPackage previous package instance in case of an update
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function download(PackageInterface $package, PackageInterface $prevPackage = null);
|
||||
|
||||
/**
|
||||
* Do anything that needs to be done between all downloads have been completed and the actual operation is executed
|
||||
*
|
||||
* All packages get first downloaded, then all together prepared, then all together installed/updated/uninstalled. Therefore
|
||||
* for error recovery it is important to avoid failing during install/update/uninstall as much as possible, and risky things or
|
||||
* user prompts should happen in the prepare step rather. In case of failure, cleanup() will be called so that changes can
|
||||
* be undone as much as possible.
|
||||
*
|
||||
* @param string $type one of install/update/uninstall
|
||||
* @param PackageInterface $package package instance
|
||||
* @param PackageInterface $prevPackage previous package instance in case of an update
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null);
|
||||
|
||||
/**
|
||||
* Installs specific package.
|
||||
*
|
||||
* @param InstalledRepositoryInterface $repo repository in which to check
|
||||
* @param PackageInterface $package package instance
|
||||
* @param InstalledRepositoryInterface $repo repository in which to check
|
||||
* @param PackageInterface $package package instance
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function install(InstalledRepositoryInterface $repo, PackageInterface $package);
|
||||
|
||||
/**
|
||||
* Updates specific package.
|
||||
*
|
||||
* @param InstalledRepositoryInterface $repo repository in which to check
|
||||
* @param PackageInterface $initial already installed package version
|
||||
* @param PackageInterface $target updated version
|
||||
* @param InstalledRepositoryInterface $repo repository in which to check
|
||||
* @param PackageInterface $initial already installed package version
|
||||
* @param PackageInterface $target updated version
|
||||
* @return PromiseInterface|null
|
||||
*
|
||||
* @throws InvalidArgumentException if $initial package is not installed
|
||||
*/
|
||||
|
@ -74,11 +91,26 @@ interface InstallerInterface
|
|||
/**
|
||||
* Uninstalls specific package.
|
||||
*
|
||||
* @param InstalledRepositoryInterface $repo repository in which to check
|
||||
* @param PackageInterface $package package instance
|
||||
* @param InstalledRepositoryInterface $repo repository in which to check
|
||||
* @param PackageInterface $package package instance
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package);
|
||||
|
||||
/**
|
||||
* Do anything to cleanup changes applied in the prepare or install/update/uninstall steps
|
||||
*
|
||||
* Note that cleanup will be called for all packages regardless if they failed an operation or not, to give
|
||||
* all installers a change to cleanup things they did previously, so you need to keep track of changes
|
||||
* applied in the installer/downloader themselves.
|
||||
*
|
||||
* @param string $type one of install/update/uninstall
|
||||
* @param PackageInterface $package package instance
|
||||
* @param PackageInterface $prevPackage previous package instance in case of an update
|
||||
* @return PromiseInterface|null
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null);
|
||||
|
||||
/**
|
||||
* Returns the installation path of a package
|
||||
*
|
||||
|
|
|
@ -85,6 +85,9 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
|
|||
return (Platform::isWindows() && $this->filesystem->isJunction($installPath)) || is_link($installPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function download(PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$this->initializeVendorDir();
|
||||
|
@ -93,6 +96,28 @@ class LibraryInstaller implements InstallerInterface, BinaryPresenceInterface
|
|||
return $this->downloadManager->download($package, $downloadPath, $prevPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$this->initializeVendorDir();
|
||||
$downloadPath = $this->getInstallPath($package);
|
||||
|
||||
return $this->downloadManager->prepare($type, $package, $downloadPath, $prevPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$this->initializeVendorDir();
|
||||
$downloadPath = $this->getInstallPath($package);
|
||||
|
||||
return $this->downloadManager->cleanup($type, $package, $downloadPath, $prevPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -55,6 +55,22 @@ class MetapackageInstaller implements InstallerInterface
|
|||
// noop
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -47,6 +47,20 @@ class NoopInstaller implements InstallerInterface
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -70,7 +70,7 @@ class PluginInstaller extends LibraryInstaller
|
|||
$this->composer->getPluginManager()->registerPackage($package, true);
|
||||
} catch (\Exception $e) {
|
||||
// Rollback installation
|
||||
$this->io->writeError('Plugin installation failed, rolling back');
|
||||
$this->io->writeError('Plugin initialization failed, uninstalling plugin');
|
||||
parent::uninstall($repo, $package);
|
||||
throw $e;
|
||||
}
|
||||
|
@ -81,12 +81,22 @@ class PluginInstaller extends LibraryInstaller
|
|||
*/
|
||||
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
|
||||
{
|
||||
$extra = $target->getExtra();
|
||||
if (empty($extra['class'])) {
|
||||
throw new \UnexpectedValueException('Error while installing '.$target->getPrettyName().', composer-plugin packages should have a class defined in their extra key to be usable.');
|
||||
}
|
||||
|
||||
parent::update($repo, $initial, $target);
|
||||
$this->composer->getPluginManager()->registerPackage($target, true);
|
||||
|
||||
try {
|
||||
$this->composer->getPluginManager()->deactivatePackage($initial, true);
|
||||
$this->composer->getPluginManager()->registerPackage($target, true);
|
||||
} catch (\Exception $e) {
|
||||
// Rollback installation
|
||||
$this->io->writeError('Plugin initialization failed, uninstalling plugin');
|
||||
parent::uninstall($repo, $target);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||
{
|
||||
$this->composer->getPluginManager()->uninstallPackage($package, true);
|
||||
parent::uninstall($repo, $package);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,6 +71,22 @@ class ProjectInstaller implements InstallerInterface
|
|||
return $this->downloadManager->download($package, $installPath, $prevPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function prepare($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$this->downloadManager->prepare($type, $package, $this->installPath, $prevPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function cleanup($type, PackageInterface $package, PackageInterface $prevPackage = null)
|
||||
{
|
||||
$this->downloadManager->cleanup($type, $package, $this->installPath, $prevPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -326,9 +326,10 @@ class JsonManipulator
|
|||
}
|
||||
|
||||
// try and find a match for the subkey
|
||||
if ($this->pregMatch('{"'.preg_quote($name).'"\s*:}i', $children)) {
|
||||
$keyRegex = str_replace('/', '\\\\?/', preg_quote($name));
|
||||
if ($this->pregMatch('{"'.$keyRegex.'"\s*:}i', $children)) {
|
||||
// find best match for the value of "name"
|
||||
if (preg_match_all('{'.self::$DEFINES.'"'.preg_quote($name).'"\s*:\s*(?:(?&json))}x', $children, $matches)) {
|
||||
if (preg_match_all('{'.self::$DEFINES.'"'.$keyRegex.'"\s*:\s*(?:(?&json))}x', $children, $matches)) {
|
||||
$bestMatch = '';
|
||||
foreach ($matches[0] as $match) {
|
||||
if (strlen($bestMatch) < strlen($match)) {
|
||||
|
|
|
@ -33,8 +33,6 @@ class Locker
|
|||
{
|
||||
/** @var JsonFile */
|
||||
private $lockFile;
|
||||
/** @var RepositoryManager */
|
||||
private $repositoryManager;
|
||||
/** @var InstallationManager */
|
||||
private $installationManager;
|
||||
/** @var string */
|
||||
|
@ -55,14 +53,12 @@ class Locker
|
|||
*
|
||||
* @param IOInterface $io
|
||||
* @param JsonFile $lockFile lockfile loader
|
||||
* @param RepositoryManager $repositoryManager repository manager instance
|
||||
* @param InstallationManager $installationManager installation manager instance
|
||||
* @param string $composerFileContents The contents of the composer file
|
||||
*/
|
||||
public function __construct(IOInterface $io, JsonFile $lockFile, RepositoryManager $repositoryManager, InstallationManager $installationManager, $composerFileContents)
|
||||
public function __construct(IOInterface $io, JsonFile $lockFile, InstallationManager $installationManager, $composerFileContents)
|
||||
{
|
||||
$this->lockFile = $lockFile;
|
||||
$this->repositoryManager = $repositoryManager;
|
||||
$this->installationManager = $installationManager;
|
||||
$this->hash = md5($composerFileContents);
|
||||
$this->contentHash = self::getContentHash($composerFileContents);
|
||||
|
|
|
@ -36,4 +36,22 @@ interface PluginInterface
|
|||
* @param IOInterface $io
|
||||
*/
|
||||
public function activate(Composer $composer, IOInterface $io);
|
||||
|
||||
/**
|
||||
* Remove any hooks from Composer
|
||||
*
|
||||
* @param Composer $composer
|
||||
* @param IOInterface $io
|
||||
*/
|
||||
public function deactivate(Composer $composer, IOInterface $io);
|
||||
|
||||
/**
|
||||
* Prepare the plugin to be uninstalled
|
||||
*
|
||||
* This will be called after deactivate
|
||||
*
|
||||
* @param Composer $composer
|
||||
* @param IOInterface $io
|
||||
*/
|
||||
public function uninstall(Composer $composer, IOInterface $io);
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ class PluginManager
|
|||
|
||||
$oldInstallerPlugin = ($package->getType() === 'composer-installer');
|
||||
|
||||
if (in_array($package->getName(), $this->registeredPlugins)) {
|
||||
if (isset($this->registeredPlugins[$package->getName()])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -201,16 +201,82 @@ class PluginManager
|
|||
if ($oldInstallerPlugin) {
|
||||
$installer = new $class($this->io, $this->composer);
|
||||
$this->composer->getInstallationManager()->addInstaller($installer);
|
||||
$this->registeredPlugins[$package->getName()] = $installer;
|
||||
} elseif (class_exists($class)) {
|
||||
$plugin = new $class();
|
||||
$this->addPlugin($plugin);
|
||||
$this->registeredPlugins[] = $package->getName();
|
||||
$this->registeredPlugins[$package->getName()] = $plugin;
|
||||
} elseif ($failOnMissingClasses) {
|
||||
throw new \UnexpectedValueException('Plugin '.$package->getName().' could not be initialized, class not found: '.$class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivates a plugin package
|
||||
*
|
||||
* If it's of type composer-installer it is unregistered from the installers
|
||||
* instead for BC
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function deactivatePackage(PackageInterface $package)
|
||||
{
|
||||
if ($this->disablePlugins) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldInstallerPlugin = ($package->getType() === 'composer-installer');
|
||||
|
||||
if (!isset($this->registeredPlugins[$package->getName()])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($oldInstallerPlugin) {
|
||||
$installer = $this->registeredPlugins[$package->getName()];
|
||||
unset($this->registeredPlugins[$package->getName()]);
|
||||
$this->composer->getInstallationManager()->removeInstaller($installer);
|
||||
} else {
|
||||
$plugin = $this->registeredPlugins[$package->getName()];
|
||||
unset($this->registeredPlugins[$package->getName()]);
|
||||
$this->removePlugin($plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall a plugin package
|
||||
*
|
||||
* If it's of type composer-installer it is unregistered from the installers
|
||||
* instead for BC
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
*
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function uninstallPackage(PackageInterface $package)
|
||||
{
|
||||
if ($this->disablePlugins) {
|
||||
return;
|
||||
}
|
||||
|
||||
$oldInstallerPlugin = ($package->getType() === 'composer-installer');
|
||||
|
||||
if (!isset($this->registeredPlugins[$package->getName()])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($oldInstallerPlugin) {
|
||||
$this->deactivatePackage($package);
|
||||
} else {
|
||||
$plugin = $this->registeredPlugins[$package->getName()];
|
||||
unset($this->registeredPlugins[$package->getName()]);
|
||||
$this->removePlugin($plugin);
|
||||
$this->uninstallPlugin($plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the internal composer-plugin-api package.
|
||||
*
|
||||
|
@ -241,6 +307,44 @@ class PluginManager
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a plugin, deactivates it and removes any listener the plugin has set on the plugin instance
|
||||
*
|
||||
* Ideally plugin packages should be deactivated via deactivatePackage, but if you use Composer
|
||||
* programmatically and want to deregister a plugin class directly this is a valid way
|
||||
* to do it.
|
||||
*
|
||||
* @param PluginInterface $plugin plugin instance
|
||||
*/
|
||||
public function removePlugin(PluginInterface $plugin)
|
||||
{
|
||||
$index = array_search($plugin, $this->plugins, true);
|
||||
if ($index === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->io->writeError('Unloading plugin '.get_class($plugin), true, IOInterface::DEBUG);
|
||||
unset($this->plugins[$index]);
|
||||
$plugin->deactivate($this->composer, $this->io);
|
||||
|
||||
$this->composer->getEventDispatcher()->removeListener($plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies a plugin it is being uninstalled and should clean up
|
||||
*
|
||||
* Ideally plugin packages should be uninstalled via uninstallPackage, but if you use Composer
|
||||
* programmatically and want to deregister a plugin class directly this is a valid way
|
||||
* to do it.
|
||||
*
|
||||
* @param PluginInterface $plugin plugin instance
|
||||
*/
|
||||
public function uninstallPlugin(PluginInterface $plugin)
|
||||
{
|
||||
$this->io->writeError('Uninstalling plugin '.get_class($plugin), true, IOInterface::DEBUG);
|
||||
$plugin->uninstall($this->composer, $this->io);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all plugins and installers from a repository
|
||||
*
|
||||
|
|
|
@ -16,6 +16,7 @@ use Composer\IO\IOInterface;
|
|||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\Loader\ArrayLoader;
|
||||
use Composer\Package\Loader\LoaderInterface;
|
||||
use Composer\Util\Zip;
|
||||
|
||||
/**
|
||||
* @author Serge Smertin <serg.smertin@gmail.com>
|
||||
|
@ -80,76 +81,15 @@ class ArtifactRepository extends ArrayRepository implements ConfigurableReposito
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a file by name, returning the one that has the shortest path.
|
||||
*
|
||||
* @param \ZipArchive $zip
|
||||
* @param string $filename
|
||||
* @return bool|int
|
||||
*/
|
||||
private function locateFile(\ZipArchive $zip, $filename)
|
||||
{
|
||||
$indexOfShortestMatch = false;
|
||||
$lengthOfShortestMatch = -1;
|
||||
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$stat = $zip->statIndex($i);
|
||||
if (strcmp(basename($stat['name']), $filename) === 0) {
|
||||
$directoryName = dirname($stat['name']);
|
||||
if ($directoryName == '.') {
|
||||
//if composer.json is in root directory
|
||||
//it has to be the one to use.
|
||||
return $i;
|
||||
}
|
||||
|
||||
if (strpos($directoryName, '\\') !== false ||
|
||||
strpos($directoryName, '/') !== false) {
|
||||
//composer.json files below first directory are rejected
|
||||
continue;
|
||||
}
|
||||
|
||||
$length = strlen($stat['name']);
|
||||
if ($indexOfShortestMatch === false || $length < $lengthOfShortestMatch) {
|
||||
//Check it's not a directory.
|
||||
$contents = $zip->getFromIndex($i);
|
||||
if ($contents !== false) {
|
||||
$indexOfShortestMatch = $i;
|
||||
$lengthOfShortestMatch = $length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $indexOfShortestMatch;
|
||||
}
|
||||
|
||||
private function getComposerInformation(\SplFileInfo $file)
|
||||
{
|
||||
$zip = new \ZipArchive();
|
||||
if ($zip->open($file->getPathname()) !== true) {
|
||||
$json = Zip::getComposerJson($file->getPathname());
|
||||
|
||||
if (null === $json) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 == $zip->numFiles) {
|
||||
$zip->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$foundFileIndex = $this->locateFile($zip, 'composer.json');
|
||||
if (false === $foundFileIndex) {
|
||||
$zip->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$configurationFileName = $zip->getNameIndex($foundFileIndex);
|
||||
$zip->close();
|
||||
|
||||
$composerFile = "zip://{$file->getPathname()}#$configurationFileName";
|
||||
$json = file_get_contents($composerFile);
|
||||
|
||||
$package = JsonFile::parseJson($json, $composerFile);
|
||||
$package = JsonFile::parseJson($json, $file->getPathname().'#composer.json');
|
||||
$package['dist'] = array(
|
||||
'type' => 'zip',
|
||||
'url' => strtr($file->getPathname(), '\\', '/'),
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace Composer\Repository;
|
|||
use Composer\Json\JsonFile;
|
||||
use Composer\Package\Loader\ArrayLoader;
|
||||
use Composer\Package\Dumper\ArrayDumper;
|
||||
use Composer\Installer\InstallationManager;
|
||||
use Composer\Util\Filesystem;
|
||||
|
||||
/**
|
||||
* Filesystem repository.
|
||||
|
@ -49,7 +51,12 @@ class FilesystemRepository extends WritableArrayRepository
|
|||
}
|
||||
|
||||
try {
|
||||
$packages = $this->file->read();
|
||||
$data = $this->file->read();
|
||||
if (isset($data['packages'])) {
|
||||
$packages = $data['packages'];
|
||||
} else {
|
||||
$packages = $data;
|
||||
}
|
||||
|
||||
// forward compatibility for composer v2 installed.json
|
||||
if (isset($packages['packages'])) {
|
||||
|
@ -79,16 +86,21 @@ class FilesystemRepository extends WritableArrayRepository
|
|||
/**
|
||||
* Writes writable repository.
|
||||
*/
|
||||
public function write()
|
||||
public function write($devMode, InstallationManager $installationManager)
|
||||
{
|
||||
$data = array();
|
||||
$data = array('packages' => array(), 'dev' => $devMode);
|
||||
$dumper = new ArrayDumper();
|
||||
$fs = new Filesystem();
|
||||
$repoDir = dirname($fs->normalizePath($this->file->getPath()));
|
||||
|
||||
foreach ($this->getCanonicalPackages() as $package) {
|
||||
$data[] = $dumper->dump($package);
|
||||
$pkgArray = $dumper->dump($package);
|
||||
$path = $installationManager->getInstallPath($package);
|
||||
$pkgArray['install-path'] = ('' !== $path && null !== $path) ? $fs->findShortestPath($repoDir, $path, true) : null;
|
||||
$data['packages'][] = $pkgArray;
|
||||
}
|
||||
|
||||
usort($data, function ($a, $b) {
|
||||
usort($data['packages'], function ($a, $b) {
|
||||
return strcmp($a['name'], $b['name']);
|
||||
});
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ use Composer\Cache;
|
|||
use Composer\IO\IOInterface;
|
||||
use Composer\Util\GitHub;
|
||||
use Composer\Util\Http\Response;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
|
||||
/**
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
|
@ -308,6 +307,10 @@ class GitHubDriver extends VcsDriver
|
|||
*/
|
||||
protected function generateSshUrl()
|
||||
{
|
||||
if (false !== strpos($this->originUrl, ':')) {
|
||||
return 'ssh://git@' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
|
||||
}
|
||||
|
||||
return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git';
|
||||
}
|
||||
|
||||
|
@ -342,10 +345,10 @@ class GitHubDriver extends VcsDriver
|
|||
$scopesIssued = array();
|
||||
$scopesNeeded = array();
|
||||
if ($headers = $e->getHeaders()) {
|
||||
if ($scopes = RemoteFilesystem::findHeaderValue($headers, 'X-OAuth-Scopes')) {
|
||||
if ($scopes = Response::findHeaderValue($headers, 'X-OAuth-Scopes')) {
|
||||
$scopesIssued = explode(' ', $scopes);
|
||||
}
|
||||
if ($scopes = RemoteFilesystem::findHeaderValue($headers, 'X-Accepted-OAuth-Scopes')) {
|
||||
if ($scopes = Response::findHeaderValue($headers, 'X-Accepted-OAuth-Scopes')) {
|
||||
$scopesNeeded = explode(' ', $scopes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,9 +68,9 @@ class GitLabDriver extends VcsDriver
|
|||
private $isPrivate = true;
|
||||
|
||||
/**
|
||||
* @var int port number
|
||||
* @var bool true if the origin has a port number or a path component in it
|
||||
*/
|
||||
protected $portNumber;
|
||||
private $hasNonstandardOrigin = false;
|
||||
|
||||
const URL_REGEX = '#^(?:(?P<scheme>https?)://(?P<domain>.+?)(?::(?P<port>[0-9]+))?/|git@(?P<domain2>[^:]+):)(?P<parts>.+)/(?P<repo>[^/]+?)(?:\.git|/)?$#';
|
||||
|
||||
|
@ -95,11 +95,10 @@ class GitLabDriver extends VcsDriver
|
|||
? $match['scheme']
|
||||
: (isset($this->repoConfig['secure-http']) && $this->repoConfig['secure-http'] === false ? 'http' : 'https')
|
||||
;
|
||||
$this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts);
|
||||
$this->originUrl = $this->determineOrigin($configuredDomains, $guessedDomain, $urlParts, $match['port']);
|
||||
|
||||
if (!empty($match['port']) && true === is_numeric($match['port'])) {
|
||||
// If it is an HTTP based URL, and it has a port
|
||||
$this->portNumber = (int) $match['port'];
|
||||
if (false !== strpos($this->originUrl, ':') || false !== strpos($this->originUrl, '/')) {
|
||||
$this->hasNonstandardOrigin = true;
|
||||
}
|
||||
|
||||
$this->namespace = implode('/', $urlParts);
|
||||
|
@ -260,10 +259,7 @@ class GitLabDriver extends VcsDriver
|
|||
*/
|
||||
public function getApiUrl()
|
||||
{
|
||||
$domainName = $this->originUrl;
|
||||
$portNumber = (true === is_numeric($this->portNumber)) ? sprintf(':%s', $this->portNumber) : '';
|
||||
|
||||
return $this->scheme.'://'.$domainName.$portNumber.'/api/v4/projects/'.$this->urlEncodeAll($this->namespace).'%2F'.$this->urlEncodeAll($this->repository);
|
||||
return $this->scheme.'://'.$this->originUrl.'/api/v4/projects/'.$this->urlEncodeAll($this->namespace).'%2F'.$this->urlEncodeAll($this->repository);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -362,6 +358,10 @@ class GitLabDriver extends VcsDriver
|
|||
*/
|
||||
protected function generateSshUrl()
|
||||
{
|
||||
if ($this->hasNonstandardOrigin) {
|
||||
return 'ssh://git@'.$this->originUrl.'/'.$this->namespace.'/'.$this->repository.'.git';
|
||||
}
|
||||
|
||||
return 'git@' . $this->originUrl . ':'.$this->namespace.'/'.$this->repository.'.git';
|
||||
}
|
||||
|
||||
|
@ -464,7 +464,7 @@ class GitLabDriver extends VcsDriver
|
|||
$guessedDomain = !empty($match['domain']) ? $match['domain'] : $match['domain2'];
|
||||
$urlParts = explode('/', $match['parts']);
|
||||
|
||||
if (false === self::determineOrigin((array) $config->get('gitlab-domains'), $guessedDomain, $urlParts)) {
|
||||
if (false === self::determineOrigin((array) $config->get('gitlab-domains'), $guessedDomain, $urlParts, $match['port'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -495,16 +495,23 @@ class GitLabDriver extends VcsDriver
|
|||
* @param array $urlParts
|
||||
* @return bool|string
|
||||
*/
|
||||
private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts)
|
||||
private static function determineOrigin(array $configuredDomains, $guessedDomain, array &$urlParts, $portNumber)
|
||||
{
|
||||
if (in_array($guessedDomain, $configuredDomains)) {
|
||||
if (in_array($guessedDomain, $configuredDomains) || ($portNumber && in_array($guessedDomain.':'.$portNumber, $configuredDomains))) {
|
||||
if ($portNumber) {
|
||||
return $guessedDomain.':'.$portNumber;
|
||||
}
|
||||
return $guessedDomain;
|
||||
}
|
||||
|
||||
if ($portNumber) {
|
||||
$guessedDomain .= ':'.$portNumber;
|
||||
}
|
||||
|
||||
while (null !== ($part = array_shift($urlParts))) {
|
||||
$guessedDomain .= '/' . $part;
|
||||
|
||||
if (in_array($guessedDomain, $configuredDomains)) {
|
||||
if (in_array($guessedDomain, $configuredDomains) || ($portNumber && in_array(preg_replace('{:\d+}', '', $guessedDomain), $configuredDomains))) {
|
||||
return $guessedDomain;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Repository;
|
||||
|
||||
use Composer\Package\AliasPackage;
|
||||
use Composer\Installer\InstallationManager;
|
||||
|
||||
/**
|
||||
* Writable array repository.
|
||||
|
@ -24,7 +25,7 @@ class WritableArrayRepository extends ArrayRepository implements WritableReposit
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function write()
|
||||
public function write($devMode, InstallationManager $installationManager)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Repository;
|
||||
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Installer\InstallationManager;
|
||||
|
||||
/**
|
||||
* Writable repository interface.
|
||||
|
@ -23,8 +24,10 @@ interface WritableRepositoryInterface extends RepositoryInterface
|
|||
{
|
||||
/**
|
||||
* Writes repository (f.e. to the disc).
|
||||
*
|
||||
* @param bool $devMode Whether dev requirements were included or not in this installation
|
||||
*/
|
||||
public function write();
|
||||
public function write($devMode, InstallationManager $installationManager);
|
||||
|
||||
/**
|
||||
* Adds package to the repository.
|
||||
|
|
|
@ -39,6 +39,11 @@ class Event extends BaseEvent
|
|||
*/
|
||||
private $devMode;
|
||||
|
||||
/**
|
||||
* @var BaseEvent
|
||||
*/
|
||||
private $originatingEvent;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
|
@ -55,6 +60,7 @@ class Event extends BaseEvent
|
|||
$this->composer = $composer;
|
||||
$this->io = $io;
|
||||
$this->devMode = $devMode;
|
||||
$this->originatingEvent = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,4 +92,42 @@ class Event extends BaseEvent
|
|||
{
|
||||
return $this->devMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the originating event.
|
||||
*
|
||||
* @return \Composer\EventDispatcher\Event|null
|
||||
*/
|
||||
public function getOriginatingEvent()
|
||||
{
|
||||
return $this->originatingEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the originating event.
|
||||
*
|
||||
* @param \Composer\EventDispatcher\Event $event
|
||||
* @return $this
|
||||
*/
|
||||
public function setOriginatingEvent(BaseEvent $event)
|
||||
{
|
||||
$this->originatingEvent = $this->calculateOriginatingEvent($event);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the upper-most event in chain.
|
||||
*
|
||||
* @param \Composer\EventDispatcher\Event $event
|
||||
* @return \Composer\EventDispatcher\Event
|
||||
*/
|
||||
private function calculateOriginatingEvent(BaseEvent $event)
|
||||
{
|
||||
if ($event instanceof Event && $event->getOriginatingEvent()) {
|
||||
return $this->calculateOriginatingEvent($event->getOriginatingEvent());
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ class ErrorHandler
|
|||
*
|
||||
* @static
|
||||
* @throws \ErrorException
|
||||
* @return bool
|
||||
*/
|
||||
public static function handle($level, $message, $file, $line)
|
||||
{
|
||||
|
@ -63,6 +64,8 @@ class ErrorHandler
|
|||
}, array_slice(debug_backtrace(), 2))));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -224,6 +224,10 @@ class Git
|
|||
|
||||
public function syncMirror($url, $dir)
|
||||
{
|
||||
if (getenv('COMPOSER_DISABLE_NETWORK')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// update the repo if it is a valid git repository
|
||||
if (is_dir($dir) && 0 === $this->process->execute('git rev-parse --git-dir', $output, $dir) && trim($output) === '.') {
|
||||
try {
|
||||
|
@ -260,9 +264,7 @@ class Git
|
|||
}
|
||||
}
|
||||
|
||||
$this->syncMirror($url, $dir);
|
||||
|
||||
return false;
|
||||
return $this->syncMirror($url, $dir);
|
||||
}
|
||||
|
||||
private function isAuthenticationFailure($url, &$match)
|
||||
|
|
|
@ -57,7 +57,10 @@ class GitLab
|
|||
*/
|
||||
public function authorizeOAuth($originUrl)
|
||||
{
|
||||
if (!in_array($originUrl, $this->config->get('gitlab-domains'), true)) {
|
||||
// before composer 1.9, origin URLs had no port number in them
|
||||
$bcOriginUrl = preg_replace('{:\d+}', '', $originUrl);
|
||||
|
||||
if (!in_array($originUrl, $this->config->get('gitlab-domains'), true) && !in_array($bcOriginUrl, $this->config->get('gitlab-domains'), true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -77,6 +80,12 @@ class GitLab
|
|||
return true;
|
||||
}
|
||||
|
||||
if (isset($authTokens[$bcOriginUrl])) {
|
||||
$this->io->setAuthentication($originUrl, $authTokens[$bcOriginUrl], 'private-token');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ use Composer\Config;
|
|||
use Composer\IO\IOInterface;
|
||||
use Composer\Downloader\TransportException;
|
||||
use Composer\CaBundle\CaBundle;
|
||||
use Composer\Util\RemoteFilesystem;
|
||||
use Composer\Util\StreamContextFactory;
|
||||
use Composer\Util\AuthHelper;
|
||||
use Composer\Util\Url;
|
||||
|
|
|
@ -61,20 +61,7 @@ class Response
|
|||
|
||||
public function getHeader($name)
|
||||
{
|
||||
$value = null;
|
||||
foreach ($this->headers as $header) {
|
||||
if (preg_match('{^'.$name.':\s*(.+?)\s*$}i', $header, $match)) {
|
||||
$value = $match[1];
|
||||
} elseif (preg_match('{^HTTP/}i', $header)) {
|
||||
// TODO ideally redirects would be handled in CurlDownloader/RemoteFilesystem and this becomes unnecessary
|
||||
//
|
||||
// In case of redirects, headers contains the headers of all responses
|
||||
// so we reset the flag when a new response is being parsed as we are only interested in the last response
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
return self::findHeaderValue($this->headers, $name);
|
||||
}
|
||||
|
||||
public function getBody()
|
||||
|
@ -91,4 +78,27 @@ class Response
|
|||
{
|
||||
$this->request = $this->code = $this->headers = $this->body = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $headers array of returned headers like from getLastHeaders()
|
||||
* @param string $name header name (case insensitive)
|
||||
* @return string|null
|
||||
*/
|
||||
public static function findHeaderValue(array $headers, $name)
|
||||
{
|
||||
$value = null;
|
||||
foreach ($headers as $header) {
|
||||
if (preg_match('{^'.preg_quote($name).':\s*(.+?)\s*$}i', $header, $match)) {
|
||||
$value = $match[1];
|
||||
} elseif (preg_match('{^HTTP/}i', $header)) {
|
||||
// TODO ideally redirects would be handled in CurlDownloader/RemoteFilesystem and this becomes unnecessary
|
||||
//
|
||||
// In case of redirects, http_response_headers contains the headers of all responses
|
||||
// so we reset the flag when a new response is being parsed as we are only interested in the last response
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -363,8 +363,6 @@ class Perforce
|
|||
while ($line !== false) {
|
||||
$line = fgets($pipe);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
public function windowsLogin($password)
|
||||
|
|
|
@ -17,6 +17,7 @@ use Composer\IO\IOInterface;
|
|||
use Composer\Downloader\TransportException;
|
||||
use Composer\CaBundle\CaBundle;
|
||||
use Composer\Util\HttpDownloader;
|
||||
use Composer\Util\Http\Response;
|
||||
|
||||
/**
|
||||
* @author François Pluchino <francois.pluchino@opendisplay.com>
|
||||
|
@ -143,27 +144,6 @@ class RemoteFilesystem
|
|||
return $this->lastHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $headers array of returned headers like from getLastHeaders()
|
||||
* @param string $name header name (case insensitive)
|
||||
* @return string|null
|
||||
*/
|
||||
public static function findHeaderValue(array $headers, $name)
|
||||
{
|
||||
$value = null;
|
||||
foreach ($headers as $header) {
|
||||
if (preg_match('{^'.$name.':\s*(.+?)\s*$}i', $header, $match)) {
|
||||
$value = $match[1];
|
||||
} elseif (preg_match('{^HTTP/}i', $header)) {
|
||||
// In case of redirects, http_response_headers contains the headers of all responses
|
||||
// so we reset the flag when a new response is being parsed as we are only interested in the last response
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $headers array of returned headers like from getLastHeaders()
|
||||
* @return int|null
|
||||
|
@ -286,13 +266,15 @@ class RemoteFilesystem
|
|||
$errorMessage .= "\n";
|
||||
}
|
||||
$errorMessage .= preg_replace('{^file_get_contents\(.*?\): }', '', $msg);
|
||||
|
||||
return true;
|
||||
});
|
||||
try {
|
||||
$result = $this->getRemoteContents($originUrl, $fileUrl, $ctx, $http_response_header);
|
||||
|
||||
if (!empty($http_response_header[0])) {
|
||||
$statusCode = $this->findStatusCode($http_response_header);
|
||||
if ($statusCode >= 400 && $this->findHeaderValue($http_response_header, 'content-type') === 'application/json') {
|
||||
if ($statusCode >= 400 && Response::findHeaderValue($http_response_header, 'content-type') === 'application/json') {
|
||||
HttpDownloader::outputWarnings($this->io, $originUrl, json_decode($result, true));
|
||||
}
|
||||
|
||||
|
@ -301,7 +283,7 @@ class RemoteFilesystem
|
|||
}
|
||||
}
|
||||
|
||||
$contentLength = !empty($http_response_header[0]) ? $this->findHeaderValue($http_response_header, 'content-length') : null;
|
||||
$contentLength = !empty($http_response_header[0]) ? Response::findHeaderValue($http_response_header, 'content-length') : null;
|
||||
if ($contentLength && Platform::strlen($result) < $contentLength) {
|
||||
// alas, this is not possible via the stream callback because STREAM_NOTIFY_COMPLETED is documented, but not implemented anywhere in PHP
|
||||
$e = new TransportException('Content-Length mismatch, received '.Platform::strlen($result).' bytes out of the expected '.$contentLength);
|
||||
|
@ -358,8 +340,8 @@ class RemoteFilesystem
|
|||
$locationHeader = null;
|
||||
if (!empty($http_response_header[0])) {
|
||||
$statusCode = $this->findStatusCode($http_response_header);
|
||||
$contentType = $this->findHeaderValue($http_response_header, 'content-type');
|
||||
$locationHeader = $this->findHeaderValue($http_response_header, 'location');
|
||||
$contentType = Response::findHeaderValue($http_response_header, 'content-type');
|
||||
$locationHeader = Response::findHeaderValue($http_response_header, 'location');
|
||||
}
|
||||
|
||||
// check for bitbucket login page asking to authenticate
|
||||
|
@ -415,7 +397,7 @@ class RemoteFilesystem
|
|||
|
||||
// decode gzip
|
||||
if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http' && !$hasFollowedRedirect) {
|
||||
$contentEncoding = $this->findHeaderValue($http_response_header, 'content-encoding');
|
||||
$contentEncoding = Response::findHeaderValue($http_response_header, 'content-encoding');
|
||||
$decode = $contentEncoding && 'gzip' === strtolower($contentEncoding);
|
||||
|
||||
if ($decode) {
|
||||
|
@ -459,6 +441,8 @@ class RemoteFilesystem
|
|||
$errorMessage .= "\n";
|
||||
}
|
||||
$errorMessage .= preg_replace('{^file_put_contents\(.*?\): }', '', $msg);
|
||||
|
||||
return true;
|
||||
});
|
||||
$result = (bool) file_put_contents($fileName, $result);
|
||||
restore_error_handler();
|
||||
|
@ -696,7 +680,7 @@ class RemoteFilesystem
|
|||
|
||||
private function handleRedirect(array $http_response_header, array $additionalOptions, $result)
|
||||
{
|
||||
if ($locationHeader = $this->findHeaderValue($http_response_header, 'location')) {
|
||||
if ($locationHeader = Response::findHeaderValue($http_response_header, 'location')) {
|
||||
if (parse_url($locationHeader, PHP_URL_SCHEME)) {
|
||||
// Absolute URL; e.g. https://example.com/composer
|
||||
$targetUrl = $locationHeader;
|
||||
|
|
|
@ -19,8 +19,6 @@ use Composer\CaBundle\CaBundle;
|
|||
*/
|
||||
final class TlsHelper
|
||||
{
|
||||
private static $useOpensslParse;
|
||||
|
||||
/**
|
||||
* Match hostname against a certificate.
|
||||
*
|
||||
|
|
|
@ -70,6 +70,9 @@ class Url
|
|||
}
|
||||
|
||||
$origin = (string) parse_url($url, PHP_URL_HOST);
|
||||
if ($port = parse_url($url, PHP_URL_PORT)) {
|
||||
$origin .= ':'.$port;
|
||||
}
|
||||
|
||||
if (strpos($origin, '.github.com') === (strlen($origin) - 11)) {
|
||||
return 'github.com';
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
<?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\Util;
|
||||
|
||||
/**
|
||||
* @author Andreas Schempp <andreas.schempp@terminal42.ch>
|
||||
*/
|
||||
class Zip
|
||||
{
|
||||
/**
|
||||
* Gets content of the root composer.json inside a ZIP archive.
|
||||
*
|
||||
* @param string $pathToZip
|
||||
* @param string $filename
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getComposerJson($pathToZip)
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
throw new \RuntimeException('The Zip Util requires PHP\'s zip extension');
|
||||
}
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
if ($zip->open($pathToZip) !== true) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (0 == $zip->numFiles) {
|
||||
$zip->close();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$foundFileIndex = self::locateFile($zip, 'composer.json');
|
||||
if (false === $foundFileIndex) {
|
||||
$zip->close();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
$content = null;
|
||||
$configurationFileName = $zip->getNameIndex($foundFileIndex);
|
||||
$stream = $zip->getStream($configurationFileName);
|
||||
|
||||
if (false !== $stream) {
|
||||
$content = stream_get_contents($stream);
|
||||
}
|
||||
|
||||
$zip->close();
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a file by name, returning the one that has the shortest path.
|
||||
*
|
||||
* @param \ZipArchive $zip
|
||||
* @param string $filename
|
||||
*
|
||||
* @return bool|int
|
||||
*/
|
||||
private static function locateFile(\ZipArchive $zip, $filename)
|
||||
{
|
||||
$indexOfShortestMatch = false;
|
||||
$lengthOfShortestMatch = -1;
|
||||
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$stat = $zip->statIndex($i);
|
||||
if (strcmp(basename($stat['name']), $filename) === 0) {
|
||||
$directoryName = dirname($stat['name']);
|
||||
if ($directoryName === '.') {
|
||||
//if composer.json is in root directory
|
||||
//it has to be the one to use.
|
||||
return $i;
|
||||
}
|
||||
|
||||
if (strpos($directoryName, '\\') !== false ||
|
||||
strpos($directoryName, '/') !== false) {
|
||||
//composer.json files below first directory are rejected
|
||||
continue;
|
||||
}
|
||||
|
||||
$length = strlen($stat['name']);
|
||||
if ($indexOfShortestMatch === false || $length < $lengthOfShortestMatch) {
|
||||
//Check it's not a directory.
|
||||
$contents = $zip->getFromIndex($i);
|
||||
if ($contents !== false) {
|
||||
$indexOfShortestMatch = $i;
|
||||
$lengthOfShortestMatch = $length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $indexOfShortestMatch;
|
||||
}
|
||||
}
|
|
@ -162,18 +162,18 @@ class AllFunctionalTest extends TestCase
|
|||
}
|
||||
};
|
||||
|
||||
for ($i = 0, $c = count($tokens); $i < $c; $i++) {
|
||||
if ('' === $tokens[$i] && null === $section) {
|
||||
foreach ($tokens as $token) {
|
||||
if ('' === $token && null === $section) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle section headers.
|
||||
if (null === $section) {
|
||||
$section = $tokens[$i];
|
||||
$section = $token;
|
||||
continue;
|
||||
}
|
||||
|
||||
$sectionData = $tokens[$i];
|
||||
$sectionData = $token;
|
||||
|
||||
// Allow sections to validate, or modify their section data.
|
||||
switch ($section) {
|
||||
|
|
|
@ -152,11 +152,4 @@ class RuleSetTest extends TestCase
|
|||
|
||||
$this->assertContains('JOB : Install command rule (install foo 2.1)', $ruleSet->getPrettyString($pool));
|
||||
}
|
||||
|
||||
private function getRuleMock()
|
||||
{
|
||||
return $this->getMockBuilder('Composer\DependencyResolver\Rule')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ class FossilDownloaderTest extends TestCase
|
|||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testDownloadForPackageWithoutSourceReference()
|
||||
public function testInstallForPackageWithoutSourceReference()
|
||||
{
|
||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||
$packageMock->expects($this->once())
|
||||
|
@ -59,7 +59,7 @@ class FossilDownloaderTest extends TestCase
|
|||
$downloader->install($packageMock, '/path');
|
||||
}
|
||||
|
||||
public function testDownload()
|
||||
public function testInstall()
|
||||
{
|
||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||
$packageMock->expects($this->any())
|
||||
|
@ -104,7 +104,9 @@ class FossilDownloaderTest extends TestCase
|
|||
->will($this->returnValue(null));
|
||||
|
||||
$downloader = $this->getDownloaderMock();
|
||||
$downloader->prepare('update', $sourcePackageMock, '/path', $initialPackageMock);
|
||||
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
|
||||
$downloader->cleanup('update', $sourcePackageMock, '/path', $initialPackageMock);
|
||||
}
|
||||
|
||||
public function testUpdate()
|
||||
|
@ -140,7 +142,9 @@ class FossilDownloaderTest extends TestCase
|
|||
->will($this->returnValue(0));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
|
||||
$downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->update($packageMock, $packageMock, $this->workingDir);
|
||||
$downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
|
|
|
@ -17,6 +17,7 @@ use Composer\Config;
|
|||
use Composer\Test\TestCase;
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\Platform;
|
||||
use Prophecy\Argument;
|
||||
|
||||
class GitDownloaderTest extends TestCase
|
||||
{
|
||||
|
@ -79,7 +80,10 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue(null));
|
||||
|
||||
$downloader = $this->getDownloaderMock();
|
||||
$downloader->download($packageMock, '/path');
|
||||
$downloader->prepare('install', $packageMock, '/path');
|
||||
$downloader->install($packageMock, '/path');
|
||||
$downloader->cleanup('install', $packageMock, '/path');
|
||||
}
|
||||
|
||||
public function testDownload()
|
||||
|
@ -130,7 +134,10 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue(0));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
|
||||
$downloader->download($packageMock, 'composerPath');
|
||||
$downloader->prepare('install', $packageMock, 'composerPath');
|
||||
$downloader->install($packageMock, 'composerPath');
|
||||
$downloader->cleanup('install', $packageMock, 'composerPath');
|
||||
}
|
||||
|
||||
public function testDownloadWithCache()
|
||||
|
@ -195,7 +202,10 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue(0));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, $config, $processExecutor);
|
||||
$downloader->download($packageMock, 'composerPath');
|
||||
$downloader->prepare('install', $packageMock, 'composerPath');
|
||||
$downloader->install($packageMock, 'composerPath');
|
||||
$downloader->cleanup('install', $packageMock, 'composerPath');
|
||||
@rmdir($cachePath);
|
||||
}
|
||||
|
||||
|
@ -265,7 +275,10 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue(0));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
|
||||
$downloader->download($packageMock, 'composerPath');
|
||||
$downloader->prepare('install', $packageMock, 'composerPath');
|
||||
$downloader->install($packageMock, 'composerPath');
|
||||
$downloader->cleanup('install', $packageMock, 'composerPath');
|
||||
}
|
||||
|
||||
public function pushUrlProvider()
|
||||
|
@ -329,12 +342,12 @@ class GitDownloaderTest extends TestCase
|
|||
$config->merge(array('config' => array('github-protocols' => $protocols)));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, $config, $processExecutor);
|
||||
$downloader->download($packageMock, 'composerPath');
|
||||
$downloader->prepare('install', $packageMock, 'composerPath');
|
||||
$downloader->install($packageMock, 'composerPath');
|
||||
$downloader->cleanup('install', $packageMock, 'composerPath');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testDownloadThrowsRuntimeExceptionIfGitCommandFails()
|
||||
{
|
||||
$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");
|
||||
|
@ -359,8 +372,20 @@ class GitDownloaderTest extends TestCase
|
|||
->with($this->equalTo($expectedGitCommand))
|
||||
->will($this->returnValue(1));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
|
||||
$downloader->install($packageMock, 'composerPath');
|
||||
// not using PHPUnit's expected exception because Prophecy exceptions extend from RuntimeException too so it is not safe
|
||||
try {
|
||||
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
|
||||
$downloader->download($packageMock, 'composerPath');
|
||||
$downloader->prepare('install', $packageMock, 'composerPath');
|
||||
$downloader->install($packageMock, 'composerPath');
|
||||
$downloader->cleanup('install', $packageMock, 'composerPath');
|
||||
$this->fail('This test should throw');
|
||||
} catch (\RuntimeException $e) {
|
||||
if ('RuntimeException' !== get_class($e)) {
|
||||
throw $e;
|
||||
}
|
||||
$this->assertEquals('RuntimeException', get_class($e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -375,7 +400,10 @@ class GitDownloaderTest extends TestCase
|
|||
->will($this->returnValue(null));
|
||||
|
||||
$downloader = $this->getDownloaderMock();
|
||||
$downloader->download($sourcePackageMock, '/path', $initialPackageMock);
|
||||
$downloader->prepare('update', $sourcePackageMock, '/path', $initialPackageMock);
|
||||
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
|
||||
$downloader->cleanup('update', $sourcePackageMock, '/path', $initialPackageMock);
|
||||
}
|
||||
|
||||
public function testUpdate()
|
||||
|
@ -392,39 +420,22 @@ class GitDownloaderTest extends TestCase
|
|||
$packageMock->expects($this->any())
|
||||
->method('getVersion')
|
||||
->will($this->returnValue('1.0.0.0'));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->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 status --porcelain --untracked-files=no")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->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")))
|
||||
->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)))
|
||||
->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));
|
||||
|
||||
$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);
|
||||
$process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
|
||||
$process->execute($expectedGitUpdateCommand, null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
|
||||
$process->execute($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --"), null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
|
||||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
|
||||
$downloader->download($packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->update($packageMock, $packageMock, $this->workingDir);
|
||||
$downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
|
||||
}
|
||||
|
||||
public function testUpdateWithNewRepoUrl()
|
||||
|
@ -444,27 +455,20 @@ class GitDownloaderTest extends TestCase
|
|||
$packageMock->expects($this->any())
|
||||
->method('getVersion')
|
||||
->will($this->returnValue('1.0.0.0'));
|
||||
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git show-ref --head -d")))
|
||||
->with($this->equalTo($this->winCompat("git --version")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(1))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
|
||||
->with($this->equalTo($this->winCompat("git show-ref --head -d")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
->will($this->returnCallback(function ($cmd, &$output, $cwd) {
|
||||
$output = 'origin https://github.com/old/url (fetch)
|
||||
origin https://github.com/old/url (push)
|
||||
composer https://github.com/old/url (fetch)
|
||||
composer https://github.com/old/url (push)
|
||||
';
|
||||
|
||||
return 0;
|
||||
}));
|
||||
->with($this->equalTo($this->winCompat("git status --porcelain --untracked-files=no")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(3))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
|
@ -482,26 +486,41 @@ composer https://github.com/old/url (push)
|
|||
->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))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
->will($this->returnCallback(function ($cmd, &$output, $cwd) {
|
||||
$output = 'origin https://github.com/old/url (fetch)
|
||||
origin https://github.com/old/url (push)
|
||||
composer https://github.com/old/url (fetch)
|
||||
composer https://github.com/old/url (push)
|
||||
';
|
||||
|
||||
return 0;
|
||||
}));
|
||||
$processExecutor->expects($this->at(8))
|
||||
->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(8))
|
||||
$processExecutor->expects($this->at(9))
|
||||
->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));
|
||||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
|
||||
$downloader->download($packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->update($packageMock, $packageMock, $this->workingDir);
|
||||
$downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @group failing
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testUpdateThrowsRuntimeExceptionIfGitCommandFails()
|
||||
{
|
||||
$expectedGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)");
|
||||
$expectedGitUpdateCommand2 = $this->winCompat("git remote set-url composer 'git@github.com:composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)");
|
||||
|
||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||
$packageMock->expects($this->any())
|
||||
|
@ -513,36 +532,38 @@ composer https://github.com/old/url (push)
|
|||
$packageMock->expects($this->any())
|
||||
->method('getVersion')
|
||||
->will($this->returnValue('1.0.0.0'));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->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 status --porcelain --untracked-files=no")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->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")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(4))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedGitUpdateCommand))
|
||||
->will($this->returnValue(1));
|
||||
|
||||
$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);
|
||||
$process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
|
||||
$process->execute($expectedGitUpdateCommand, null, $this->winCompat($this->workingDir))->willReturn(1)->shouldBeCalled();
|
||||
$process->execute($expectedGitUpdateCommand2, null, $this->winCompat($this->workingDir))->willReturn(1)->shouldBeCalled();
|
||||
$process->getErrorOutput()->willReturn('');
|
||||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
|
||||
$downloader->update($packageMock, $packageMock, $this->workingDir);
|
||||
|
||||
// not using PHPUnit's expected exception because Prophecy exceptions extend from RuntimeException too so it is not safe
|
||||
try {
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
|
||||
$downloader->download($packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->update($packageMock, $packageMock, $this->workingDir);
|
||||
$downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
|
||||
$this->fail('This test should throw');
|
||||
} catch (\RuntimeException $e) {
|
||||
if ('RuntimeException' !== get_class($e)) {
|
||||
throw $e;
|
||||
}
|
||||
$this->assertEquals('RuntimeException', get_class($e));
|
||||
}
|
||||
}
|
||||
|
||||
public function testUpdateDoesntThrowsRuntimeExceptionIfGitCommandFailsAtFirstButIsAbleToRecover()
|
||||
{
|
||||
$expectedFirstGitUpdateCommand = $this->winCompat("git remote set-url composer '' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)");
|
||||
$expectedFirstGitUpdateCommand = $this->winCompat("git remote set-url composer '".(Platform::isWindows() ? 'C:\\' : '/')."' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)");
|
||||
$expectedSecondGitUpdateCommand = $this->winCompat("git remote set-url composer 'https://github.com/composer/composer' && git rev-parse --quiet --verify 'ref^{commit}' || (git fetch composer && git fetch --tags composer)");
|
||||
|
||||
$packageMock = $this->getMockBuilder('Composer\Package\PackageInterface')->getMock();
|
||||
|
@ -554,52 +575,24 @@ composer https://github.com/old/url (push)
|
|||
->will($this->returnValue('1.0.0.0'));
|
||||
$packageMock->expects($this->any())
|
||||
->method('getSourceUrls')
|
||||
->will($this->returnValue(array('/foo/bar', 'https://github.com/composer/composer')));
|
||||
$processExecutor = $this->getMockBuilder('Composer\Util\ProcessExecutor')->getMock();
|
||||
$processExecutor->expects($this->at(0))
|
||||
->method('execute')
|
||||
->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 status --porcelain --untracked-files=no")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(2))
|
||||
->method('execute')
|
||||
->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")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(4))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedFirstGitUpdateCommand))
|
||||
->will($this->returnValue(1));
|
||||
$processExecutor->expects($this->at(6))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git --version")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(7))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(8))
|
||||
->method('execute')
|
||||
->with($this->equalTo($this->winCompat("git remote -v")))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(9))
|
||||
->method('execute')
|
||||
->with($this->equalTo($expectedSecondGitUpdateCommand))
|
||||
->will($this->returnValue(0));
|
||||
$processExecutor->expects($this->at(11))
|
||||
->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));
|
||||
->will($this->returnValue(array(Platform::isWindows() ? 'C:\\' : '/', 'https://github.com/composer/composer')));
|
||||
|
||||
$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);
|
||||
$process->execute($this->winCompat('git branch -r'), Argument::cetera())->willReturn(0);
|
||||
$process->execute($expectedFirstGitUpdateCommand, Argument::cetera())->willReturn(1)->shouldBeCalled();
|
||||
$process->execute($expectedSecondGitUpdateCommand, Argument::cetera())->willReturn(0)->shouldBeCalled();
|
||||
$process->execute($this->winCompat("git checkout 'ref' -- && git reset --hard 'ref' --"), null, $this->winCompat($this->workingDir))->willReturn(0)->shouldBeCalled();
|
||||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $processExecutor);
|
||||
$downloader = $this->getDownloaderMock(null, new Config(), $process->reveal());
|
||||
$downloader->download($packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->update($packageMock, $packageMock, $this->workingDir);
|
||||
$downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
|
||||
}
|
||||
|
||||
public function testDowngradeShowsAppropriateMessage()
|
||||
|
@ -644,7 +637,10 @@ composer https://github.com/old/url (push)
|
|||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock($ioMock, null, $processExecutor);
|
||||
$downloader->download($newPackage, $this->workingDir, $oldPackage);
|
||||
$downloader->prepare('update', $newPackage, $this->workingDir, $oldPackage);
|
||||
$downloader->update($oldPackage, $newPackage, $this->workingDir);
|
||||
$downloader->cleanup('update', $newPackage, $this->workingDir, $oldPackage);
|
||||
}
|
||||
|
||||
public function testNotUsingDowngradingWithReferences()
|
||||
|
@ -679,11 +675,14 @@ composer https://github.com/old/url (push)
|
|||
$ioMock = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$ioMock->expects($this->at(0))
|
||||
->method('writeError')
|
||||
->with($this->stringContains('updating'));
|
||||
->with($this->stringContains('Updating'));
|
||||
|
||||
$this->fs->ensureDirectoryExists($this->workingDir.'/.git');
|
||||
$downloader = $this->getDownloaderMock($ioMock, null, $processExecutor);
|
||||
$downloader->download($newPackage, $this->workingDir, $oldPackage);
|
||||
$downloader->prepare('update', $newPackage, $this->workingDir, $oldPackage);
|
||||
$downloader->update($oldPackage, $newPackage, $this->workingDir);
|
||||
$downloader->cleanup('update', $newPackage, $this->workingDir, $oldPackage);
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
|
@ -703,7 +702,9 @@ composer https://github.com/old/url (push)
|
|||
->will($this->returnValue(true));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, null, $processExecutor, $filesystem);
|
||||
$downloader->prepare('uninstall', $packageMock, 'composerPath');
|
||||
$downloader->remove($packageMock, 'composerPath');
|
||||
$downloader->cleanup('uninstall', $packageMock, 'composerPath');
|
||||
}
|
||||
|
||||
public function testGetInstallationSource()
|
||||
|
|
|
@ -98,7 +98,9 @@ class HgDownloaderTest extends TestCase
|
|||
->will($this->returnValue(null));
|
||||
|
||||
$downloader = $this->getDownloaderMock();
|
||||
$downloader->prepare('update', $sourcePackageMock, '/path', $initialPackageMock);
|
||||
$downloader->update($initialPackageMock, $sourcePackageMock, '/path');
|
||||
$downloader->cleanup('update', $sourcePackageMock, '/path', $initialPackageMock);
|
||||
}
|
||||
|
||||
public function testUpdate()
|
||||
|
@ -129,7 +131,9 @@ class HgDownloaderTest extends TestCase
|
|||
->will($this->returnValue(0));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, null, $processExecutor);
|
||||
$downloader->prepare('update', $packageMock, $this->workingDir, $packageMock);
|
||||
$downloader->update($packageMock, $packageMock, $this->workingDir);
|
||||
$downloader->cleanup('update', $packageMock, $this->workingDir, $packageMock);
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
|
@ -148,7 +152,9 @@ class HgDownloaderTest extends TestCase
|
|||
->will($this->returnValue(true));
|
||||
|
||||
$downloader = $this->getDownloaderMock(null, null, $processExecutor, $filesystem);
|
||||
$downloader->prepare('uninstall', $packageMock, 'composerPath');
|
||||
$downloader->remove($packageMock, 'composerPath');
|
||||
$downloader->cleanup('uninstall', $packageMock, 'composerPath');
|
||||
}
|
||||
|
||||
public function testGetInstallationSource()
|
||||
|
|
|
@ -338,7 +338,7 @@ class ZipDownloaderTest extends TestCase
|
|||
|
||||
class MockedZipDownloader extends ZipDownloader
|
||||
{
|
||||
public function download(PackageInterface $package, $path, $output = true)
|
||||
public function download(PackageInterface $package, $path, PackageInterface $prevPackage = null, $output = true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -17,37 +17,38 @@ 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/zipball/2222222222222222222222222222222222222222", "type": "zip" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
},
|
||||
{
|
||||
"name": "c/c", "version": "1.0.0",
|
||||
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/c/newc", "type": "git" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/c/newc/zipball/2222222222222222222222222222222222222222", "type": "zip" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/c/newc/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
},
|
||||
{
|
||||
"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/zipball/2222222222222222222222222222222222222222", "type": "zip" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/d/newd/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
},
|
||||
{
|
||||
"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/zipball/2222222222222222222222222222222222222222", "type": "zip" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/e/newe/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
},
|
||||
{
|
||||
"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/zipball/2222222222222222222222222222222222222222", "type": "zip" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/f/newf/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"transport-options": { "foo": "bar2" }
|
||||
},
|
||||
{
|
||||
"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/zipball/2222222222222222222222222222222222222222", "type": "zip" }
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/g/newg/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -67,32 +68,34 @@ 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" }
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" }
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/b", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip" }
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" }
|
||||
},
|
||||
{
|
||||
"name": "c/c", "version": "1.0.0",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" }
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" }
|
||||
},
|
||||
{
|
||||
"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" }
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" }
|
||||
},
|
||||
{
|
||||
"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" }
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"transport-options": { "foo": "bar" }
|
||||
},
|
||||
{
|
||||
"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" }
|
||||
"dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip", "shasum": "oldsum" },
|
||||
"transport-options": { "foo": "bar" }
|
||||
}
|
||||
]
|
||||
--LOCK--
|
||||
|
@ -101,38 +104,40 @@ 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" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/a/a/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/b/b", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/b/b/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "c/c", "version": "1.0.0",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"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" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/d/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"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" },
|
||||
"type": "library"
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library",
|
||||
"transport-options": { "foo": "bar" }
|
||||
},
|
||||
{
|
||||
"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" },
|
||||
"type": "library"
|
||||
"dist": { "reference": "0000000000000000000000000000000000000000", "url": "https://api.github.com/repos/g/g/zipball/0000000000000000000000000000000000000000", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library",
|
||||
"transport-options": { "foo": "bar" }
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
|
@ -150,43 +155,44 @@ 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/zipball/2222222222222222222222222222222222222222", "type": "zip" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/a/newa/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "b/b", "version": "2.0.3",
|
||||
"source": { "reference": "2222222222222222222222222222222222222222", "url": "https://github.com/b/newb", "type": "git" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/zipball/2222222222222222222222222222222222222222", "type": "zip" },
|
||||
"dist": { "reference": "2222222222222222222222222222222222222222", "url": "https://api.github.com/repos/b/newb/tarball/2222222222222222222222222222222222222222", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "c/c", "version": "1.0.0",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/c/c", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/c/c/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"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/zipball/1111111111111111111111111111111111111111", "type": "zip" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/d/newd/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"name": "e/e", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/e/newe", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/e/newe/zipball/1111111111111111111111111111111111111111", "type": "zip" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/e/newe/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
},
|
||||
{
|
||||
"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" },
|
||||
"type": "library"
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/f/f/zipball/1111111111111111111111111111111111111111", "type": "zip", "shasum": "oldsum" },
|
||||
"type": "library",
|
||||
"transport-options": { "foo": "bar" }
|
||||
},
|
||||
{
|
||||
"name": "g/g", "version": "dev-master",
|
||||
"source": { "reference": "1111111111111111111111111111111111111111", "url": "https://github.com/g/newg", "type": "git" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/g/newg/zipball/1111111111111111111111111111111111111111", "type": "zip" },
|
||||
"dist": { "reference": "1111111111111111111111111111111111111111", "url": "https://api.github.com/repos/g/newg/tarball/1111111111111111111111111111111111111111", "type": "tar", "shasum": "newsum" },
|
||||
"type": "library"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -246,7 +246,7 @@ class InstallerTest extends TestCase
|
|||
}
|
||||
|
||||
$contents = json_encode($composerConfig);
|
||||
$locker = new Locker($io, $lockJsonMock, $repositoryManager, $composer->getInstallationManager(), $contents);
|
||||
$locker = new Locker($io, $lockJsonMock, $composer->getInstallationManager(), $contents);
|
||||
$composer->setLocker($locker);
|
||||
|
||||
$eventDispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
|
||||
|
|
|
@ -1448,6 +1448,22 @@ class JsonManipulatorTest extends TestCase
|
|||
"repositories": {
|
||||
}
|
||||
}
|
||||
',
|
||||
),
|
||||
'works on simple ones escaped slash' => array(
|
||||
'{
|
||||
"repositories": {
|
||||
"foo\/bar": {
|
||||
"bar": "baz"
|
||||
}
|
||||
}
|
||||
}',
|
||||
'foo/bar',
|
||||
true,
|
||||
'{
|
||||
"repositories": {
|
||||
}
|
||||
}
|
||||
',
|
||||
),
|
||||
'works on simple ones middle' => array(
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Test\Mock;
|
||||
|
||||
use Composer\Repository\InstalledFilesystemRepository;
|
||||
use Composer\Installer\InstallationManager;
|
||||
|
||||
class InstalledFilesystemRepositoryMock extends InstalledFilesystemRepository
|
||||
{
|
||||
|
@ -20,7 +21,7 @@ class InstalledFilesystemRepositoryMock extends InstalledFilesystemRepository
|
|||
{
|
||||
}
|
||||
|
||||
public function write()
|
||||
public function write($devMode, InstallationManager $installationManager)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,7 +148,6 @@ class ArrayLoaderTest extends TestCase
|
|||
{
|
||||
$package = $this->loader->load($config);
|
||||
$dumper = new ArrayDumper;
|
||||
$expectedConfig = $config;
|
||||
$expectedConfig = $this->fixConfigWhenLoadConfigIsFalse($config);
|
||||
$this->assertEquals($expectedConfig, $dumper->dump($package));
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ class LockerTest extends TestCase
|
|||
$locker = new Locker(
|
||||
new NullIO,
|
||||
$json,
|
||||
$this->createRepositoryManagerMock(),
|
||||
$this->createInstallationManagerMock(),
|
||||
$this->getJsonContent()
|
||||
);
|
||||
|
@ -44,10 +43,9 @@ class LockerTest extends TestCase
|
|||
public function testGetNotLockedPackages()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
|
||||
$locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
|
||||
|
||||
$json
|
||||
->expects($this->once())
|
||||
|
@ -62,10 +60,9 @@ class LockerTest extends TestCase
|
|||
public function testGetLockedPackages()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
|
||||
$locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
|
||||
|
||||
$json
|
||||
->expects($this->once())
|
||||
|
@ -89,11 +86,10 @@ class LockerTest extends TestCase
|
|||
public function testSetLockData()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$jsonContent = $this->getJsonContent() . ' ';
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
|
||||
$locker = new Locker(new NullIO, $json, $inst, $jsonContent);
|
||||
|
||||
$package1 = $this->createPackageMock();
|
||||
$package2 = $this->createPackageMock();
|
||||
|
@ -162,10 +158,9 @@ class LockerTest extends TestCase
|
|||
public function testLockBadPackages()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
|
||||
$locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
|
||||
|
||||
$package1 = $this->createPackageMock();
|
||||
$package1
|
||||
|
@ -181,11 +176,10 @@ class LockerTest extends TestCase
|
|||
public function testIsFresh()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$jsonContent = $this->getJsonContent();
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
|
||||
$locker = new Locker(new NullIO, $json, $inst, $jsonContent);
|
||||
|
||||
$json
|
||||
->expects($this->once())
|
||||
|
@ -198,10 +192,9 @@ class LockerTest extends TestCase
|
|||
public function testIsFreshFalse()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
|
||||
$locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
|
||||
|
||||
$json
|
||||
->expects($this->once())
|
||||
|
@ -214,11 +207,10 @@ class LockerTest extends TestCase
|
|||
public function testIsFreshWithContentHash()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$jsonContent = $this->getJsonContent();
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
|
||||
$locker = new Locker(new NullIO, $json, $inst, $jsonContent);
|
||||
|
||||
$json
|
||||
->expects($this->once())
|
||||
|
@ -231,11 +223,10 @@ class LockerTest extends TestCase
|
|||
public function testIsFreshWithContentHashAndNoHash()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$jsonContent = $this->getJsonContent();
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $jsonContent);
|
||||
$locker = new Locker(new NullIO, $json, $inst, $jsonContent);
|
||||
|
||||
$json
|
||||
->expects($this->once())
|
||||
|
@ -248,10 +239,9 @@ class LockerTest extends TestCase
|
|||
public function testIsFreshFalseWithContentHash()
|
||||
{
|
||||
$json = $this->createJsonFileMock();
|
||||
$repo = $this->createRepositoryManagerMock();
|
||||
$inst = $this->createInstallationManagerMock();
|
||||
|
||||
$locker = new Locker(new NullIO, $json, $repo, $inst, $this->getJsonContent());
|
||||
$locker = new Locker(new NullIO, $json, $inst, $this->getJsonContent());
|
||||
|
||||
$differentHash = md5($this->getJsonContent(array('name' => 'test2')));
|
||||
|
||||
|
@ -270,19 +260,6 @@ class LockerTest extends TestCase
|
|||
->getMock();
|
||||
}
|
||||
|
||||
private function createRepositoryManagerMock()
|
||||
{
|
||||
$mock = $this->getMockBuilder('Composer\Repository\RepositoryManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getLocalRepository')
|
||||
->will($this->returnValue($this->getMockBuilder('Composer\Repository\ArrayRepository')->getMock()));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
private function createInstallationManagerMock()
|
||||
{
|
||||
$mock = $this->getMockBuilder('Composer\Installer\InstallationManager')
|
||||
|
|
|
@ -12,5 +12,16 @@ class Plugin implements PluginInterface
|
|||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v1');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v1');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v1');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,16 @@ class Plugin2 implements PluginInterface
|
|||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v2');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v2');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v2');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,16 @@ class Plugin2 implements PluginInterface
|
|||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v3');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v3');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v3');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,5 +13,16 @@ class Plugin1 implements PluginInterface
|
|||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v4-plugin1');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v4-plugin1');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v4-plugin1');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,5 +13,16 @@ class Plugin2 implements PluginInterface
|
|||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v4-plugin2');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v4-plugin2');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v4-plugin2');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,16 @@ class Plugin5 implements PluginInterface
|
|||
{
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v5');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v5');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v5');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,16 @@ class Plugin6 implements PluginInterface
|
|||
{
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v6');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v6');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v6');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,5 +10,16 @@ class Plugin7 implements PluginInterface
|
|||
{
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v7');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v7');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v7');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,17 @@ class Plugin8 implements PluginInterface, Capable
|
|||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v8');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v8');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v8');
|
||||
}
|
||||
|
||||
public function getCapabilities()
|
||||
|
|
|
@ -14,5 +14,16 @@ class Plugin implements PluginInterface
|
|||
|
||||
public function activate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('activate v9');
|
||||
}
|
||||
|
||||
public function deactivate(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('deactivate v9');
|
||||
}
|
||||
|
||||
public function uninstall(Composer $composer, IOInterface $io)
|
||||
{
|
||||
$io->write('uninstall v9');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ use Composer\Package\CompletePackage;
|
|||
use Composer\Package\Loader\JsonLoader;
|
||||
use Composer\Package\Loader\ArrayLoader;
|
||||
use Composer\Plugin\PluginManager;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Composer\IO\BufferIO;
|
||||
use Composer\EventDispatcher\EventDispatcher;
|
||||
use Composer\Autoload\AutoloadGenerator;
|
||||
use Composer\Test\TestCase;
|
||||
use Composer\Util\Filesystem;
|
||||
|
@ -96,7 +99,7 @@ class PluginInstallerTest extends TestCase
|
|||
return __DIR__.'/Fixtures/'.$package->getPrettyName();
|
||||
}));
|
||||
|
||||
$this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$this->io = new BufferIO();
|
||||
|
||||
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock();
|
||||
$this->autoloadGenerator = new AutoloadGenerator($dispatcher);
|
||||
|
@ -108,6 +111,7 @@ class PluginInstallerTest extends TestCase
|
|||
$this->composer->setRepositoryManager($rm);
|
||||
$this->composer->setInstallationManager($im);
|
||||
$this->composer->setAutoloadGenerator($this->autoloadGenerator);
|
||||
$this->composer->setEventDispatcher(new EventDispatcher($this->composer, $this->io));
|
||||
|
||||
$this->pm = new PluginManager($this->io, $this->composer);
|
||||
$this->composer->setPluginManager($this->pm);
|
||||
|
@ -140,6 +144,7 @@ class PluginInstallerTest extends TestCase
|
|||
|
||||
$plugins = $this->pm->getPlugins();
|
||||
$this->assertEquals('installer-v1', $plugins[0]->version);
|
||||
$this->assertEquals('activate v1'.PHP_EOL, $this->io->getOutput());
|
||||
}
|
||||
|
||||
public function testInstallMultiplePlugins()
|
||||
|
@ -158,6 +163,7 @@ class PluginInstallerTest extends TestCase
|
|||
$this->assertEquals('installer-v4', $plugins[0]->version);
|
||||
$this->assertEquals('plugin2', $plugins[1]->name);
|
||||
$this->assertEquals('installer-v4', $plugins[1]->version);
|
||||
$this->assertEquals('activate v4-plugin1'.PHP_EOL.'activate v4-plugin2'.PHP_EOL, $this->io->getOutput());
|
||||
}
|
||||
|
||||
public function testUpgradeWithNewClassName()
|
||||
|
@ -176,7 +182,29 @@ class PluginInstallerTest extends TestCase
|
|||
$installer->update($this->repository, $this->packages[0], $this->packages[1]);
|
||||
|
||||
$plugins = $this->pm->getPlugins();
|
||||
$this->assertCount(1, $plugins);
|
||||
$this->assertEquals('installer-v2', $plugins[1]->version);
|
||||
$this->assertEquals('activate v1'.PHP_EOL.'deactivate v1'.PHP_EOL.'activate v2'.PHP_EOL, $this->io->getOutput());
|
||||
}
|
||||
|
||||
public function testUninstall()
|
||||
{
|
||||
$this->repository
|
||||
->expects($this->once())
|
||||
->method('getPackages')
|
||||
->will($this->returnValue(array($this->packages[0])));
|
||||
$this->repository
|
||||
->expects($this->exactly(1))
|
||||
->method('hasPackage')
|
||||
->will($this->onConsecutiveCalls(true, false));
|
||||
$installer = new PluginInstaller($this->io, $this->composer);
|
||||
$this->pm->loadInstalledPlugins();
|
||||
|
||||
$installer->uninstall($this->repository, $this->packages[0]);
|
||||
|
||||
$plugins = $this->pm->getPlugins();
|
||||
$this->assertCount(0, $plugins);
|
||||
$this->assertEquals('activate v1'.PHP_EOL.'deactivate v1'.PHP_EOL.'uninstall v1'.PHP_EOL, $this->io->getOutput());
|
||||
}
|
||||
|
||||
public function testUpgradeWithSameClassName()
|
||||
|
@ -196,6 +224,7 @@ class PluginInstallerTest extends TestCase
|
|||
|
||||
$plugins = $this->pm->getPlugins();
|
||||
$this->assertEquals('installer-v3', $plugins[1]->version);
|
||||
$this->assertEquals('activate v2'.PHP_EOL.'deactivate v2'.PHP_EOL.'activate v3'.PHP_EOL, $this->io->getOutput());
|
||||
}
|
||||
|
||||
public function testRegisterPluginOnlyOneTime()
|
||||
|
@ -213,6 +242,7 @@ class PluginInstallerTest extends TestCase
|
|||
$plugins = $this->pm->getPlugins();
|
||||
$this->assertCount(1, $plugins);
|
||||
$this->assertEquals('installer-v1', $plugins[0]->version);
|
||||
$this->assertEquals('activate v1'.PHP_EOL, $this->io->getOutput());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -227,7 +227,9 @@ class ComposerRepositoryTest extends TestCase
|
|||
$repository = new ComposerRepository(
|
||||
array('url' => $repositoryUrl),
|
||||
new NullIO(),
|
||||
FactoryMock::createConfig()
|
||||
FactoryMock::createConfig(),
|
||||
$this->getMockBuilder('Composer\Util\HttpDownloader')->disableOriginalConstructor()->getMock(),
|
||||
$this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')->disableOriginalConstructor()->getMock()
|
||||
);
|
||||
|
||||
$object = new \ReflectionObject($repository);
|
||||
|
|
|
@ -82,11 +82,21 @@ class FilesystemRepositoryTest extends TestCase
|
|||
$json = $this->createJsonFileMock();
|
||||
|
||||
$repository = new FilesystemRepository($json);
|
||||
$im = $this->getMockBuilder('Composer\Installer\InstallationManager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$im->expects($this->once())
|
||||
->method('getInstallPath')
|
||||
->will($this->returnValue('/foo/bar/vendor/woop/woop'));
|
||||
|
||||
$json
|
||||
->expects($this->once())
|
||||
->method('read')
|
||||
->will($this->returnValue(array()));
|
||||
$json
|
||||
->expects($this->once())
|
||||
->method('getPath')
|
||||
->will($this->returnValue('/foo/bar/vendor/composer/installed.json'));
|
||||
$json
|
||||
->expects($this->once())
|
||||
->method('exists')
|
||||
|
@ -95,11 +105,12 @@ class FilesystemRepositoryTest extends TestCase
|
|||
->expects($this->once())
|
||||
->method('write')
|
||||
->with(array(
|
||||
array('name' => 'mypkg', 'type' => 'library', 'version' => '0.1.10', 'version_normalized' => '0.1.10.0'),
|
||||
'packages' => array(array('name' => 'mypkg', 'type' => 'library', 'version' => '0.1.10', 'version_normalized' => '0.1.10.0', 'install-path' => '../woop/woop')),
|
||||
'dev' => true,
|
||||
));
|
||||
|
||||
$repository->addPackage($this->getPackage('mypkg', '0.1.10'));
|
||||
$repository->write();
|
||||
$repository->write(true, $im);
|
||||
}
|
||||
|
||||
private function createJsonFileMock()
|
||||
|
|
|
@ -40,15 +40,6 @@ class FossilDriverTest extends TestCase
|
|||
$fs->removeDirectory($this->home);
|
||||
}
|
||||
|
||||
private function getCmd($cmd)
|
||||
{
|
||||
if (Platform::isWindows()) {
|
||||
return strtr($cmd, "'", '"');
|
||||
}
|
||||
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
public static function supportProvider()
|
||||
{
|
||||
return array(
|
||||
|
|
|
@ -71,15 +71,6 @@ class SvnDriverTest extends TestCase
|
|||
$svn->initialize();
|
||||
}
|
||||
|
||||
private function getCmd($cmd)
|
||||
{
|
||||
if (Platform::isWindows()) {
|
||||
return strtr($cmd, "'", '"');
|
||||
}
|
||||
|
||||
return $cmd;
|
||||
}
|
||||
|
||||
public static function supportProvider()
|
||||
{
|
||||
return array(
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
<?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\Script;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Config;
|
||||
use Composer\Script\Event;
|
||||
use Composer\Test\TestCase;
|
||||
|
||||
class EventTest extends TestCase
|
||||
{
|
||||
public function testEventSetsOriginatingEvent()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$composer = $this->createComposerInstance();
|
||||
|
||||
$originatingEvent = new \Composer\EventDispatcher\Event('originatingEvent');
|
||||
|
||||
$scriptEvent = new Event('test', $composer, $io, true);
|
||||
|
||||
$this->assertNull(
|
||||
$scriptEvent->getOriginatingEvent(),
|
||||
'originatingEvent is initialized as null'
|
||||
);
|
||||
|
||||
$scriptEvent->setOriginatingEvent($originatingEvent);
|
||||
|
||||
$this->assertSame(
|
||||
$originatingEvent,
|
||||
$scriptEvent->getOriginatingEvent(),
|
||||
'getOriginatingEvent() SHOULD return test event'
|
||||
);
|
||||
}
|
||||
|
||||
public function testEventCalculatesNestedOriginatingEvent()
|
||||
{
|
||||
$io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||
$composer = $this->createComposerInstance();
|
||||
|
||||
$originatingEvent = new \Composer\EventDispatcher\Event('upperOriginatingEvent');
|
||||
$intermediateEvent = new Event('intermediate', $composer, $io, true);
|
||||
$intermediateEvent->setOriginatingEvent($originatingEvent);
|
||||
|
||||
$scriptEvent = new Event('test', $composer, $io, true);
|
||||
$scriptEvent->setOriginatingEvent($intermediateEvent);
|
||||
|
||||
$this->assertNotSame(
|
||||
$intermediateEvent,
|
||||
$scriptEvent->getOriginatingEvent(),
|
||||
'getOriginatingEvent() SHOULD NOT return intermediate events'
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
$originatingEvent,
|
||||
$scriptEvent->getOriginatingEvent(),
|
||||
'getOriginatingEvent() SHOULD return upper-most event'
|
||||
);
|
||||
}
|
||||
|
||||
private function createComposerInstance()
|
||||
{
|
||||
$composer = new Composer;
|
||||
$config = new Config;
|
||||
$composer->setConfig($config);
|
||||
$package = $this->getMockBuilder('Composer\Package\RootPackageInterface')->getMock();
|
||||
$composer->setPackage($package);
|
||||
|
||||
return $composer;
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -24,12 +24,9 @@ use RecursiveIteratorIterator;
|
|||
*/
|
||||
class GitHubTest extends TestCase
|
||||
{
|
||||
private $username = 'username';
|
||||
private $password = 'password';
|
||||
private $authcode = 'authcode';
|
||||
private $message = 'mymessage';
|
||||
private $origin = 'github.com';
|
||||
private $token = 'githubtoken';
|
||||
|
||||
public function testUsernamePasswordAuthenticationFlow()
|
||||
{
|
||||
|
|
|
@ -24,7 +24,6 @@ class GitLabTest extends TestCase
|
|||
{
|
||||
private $username = 'username';
|
||||
private $password = 'password';
|
||||
private $authcode = 'authcode';
|
||||
private $message = 'mymessage';
|
||||
private $origin = 'gitlab.com';
|
||||
private $token = 'gitlabtoken';
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<?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\Util;
|
||||
|
||||
use Composer\Util\Zip;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @author Andreas Schempp <andreas.schempp@terminal42.ch>
|
||||
*/
|
||||
class ZipTest extends TestCase
|
||||
{
|
||||
public function testThrowsExceptionIfZipExcentionIsNotLoaded()
|
||||
{
|
||||
if (extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is loaded.');
|
||||
}
|
||||
|
||||
$this->setExpectedException('\RuntimeException', 'The Zip Util requires PHP\'s zip extension');
|
||||
|
||||
Zip::getComposerJson('');
|
||||
}
|
||||
|
||||
public function testReturnsNullifTheZipIsNotFound()
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/invalid.zip');
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testReturnsNullIfTheZipIsEmpty()
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/empty.zip');
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testReturnsNullIfTheZipHasNoComposerJson()
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/nojson.zip');
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testReturnsNullIfTheComposerJsonIsInASubSubfolder()
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/subfolder.zip');
|
||||
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testReturnsComposerJsonInZipRoot()
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/root.zip');
|
||||
|
||||
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||
}
|
||||
|
||||
public function testReturnsComposerJsonInFirstFolder()
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/folder.zip');
|
||||
|
||||
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||
}
|
||||
|
||||
public function testReturnsRootComposerJsonAndSkipsSubfolders()
|
||||
{
|
||||
if (!extension_loaded('zip')) {
|
||||
$this->markTestSkipped('The PHP zip extension is not loaded.');
|
||||
return;
|
||||
}
|
||||
|
||||
$result = Zip::getComposerJson(__DIR__.'/Fixtures/Zip/multiple.zip');
|
||||
|
||||
$this->assertEquals("{\n \"name\": \"foo/bar\"\n}\n", $result);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue