Merge branch 'master' into filter-packages
* master: Add tests for edge cases of packages providing names which exist as real packages Add another test verifying that a package may provide an incompatible version of sth that actually exists Fix provider coexistence test, needs another requirement to install both Fix test filename to end with .test extension so it gets run Update config section to note required scope for GitLab tokens Fix pre/post-package-install/update/uninstall events receiving a partial list of operations, fixes #9079 Also remove credentials from cache dirs in git/svn drivers, fixes #7439, refs #9155 AuthHelper: Allow fall-through GitLab-specific HTTP headers for auth Sanitize repo URLs to mask HTTP auth passwords from cache directory Util/Zip: fix strpos args orderpull/8850/head
commit
1385412748
|
@ -93,7 +93,8 @@ private repositories on gitlab. Using `{"gitlab.com": {"username": "gitlabuser",
|
||||||
token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/)
|
token functionality (https://docs.gitlab.com/ee/user/project/deploy_tokens/)
|
||||||
Please note: If the package is not hosted at
|
Please note: If the package is not hosted at
|
||||||
gitlab.com the domain names must be also specified with the
|
gitlab.com the domain names must be also specified with the
|
||||||
[`gitlab-domains`](06-config.md#gitlab-domains) option.
|
[`gitlab-domains`](06-config.md#gitlab-domains) option. The token must have
|
||||||
|
`api` or `read_api` scope.
|
||||||
|
|
||||||
## disable-tls
|
## disable-tls
|
||||||
|
|
||||||
|
|
|
@ -292,14 +292,12 @@ class InstallationManager
|
||||||
if ($batch) {
|
if ($batch) {
|
||||||
$batches[] = $batch;
|
$batches[] = $batch;
|
||||||
}
|
}
|
||||||
unset($operations[$index]);
|
|
||||||
$batches[] = array($index => $operation);
|
$batches[] = array($index => $operation);
|
||||||
$batch = array();
|
$batch = array();
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unset($operations[$index]);
|
|
||||||
$batch[$index] = $operation;
|
$batch[$index] = $operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,7 +306,7 @@ class InstallationManager
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($batches as $batch) {
|
foreach ($batches as $batch) {
|
||||||
$this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts);
|
$this->executeBatch($repo, $batch, $cleanupPromises, $devMode, $runScripts, $operations);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$runCleanup();
|
$runCleanup();
|
||||||
|
@ -336,7 +334,11 @@ class InstallationManager
|
||||||
$repo->write($devMode, $this);
|
$repo->write($devMode, $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function executeBatch(RepositoryInterface $repo, array $operations, array $cleanupPromises, $devMode, $runScripts)
|
/**
|
||||||
|
* @param array $operations List of operations to execute in this batch
|
||||||
|
* @param array $allOperations Complete list of operations to be executed in the install job, used for event listeners
|
||||||
|
*/
|
||||||
|
private function executeBatch(RepositoryInterface $repo, array $operations, array $cleanupPromises, $devMode, $runScripts, array $allOperations)
|
||||||
{
|
{
|
||||||
foreach ($operations as $index => $operation) {
|
foreach ($operations as $index => $operation) {
|
||||||
$opType = $operation->getOperationType();
|
$opType = $operation->getOperationType();
|
||||||
|
@ -363,7 +365,7 @@ class InstallationManager
|
||||||
|
|
||||||
$event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($opType);
|
$event = 'Composer\Installer\PackageEvents::PRE_PACKAGE_'.strtoupper($opType);
|
||||||
if (defined($event) && $runScripts && $this->eventDispatcher) {
|
if (defined($event) && $runScripts && $this->eventDispatcher) {
|
||||||
$this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
|
$this->eventDispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $allOperations, $operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
$dispatcher = $this->eventDispatcher;
|
$dispatcher = $this->eventDispatcher;
|
||||||
|
@ -378,12 +380,12 @@ class InstallationManager
|
||||||
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
|
$promise = $promise->then(function () use ($opType, $installManager, $repo, $operation) {
|
||||||
return $installManager->$opType($repo, $operation);
|
return $installManager->$opType($repo, $operation);
|
||||||
})->then($cleanupPromises[$index])
|
})->then($cleanupPromises[$index])
|
||||||
->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $operations, $operation) {
|
->then(function () use ($opType, $runScripts, $dispatcher, $installManager, $devMode, $repo, $allOperations, $operation) {
|
||||||
$repo->write($devMode, $installManager);
|
$repo->write($devMode, $installManager);
|
||||||
|
|
||||||
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($opType);
|
$event = 'Composer\Installer\PackageEvents::POST_PACKAGE_'.strtoupper($opType);
|
||||||
if (defined($event) && $runScripts && $dispatcher) {
|
if (defined($event) && $runScripts && $dispatcher) {
|
||||||
$dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $operations, $operation);
|
$dispatcher->dispatchPackageEvent(constant($event), $devMode, $repo, $allOperations, $operation);
|
||||||
}
|
}
|
||||||
}, function ($e) use ($opType, $package, $io) {
|
}, function ($e) use ($opType, $package, $io) {
|
||||||
$io->writeError(' <error>' . ucfirst($opType) .' of '.$package->getPrettyName().' failed</error>');
|
$io->writeError(' <error>' . ucfirst($opType) .' of '.$package->getPrettyName().' failed</error>');
|
||||||
|
|
|
@ -129,7 +129,7 @@ class ComposerRepository extends ArrayRepository implements ConfigurableReposito
|
||||||
|
|
||||||
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
|
$this->baseUrl = rtrim(preg_replace('{(?:/[^/\\\\]+\.json)?(?:[?#].*)?$}', '', $this->url), '/');
|
||||||
$this->io = $io;
|
$this->io = $io;
|
||||||
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$~');
|
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', Url::sanitize($this->url)), 'a-z0-9.$~');
|
||||||
$this->cache->setReadOnly($config->get('cache-read-only'));
|
$this->cache->setReadOnly($config->get('cache-read-only'));
|
||||||
$this->versionParser = new VersionParser();
|
$this->versionParser = new VersionParser();
|
||||||
$this->loader = new ArrayLoader($this->versionParser);
|
$this->loader = new ArrayLoader($this->versionParser);
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Composer\Repository\Vcs;
|
||||||
|
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
|
use Composer\Util\Url;
|
||||||
use Composer\Util\Git as GitUtil;
|
use Composer\Util\Git as GitUtil;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Cache;
|
use Composer\Cache;
|
||||||
|
@ -74,7 +75,7 @@ class GitDriver extends VcsDriver
|
||||||
$this->getTags();
|
$this->getTags();
|
||||||
$this->getBranches();
|
$this->getBranches();
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $cacheUrl));
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', Url::sanitize($cacheUrl)));
|
||||||
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ use Composer\Config;
|
||||||
use Composer\Json\JsonFile;
|
use Composer\Json\JsonFile;
|
||||||
use Composer\Util\ProcessExecutor;
|
use Composer\Util\ProcessExecutor;
|
||||||
use Composer\Util\Filesystem;
|
use Composer\Util\Filesystem;
|
||||||
|
use Composer\Util\Url;
|
||||||
use Composer\Util\Svn as SvnUtil;
|
use Composer\Util\Svn as SvnUtil;
|
||||||
use Composer\IO\IOInterface;
|
use Composer\IO\IOInterface;
|
||||||
use Composer\Downloader\TransportException;
|
use Composer\Downloader\TransportException;
|
||||||
|
@ -77,7 +78,7 @@ class SvnDriver extends VcsDriver
|
||||||
$this->baseUrl = substr($this->url, 0, $pos);
|
$this->baseUrl = substr($this->url, 0, $pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->baseUrl));
|
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', Url::sanitize($this->baseUrl)));
|
||||||
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
$this->cache->setReadOnly($this->config->get('cache-read-only'));
|
||||||
|
|
||||||
$this->getBranches();
|
$this->getBranches();
|
||||||
|
|
|
@ -205,7 +205,10 @@ class AuthHelper
|
||||||
$headers[] = 'Authorization: token '.$auth['username'];
|
$headers[] = 'Authorization: token '.$auth['username'];
|
||||||
$authenticationDisplayMessage = 'Using GitHub token authentication';
|
$authenticationDisplayMessage = 'Using GitHub token authentication';
|
||||||
}
|
}
|
||||||
} elseif (in_array($origin, $this->config->get('gitlab-domains'), true)) {
|
} elseif (
|
||||||
|
in_array($origin, $this->config->get('gitlab-domains'), true)
|
||||||
|
&& in_array($auth['password'], array('oauth2', 'private-token', 'gitlab-ci-token'), true)
|
||||||
|
) {
|
||||||
if ($auth['password'] === 'oauth2') {
|
if ($auth['password'] === 'oauth2') {
|
||||||
$headers[] = 'Authorization: Bearer '.$auth['username'];
|
$headers[] = 'Authorization: Bearer '.$auth['username'];
|
||||||
$authenticationDisplayMessage = 'Using GitLab OAuth token authentication';
|
$authenticationDisplayMessage = 'Using GitLab OAuth token authentication';
|
||||||
|
|
|
@ -92,7 +92,7 @@ class Zip
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle archives which do not have a TOC record for the directory itself
|
// handle archives which do not have a TOC record for the directory itself
|
||||||
if (false === strpos('\\', $dirname) && false === strpos('/', $dirname)) {
|
if (false === strpos($dirname, '\\') && false === strpos($dirname, '/')) {
|
||||||
$topLevelPaths[$dirname.'/'] = true;
|
$topLevelPaths[$dirname.'/'] = true;
|
||||||
if (\count($topLevelPaths) > 1) {
|
if (\count($topLevelPaths) > 1) {
|
||||||
throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths)));
|
throw new \RuntimeException('Archive has more than one top level directories, and no composer.json was found on the top level, so it\'s an invalid archive. Top level paths found were: '.implode(',', array_keys($topLevelPaths)));
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
--TEST--
|
||||||
|
Test that providers can coexist with a different version of the package they provide
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "foo/provider",
|
||||||
|
"provide": {
|
||||||
|
"foo/original": "3.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/original",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"foo/original": "1.0.0",
|
||||||
|
"foo/provider": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
Installing foo/original (1.0.0)
|
||||||
|
Installing foo/provider (1.0.0)
|
|
@ -0,0 +1,56 @@
|
||||||
|
--TEST--
|
||||||
|
A root requirement for a name exists as well as a dependency's requirement for the same name but in a different version.
|
||||||
|
Since the root requirement does not allow the dependency's requirement to be installed, this conflicts.
|
||||||
|
|
||||||
|
The difference between this test and the one which does not conflict is that here the root requirement could only be
|
||||||
|
satisfied with the provided package but would conflict with the actual package by the given name.
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "foo/provider",
|
||||||
|
"provide": {
|
||||||
|
"foo/original": "3.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/original",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/requirer",
|
||||||
|
"require": {
|
||||||
|
"foo/original": "1.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"foo/original": "3.0.0",
|
||||||
|
"foo/provider": "1.0.0",
|
||||||
|
"foo/requirer": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
|
||||||
|
--EXPECT-EXIT-CODE--
|
||||||
|
2
|
||||||
|
|
||||||
|
--EXPECT-OUTPUT--
|
||||||
|
Loading composer repositories with package information
|
||||||
|
Updating dependencies
|
||||||
|
Your requirements could not be resolved to an installable set of packages.
|
||||||
|
|
||||||
|
Problem 1
|
||||||
|
- Root composer.json requires foo/requirer 1.0.0 -> satisfiable by foo/requirer[1.0.0].
|
||||||
|
- foo/requirer 1.0.0 requires foo/original 1.0.0 -> found foo/original[1.0.0] but it conflicts with your root composer.json require (3.0.0).
|
||||||
|
|
||||||
|
--EXPECT--
|
|
@ -0,0 +1,54 @@
|
||||||
|
--TEST--
|
||||||
|
This is a variant of the test provider-gets-picked-together-with-other-version-of-provided-conflict.test which differs
|
||||||
|
in so far as that the root requirements were all moved into a package which now allows the combination to succeed.
|
||||||
|
|
||||||
|
Only root requirements strictly limit compatibility with versions if a package by a provided name also actually exists.
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "foo/root",
|
||||||
|
"require": {
|
||||||
|
"foo/original": "3.0.0",
|
||||||
|
"foo/provider": "1.0.0",
|
||||||
|
"foo/requirer": "1.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/provider",
|
||||||
|
"provide": {
|
||||||
|
"foo/original": "3.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/original",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/requirer",
|
||||||
|
"require": {
|
||||||
|
"foo/original": "1.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"foo/root": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
Installing foo/original (1.0.0)
|
||||||
|
Installing foo/provider (1.0.0)
|
||||||
|
Installing foo/requirer (1.0.0)
|
||||||
|
Installing foo/root (1.0.0)
|
|
@ -0,0 +1,46 @@
|
||||||
|
--TEST--
|
||||||
|
A root requirement for a name exists as well as a dependency's requirement for the same name but in a different version.
|
||||||
|
The root requirement is satisfied by the actual package by that name in that version. The dependency's requirement can
|
||||||
|
be satisfied by another package which provides the name in the other version. This will result in all packages being
|
||||||
|
installed as the provider does not conflict with the actual package and all requirements can be met.
|
||||||
|
--COMPOSER--
|
||||||
|
{
|
||||||
|
"repositories": [
|
||||||
|
{
|
||||||
|
"type": "package",
|
||||||
|
"package": [
|
||||||
|
{
|
||||||
|
"name": "foo/provider",
|
||||||
|
"provide": {
|
||||||
|
"foo/original": "3.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/original",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "foo/requirer",
|
||||||
|
"require": {
|
||||||
|
"foo/original": "3.0.0"
|
||||||
|
},
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"require": {
|
||||||
|
"foo/original": "1.0.0",
|
||||||
|
"foo/provider": "1.0.0",
|
||||||
|
"foo/requirer": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--RUN--
|
||||||
|
update
|
||||||
|
|
||||||
|
--EXPECT--
|
||||||
|
Installing foo/original (1.0.0)
|
||||||
|
Installing foo/provider (1.0.0)
|
||||||
|
Installing foo/requirer (1.0.0)
|
|
@ -15,12 +15,16 @@ Test that providers can be installed in conjunction with the package they provid
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "foo/standard",
|
"name": "foo/standard",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0",
|
||||||
|
"provide": {
|
||||||
|
"foo/also-necessary": "1.0.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
"foo/also-necessary": "1.0.0",
|
||||||
"foo/standard": "1.0.0",
|
"foo/standard": "1.0.0",
|
||||||
"foo/polyfill": "1.0.0"
|
"foo/polyfill": "1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -30,5 +34,5 @@ Test that providers can be installed in conjunction with the package they provid
|
||||||
update
|
update
|
||||||
|
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
Installing foo/standard (1.0.0)
|
|
||||||
Installing foo/polyfill (1.0.0)
|
Installing foo/polyfill (1.0.0)
|
||||||
|
Installing foo/standard (1.0.0)
|
|
@ -280,6 +280,14 @@ class AuthHelperTest extends TestCase
|
||||||
'password' => 'my_password'
|
'password' => 'my_password'
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'https://gitlab.com',
|
||||||
|
'gitlab.com',
|
||||||
|
array(
|
||||||
|
'username' => 'my_username',
|
||||||
|
'password' => 'my_password'
|
||||||
|
)
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +310,7 @@ class AuthHelperTest extends TestCase
|
||||||
$this->config->expects($this->once())
|
$this->config->expects($this->once())
|
||||||
->method('get')
|
->method('get')
|
||||||
->with('gitlab-domains')
|
->with('gitlab-domains')
|
||||||
->willReturn(array());
|
->willReturn(array($origin));
|
||||||
|
|
||||||
$this->io->expects($this->once())
|
$this->io->expects($this->once())
|
||||||
->method('writeError')
|
->method('writeError')
|
||||||
|
|
Loading…
Reference in New Issue