1
0
Fork 0

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 order
pull/8850/head
Nils Adermann 2020-08-28 14:52:58 +02:00
commit 1385412748
13 changed files with 226 additions and 16 deletions

View File

@ -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

View File

@ -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>');

View File

@ -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);

View File

@ -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'));
} }

View File

@ -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();

View File

@ -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';

View File

@ -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)));

View File

@ -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)

View File

@ -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--

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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')