diff --git a/CHANGELOG.md b/CHANGELOG.md index bec514aa2..045b45c1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +### [1.10.0] 2020-03-10 + + * Added `bearer` auth config to authenticate using `Authorization: Bearer ` headers + * Added `plugin-api-version` in composer.lock so future Composer versions know if they are running a lock file which was not built by the correct version + * Fixed composer fund command and funding info parsing to be more useful + * Fixed issue where --no-dev autoload generation was excluding some packages which should not have been excluded + * Fixed 1.10-RC regression in create project's handling of absolute paths + ### [1.10.0-RC] 2020-02-14 * Breaking: `composer global exec ...` now executes the process in the current working directory instead of executing it in the global directory. @@ -811,6 +819,7 @@ * Initial release +[1.10.0]: https://github.com/composer/composer/compare/1.10.0-RC...1.10.0 [1.10.0-RC]: https://github.com/composer/composer/compare/1.9.3...1.10.0-RC [1.9.3]: https://github.com/composer/composer/compare/1.9.2...1.9.3 [1.9.2]: https://github.com/composer/composer/compare/1.9.1...1.9.2 diff --git a/composer.lock b/composer.lock index aeedbd6fa..f77b6adc3 100644 --- a/composer.lock +++ b/composer.lock @@ -185,16 +185,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "cbe23383749496fe0f373345208b79568e4bc248" + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248", - "reference": "cbe23383749496fe0f373345208b79568e4bc248", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", "shasum": "" }, "require": { @@ -225,12 +225,13 @@ "Xdebug", "performance" ], - "support": { - "irc": "irc://irc.freenode.org/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/1.4.0" - }, - "time": "2019-11-06T16:40:04+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + } + ], + "time": "2020-03-01T12:26:26+00:00" }, { "name": "justinrainbow/json-schema", @@ -935,6 +936,10 @@ "constructor", "instantiate" ], + "support": { + "issues": "https://github.com/doctrine/instantiator/issues", + "source": "https://github.com/doctrine/instantiator/tree/master" + }, "time": "2015-06-14T21:17:01+00:00" }, { @@ -978,26 +983,20 @@ "license": [ "MIT" ], - "authors": [ - { - "name": "Mike van Riel", - "email": "mike.vanriel@naenius.com" - } - ], "time": "2016-01-25T08:17:30+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.10.2", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b4400efc9d206e83138e2bb97ed7f5b14b831cd9", - "reference": "b4400efc9d206e83138e2bb97ed7f5b14b831cd9", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { @@ -1047,7 +1046,7 @@ "spy", "stub" ], - "time": "2020-01-20T15:57:02+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "sebastian/comparator", @@ -1287,23 +1286,23 @@ }, { "name": "symfony/phpunit-bridge", - "version": "v3.4.37", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "ebfd1b428ffc14306e843092763f228bfba168d0" + "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/ebfd1b428ffc14306e843092763f228bfba168d0", - "reference": "ebfd1b428ffc14306e843092763f228bfba168d0", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/c02893ae43532b46a4f0e0f207d088b939f278d9", + "reference": "c02893ae43532b46a4f0e0f207d088b939f278d9", "shasum": "" }, "require": { "php": ">=5.3.3" }, "conflict": { - "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0" + "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0|<6.4,>=6.0" }, "suggest": { "symfony/debug": "For tracking deprecated interfaces usages at runtime with DebugClassLoader" @@ -1348,7 +1347,21 @@ ], "description": "Symfony PHPUnit Bridge", "homepage": "https://symfony.com", - "time": "2020-01-14T14:27:59+00:00" + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2020-02-21T08:01:47+00:00" } ], "aliases": [], @@ -1362,5 +1375,6 @@ "platform-dev": [], "platform-overrides": { "php": "5.3.9" - } + }, + "plugin-api-version": "1.1.0" } diff --git a/doc/06-config.md b/doc/06-config.md index 0f110a428..603de014a 100644 --- a/doc/06-config.md +++ b/doc/06-config.md @@ -79,16 +79,16 @@ an OAuth token for GitHub. A list of domain names and oauth keys. For example using `{"gitlab.com": "oauthtoken"}` as the value of this option will use `oauthtoken` to access -private repositories on gitlab. Please note: If the package is not hosted at -gitlab.com the domain names must be also specified with the +private repositories on gitlab. Please note: If the package is not hosted at +gitlab.com the domain names must be also specified with the [`gitlab-domains`](06-config.md#gitlab-domains) option. ## gitlab-token A list of domain names and private tokens. For example using `{"gitlab.com": "privatetoken"}` as the value of this option will use `privatetoken` to access -private repositories on gitlab. Please note: If the package is not hosted at -gitlab.com the domain names must be also specified with the +private repositories on gitlab. Please note: If the package is not hosted at +gitlab.com the domain names must be also specified with the [`gitlab-domains`](06-config.md#gitlab-domains) option. ## disable-tls @@ -129,11 +129,17 @@ A list of domain names and username/passwords to authenticate against them. For example using `{"example.org": {"username": "alice", "password": "foo"}}` as the value of this option will let Composer authenticate against example.org. -> **Note:** Authentication-related config options like `http-basic` and +> **Note:** Authentication-related config options like `http-basic`, `bearer` and > `github-oauth` can also be specified inside a `auth.json` file that goes > besides your `composer.json`. That way you can gitignore it and every > developer can place their own credentials in there. +## bearer + +A list of domain names and tokens to authenticate against them. For example using +`{"example.org": "foo"}` as the value of this option will let Composer authenticate +against example.org using an `Authorization: Bearer foo` header. + ## platform Lets you fake platform packages (PHP and extensions) so that you can emulate a @@ -298,7 +304,7 @@ in the composer home, cache, and data directories. ## lock -Defaults to `true`. If set to `false`, Composer will not create a `composer.lock` +Defaults to `true`. If set to `false`, Composer will not create a `composer.lock` file. ← [Repositories](05-repositories.md) | [Community](07-community.md) → diff --git a/doc/articles/versions.md b/doc/articles/versions.md index e3da6c8ec..85c9356d2 100644 --- a/doc/articles/versions.md +++ b/doc/articles/versions.md @@ -54,7 +54,7 @@ v2.0.2 Normally, Composer deals with tags (as opposed to branches -- if you don't know what this means, read up on -[version control systems](https://en.wikipedia.org/wiki/Version_control#Common_vocabulary)). +[version control systems](https://en.wikipedia.org/wiki/Version_control#Common_terminology)). When you write a version constraint, it may reference a specific tag (e.g., `1.1`) or it may reference a valid range of tags (e.g., `>=1.1 <2.0`, or `~4.0`). To resolve these constraints, Composer first asks the VCS to list diff --git a/res/composer-schema.json b/res/composer-schema.json index a74819baa..bcb712882 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -140,7 +140,16 @@ "gitlab-token": { "type": "object", "description": "A hash of domain name => gitlab private tokens, typically {\"gitlab.com\":\"\"}.", - "additionalProperties": true + "additionalProperties": { + "type": "string" + } + }, + "bearer": { + "type": "object", + "description": "A hash of domain name => bearer authentication token, for example {\"example.com\":\"\"}.", + "additionalProperties": { + "type": "string" + } }, "disable-tls": { "type": "boolean", diff --git a/src/Composer/Autoload/AutoloadGenerator.php b/src/Composer/Autoload/AutoloadGenerator.php index 2bb0c98b1..e7cd482b8 100644 --- a/src/Composer/Autoload/AutoloadGenerator.php +++ b/src/Composer/Autoload/AutoloadGenerator.php @@ -236,6 +236,7 @@ EOF; // flatten array $classMap = array(); + $ambiguousClasses = array(); if ($scanPsr0Packages) { $namespacesToScan = array(); @@ -256,14 +257,23 @@ EOF; continue; } - $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap); + $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, $namespace, $group['type'], $classMap, $ambiguousClasses); } } } } foreach ($autoloads['classmap'] as $dir) { - $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap); + $classMap = $this->addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist, null, null, $classMap, $ambiguousClasses); + } + + foreach ($ambiguousClasses as $className => $ambigiousPaths) { + $cleanPath = str_replace(array('$vendorDir . \'', '$baseDir . \'', "',\n"), array($vendorPath, $basePath, ''), $classMap[$className]); + + $this->io->writeError( + 'Warning: Ambiguous class resolution, "'.$className.'"'. + ' was found '. (count($ambigiousPaths) + 1) .'x: in "'.$cleanPath.'" and "'. implode('", "', $ambigiousPaths) .'", the first will be used.' + ); } ksort($classMap); @@ -326,17 +336,14 @@ EOF; return 0; } - private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap = array()) + private function addClassMapCode($filesystem, $basePath, $vendorPath, $dir, $blacklist = null, $namespaceFilter = null, $autoloadType = null, array $classMap, array &$ambiguousClasses) { foreach ($this->generateClassMap($dir, $blacklist, $namespaceFilter, $autoloadType) as $class => $path) { $pathCode = $this->getPathCode($filesystem, $basePath, $vendorPath, $path).",\n"; if (!isset($classMap[$class])) { $classMap[$class] = $pathCode; } elseif ($this->io && $classMap[$class] !== $pathCode && !preg_match('{/(test|fixture|example|stub)s?/}i', strtr($classMap[$class].' '.$path, '\\', '/'))) { - $this->io->writeError( - 'Warning: Ambiguous class resolution, "'.$class.'"'. - ' was found in both "'.str_replace(array('$vendorDir . \'', "',\n"), array($vendorPath, ''), $classMap[$class]).'" and "'.$path.'", the first will be used.' - ); + $ambiguousClasses[$class][] = $path; } } @@ -393,8 +400,8 @@ EOF; /** * Compiles an ordered list of namespace => path mappings * - * @param array $packageMap array of array(package, installDir-relative-to-composer.json) - * @param PackageInterface $mainPackage root package instance + * @param array $packageMap array of array(package, installDir-relative-to-composer.json) + * @param PackageInterface $mainPackage root package instance * @param bool $filterOutRequireDevPackages whether to filter out require-dev packages * @return array array('psr-0' => array('Ns\\Foo' => array('installDir'))) */ @@ -939,16 +946,23 @@ INITIALIZER; { $packages = array(); $include = array(); + $replacedBy = array(); foreach ($packageMap as $item) { $package = $item[0]; $name = $package->getName(); $packages[$name] = $package; + foreach ($package->getReplaces() as $replace) { + $replacedBy[$replace->getTarget()] = $name; + } } - $add = function (PackageInterface $package) use (&$add, $packages, &$include) { + $add = function (PackageInterface $package) use (&$add, $packages, &$include, $replacedBy) { foreach ($package->getRequires() as $link) { $target = $link->getTarget(); + if (isset($replacedBy[$target])) { + $target = $replacedBy[$target]; + } if (!isset($include[$target])) { $include[$target] = true; if (isset($packages[$target])) { diff --git a/src/Composer/Command/ConfigCommand.php b/src/Composer/Command/ConfigCommand.php index 56e994036..2695f0399 100644 --- a/src/Composer/Command/ConfigCommand.php +++ b/src/Composer/Command/ConfigCommand.php @@ -187,7 +187,7 @@ EOT } if ($input->getOption('global') && !$this->authConfigFile->exists()) { touch($this->authConfigFile->getPath()); - $this->authConfigFile->write(array('bitbucket-oauth' => new \ArrayObject, 'github-oauth' => new \ArrayObject, 'gitlab-oauth' => new \ArrayObject, 'gitlab-token' => new \ArrayObject, 'http-basic' => new \ArrayObject)); + $this->authConfigFile->write(array('bitbucket-oauth' => new \ArrayObject, 'github-oauth' => new \ArrayObject, 'gitlab-oauth' => new \ArrayObject, 'gitlab-token' => new \ArrayObject, 'http-basic' => new \ArrayObject, 'bearer' => new \ArrayObject)); Silencer::call('chmod', $this->authConfigFile->getPath(), 0600); } @@ -667,7 +667,7 @@ EOT } // handle auth - if (preg_match('/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic)\.(.+)/', $settingKey, $matches)) { + if (preg_match('/^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|bearer)\.(.+)/', $settingKey, $matches)) { if ($input->getOption('unset')) { $this->authConfigSource->removeConfigSetting($matches[1].'.'.$matches[2]); $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); @@ -681,7 +681,7 @@ EOT } $this->configSource->removeConfigSetting($matches[1].'.'.$matches[2]); $this->authConfigSource->addConfigSetting($matches[1].'.'.$matches[2], array('consumer-key' => $values[0], 'consumer-secret' => $values[1])); - } elseif (in_array($matches[1], array('github-oauth', 'gitlab-oauth', 'gitlab-token'), true)) { + } elseif (in_array($matches[1], array('github-oauth', 'gitlab-oauth', 'gitlab-token', 'bearer'), true)) { if (1 !== count($values)) { throw new \RuntimeException('Too many arguments, expected only one token'); } diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php index 5b362b484..52c75a609 100644 --- a/src/Composer/Command/CreateProjectCommand.php +++ b/src/Composer/Command/CreateProjectCommand.php @@ -303,13 +303,16 @@ EOT // if no directory was specified, use the 2nd part of the package name if (null === $directory) { $parts = explode("/", $name, 2); - $directory = array_pop($parts); + $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); } - $directory = getcwd() . DIRECTORY_SEPARATOR . $directory; - $io->writeError('Creating a "' . $packageName . '" project at "' . $directory . '"'); - $fs = new Filesystem(); + if (!$fs->isAbsolutePath($directory)) { + $directory = getcwd() . DIRECTORY_SEPARATOR . $directory; + } + + $io->writeError('Creating a "' . $packageName . '" project at "' . $fs->findShortestPath(getcwd(), $directory, true) . '"'); + if (file_exists($directory)) { if (!is_dir($directory)) { throw new \InvalidArgumentException('Cannot create project directory at "'.$directory.'", it exists as a file.'); diff --git a/src/Composer/Command/FundCommand.php b/src/Composer/Command/FundCommand.php index 3c80abc23..53c915c68 100644 --- a/src/Composer/Command/FundCommand.php +++ b/src/Composer/Command/FundCommand.php @@ -14,6 +14,7 @@ namespace Composer\Command; use Composer\Package\CompletePackageInterface; use Composer\Package\AliasPackage; +use Composer\Repository\CompositeRepository; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -36,20 +37,19 @@ class FundCommand extends BaseCommand $composer = $this->getComposer(); $repo = $composer->getRepositoryManager()->getLocalRepository(); + $remoteRepos = new CompositeRepository($composer->getRepositoryManager()->getRepositories()); $fundings = array(); foreach ($repo->getPackages() as $package) { if ($package instanceof AliasPackage) { continue; } - if ($package instanceof CompletePackageInterface && $funding = $package->getFunding()) { - foreach ($funding as $fundingOption) { - list($vendor, $packageName) = explode('/', $package->getPrettyName()); - $url = $fundingOption['url']; - if (!empty($fundingOption['type']) && $fundingOption['type'] === 'github' && preg_match('{^https://github.com/([^/]+)$}', $url, $match)) { - $url = 'https://github.com/sponsors/'.$match[1]; - } - $fundings[$vendor][$url][] = $packageName; - } + $latest = $remoteRepos->findPackage($package->getName(), 'dev-master'); + if ($latest instanceof CompletePackageInterface && $latest->getFunding()) { + $fundings = $this->insertFundingData($fundings, $latest); + continue; + } + if ($package instanceof CompletePackageInterface && $package->getFunding()) { + $fundings = $this->insertFundingData($fundings, $package); } } @@ -86,4 +86,18 @@ class FundCommand extends BaseCommand return 0; } + + private function insertFundingData(array $fundings, CompletePackageInterface $package) + { + foreach ($package->getFunding() as $fundingOption) { + list($vendor, $packageName) = explode('/', $package->getPrettyName()); + $url = $fundingOption['url']; + if (!empty($fundingOption['type']) && $fundingOption['type'] === 'github' && preg_match('{^https://github.com/([^/]+)$}', $url, $match)) { + $url = 'https://github.com/sponsors/'.$match[1]; + } + $fundings[$vendor][$url][] = $packageName; + } + + return $fundings; + } } diff --git a/src/Composer/Command/ShowCommand.php b/src/Composer/Command/ShowCommand.php index 125dd741a..c90ad2d09 100644 --- a/src/Composer/Command/ShowCommand.php +++ b/src/Composer/Command/ShowCommand.php @@ -495,7 +495,7 @@ EOT } $io->write(''); if (isset($package['warning'])) { - $io->writeError('' . $package['warning'] . ''); + $io->write('' . $package['warning'] . ''); } } diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 1050096b1..e57d21cab 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -70,6 +70,7 @@ class Config // gitlab-oauth // gitlab-token // http-basic + // bearer ); public static $defaultRepositories = array( @@ -133,7 +134,7 @@ class Config // override defaults with given config if (!empty($config['config']) && is_array($config['config'])) { foreach ($config['config'] as $key => $val) { - if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic')) && isset($this->config[$key])) { + if (in_array($key, array('bitbucket-oauth', 'github-oauth', 'gitlab-oauth', 'gitlab-token', 'http-basic', 'bearer')) && isset($this->config[$key])) { $this->config[$key] = array_merge($this->config[$key], $val); } elseif ('preferred-install' === $key && isset($this->config[$key])) { if (is_array($val) || is_array($this->config[$key])) { diff --git a/src/Composer/Config/JsonConfigSource.php b/src/Composer/Config/JsonConfigSource.php index 5a64c5701..67634f0c5 100644 --- a/src/Composer/Config/JsonConfigSource.php +++ b/src/Composer/Config/JsonConfigSource.php @@ -96,7 +96,7 @@ class JsonConfigSource implements ConfigSourceInterface { $authConfig = $this->authConfig; $this->manipulateJson('addConfigSetting', $name, $value, function (&$config, $key, $val) use ($authConfig) { - if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) { + if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\.}', $key)) { list($key, $host) = explode('.', $key, 2); if ($authConfig) { $config[$key][$host] = $val; @@ -116,7 +116,7 @@ class JsonConfigSource implements ConfigSourceInterface { $authConfig = $this->authConfig; $this->manipulateJson('removeConfigSetting', $name, function (&$config, $key) use ($authConfig) { - if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|http-basic|platform)\.}', $key)) { + if (preg_match('{^(bitbucket-oauth|github-oauth|gitlab-oauth|gitlab-token|bearer|http-basic|platform)\.}', $key)) { list($key, $host) = explode('.', $key, 2); if ($authConfig) { unset($config[$key][$host]); diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 434997439..9875b69c1 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -131,7 +131,8 @@ class Application extends BaseApplication if ($input->hasParameterOption('--no-cache')) { $io->writeError('Disabling cache usage', true, IOInterface::DEBUG); - putenv('COMPOSER_CACHE_DIR='.(Platform::isWindows() ? 'nul' : '/dev/null')); + $_SERVER['COMPOSER_CACHE_DIR'] = Platform::isWindows() ? 'nul' : '/dev/null'; + putenv('COMPOSER_CACHE_DIR='.$_SERVER['COMPOSER_CACHE_DIR']); } // switch working dir diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index fda5c29fd..bcd95b91b 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -243,7 +243,8 @@ class EventDispatcher $finder = new PhpExecutableFinder(); $phpPath = $finder->find(false); if ($phpPath) { - putenv('PHP_BINARY=' . $phpPath); + $_SERVER['PHP_BINARY'] = $phpPath; + putenv('PHP_BINARY=' . $_SERVER['PHP_BINARY']); } } diff --git a/src/Composer/IO/BaseIO.php b/src/Composer/IO/BaseIO.php index c62412ea6..e2d916a15 100644 --- a/src/Composer/IO/BaseIO.php +++ b/src/Composer/IO/BaseIO.php @@ -115,6 +115,7 @@ abstract class BaseIO implements IOInterface $gitlabOauth = $config->get('gitlab-oauth') ?: array(); $gitlabToken = $config->get('gitlab-token') ?: array(); $httpBasic = $config->get('http-basic') ?: array(); + $bearerToken = $config->get('bearer') ?: array(); // reload oauth tokens from config if available @@ -142,6 +143,10 @@ abstract class BaseIO implements IOInterface $this->checkAndSetAuthentication($domain, $cred['username'], $cred['password']); } + foreach ($bearerToken as $domain => $token) { + $this->checkAndSetAuthentication($domain, $token, 'bearer'); + } + // setup process timeout ProcessExecutor::setTimeout((int) $config->get('process-timeout')); } diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php index 70d127b5d..71b0f8262 100644 --- a/src/Composer/Installer.php +++ b/src/Composer/Installer.php @@ -219,8 +219,8 @@ class Installer } if ($this->runScripts) { - $devMode = (int) $this->devMode; - putenv("COMPOSER_DEV_MODE=$devMode"); + $_SERVER['COMPOSER_DEV_MODE'] = (int) $this->devMode; + putenv('COMPOSER_DEV_MODE='.$_SERVER['COMPOSER_DEV_MODE']); // dispatch pre event // should we treat this more strictly as running an update and then running an install, triggering events multiple times? diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index 5f4578d1e..d0aeea878 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -23,6 +23,7 @@ use Composer\EventDispatcher\EventDispatcher; use Composer\Util\ProcessExecutor; use Composer\Util\HttpDownloader; use Composer\Util\Url; +use Composer\Semver\Constraint\Constraint; use Composer\IO\IOInterface; use Composer\Config; @@ -381,7 +382,12 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt private function validateBranch($branch) { try { - return $this->versionParser->normalizeBranch($branch); + $normalizedBranch = $this->versionParser->normalizeBranch($branch); + + // validate that the branch name has no weird characters conflicting with constraints + $this->versionParser->parseConstraints($normalizedBranch); + + return $normalizedBranch; } catch (\Exception $e) { } @@ -421,7 +427,7 @@ class VcsRepository extends ArrayRepository implements ConfigurableRepositoryInt $this->io->overwriteError($msg, false); } - if ($existingPackage = $this->findPackage($cachedPackage['name'], $cachedPackage['version_normalized'])) { + if ($existingPackage = $this->findPackage($cachedPackage['name'], new Constraint('=', $cachedPackage['version_normalized']))) { if ($isVeryVerbose) { $this->io->writeError('Skipped cached version '.$version.', it conflicts with an another tag ('.$existingPackage->getPrettyVersion().') as both resolve to '.$cachedPackage['version_normalized'].' internally'); } diff --git a/src/Composer/Util/AuthHelper.php b/src/Composer/Util/AuthHelper.php index e4e829ea7..81eb49020 100644 --- a/src/Composer/Util/AuthHelper.php +++ b/src/Composer/Util/AuthHelper.php @@ -117,7 +117,7 @@ class AuthHelper $message = "\n".'Could not fetch '.$url.', enter your ' . $origin . ' credentials ' .($statusCode === 401 ? 'to access private repos' : 'to go over the API rate limit'); $gitLabUtil = new GitLab($this->io, $this->config, null); - if ($this->io->hasAuthentication($origin) && ($auth = $this->io->getAuthentication($origin)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token'), true)) { + if ($this->io->hasAuthentication($origin) && ($auth = $this->io->getAuthentication($origin)) && in_array($auth['password'], array('gitlab-ci-token', 'private-token', 'oauth2'), true)) { throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode); } @@ -196,7 +196,9 @@ class AuthHelper if ($this->io->hasAuthentication($origin)) { $authenticationDisplayMessage = null; $auth = $this->io->getAuthentication($origin); - if ('github.com' === $origin && 'x-oauth-basic' === $auth['password']) { + if ($auth['password'] === 'bearer') { + $headers[] = 'Authorization: Bearer '.$auth['username']; + } elseif ('github.com' === $origin && 'x-oauth-basic' === $auth['password']) { $headers[] = 'Authorization: token '.$auth['username']; $authenticationDisplayMessage = 'Using GitHub token authentication'; } elseif (in_array($origin, $this->config->get('gitlab-domains'), true)) { diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index b1a106709..8df7de8b5 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -207,7 +207,7 @@ class Filesystem usleep(350000); $unlinked = @$this->unlinkImplementation($path); } - + if (!$unlinked) { $error = error_get_last(); $message = 'Could not delete '.$path.': ' . @$error['message']; @@ -238,7 +238,7 @@ class Filesystem usleep(350000); $deleted = @rmdir($path); } - + if (!$deleted) { $error = error_get_last(); $message = 'Could not delete '.$path.': ' . @$error['message']; @@ -312,7 +312,9 @@ class Filesystem } if (!function_exists('proc_open')) { - return $this->copyThenRemove($source, $target); + $this->copyThenRemove($source, $target); + + return; } if (Platform::isWindows()) { @@ -342,7 +344,7 @@ class Filesystem } } - return $this->copyThenRemove($source, $target); + $this->copyThenRemove($source, $target); } /** diff --git a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php index 055b03e1c..3cd8f7ff2 100644 --- a/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php +++ b/tests/Composer/Test/Autoload/AutoloadGeneratorTest.php @@ -486,6 +486,58 @@ class AutoloadGeneratorTest extends TestCase $this->assertFileExists($this->vendorDir.'/composer/autoload_classmap.php', "ClassMap file needs to be generated, even if empty."); } + public function testNonDevAutoloadReplacesNestedRequirements() + { + $package = new Package('a', '1.0', '1.0'); + $package->setRequires(array( + new Link('a', 'a/a') + )); + + $packages = array(); + $packages[] = $a = new Package('a/a', '1.0', '1.0'); + $packages[] = $b = new Package('b/b', '1.0', '1.0'); + $packages[] = $c = new Package('c/c', '1.0', '1.0'); + $packages[] = $d = new Package('d/d', '1.0', '1.0'); + $packages[] = $e = new Package('e/e', '1.0', '1.0'); + $a->setAutoload(array('classmap' => array('src/A.php'))); + $a->setRequires(array( + new Link('a/a', 'b/b') + )); + $b->setAutoload(array('classmap' => array('src/B.php'))); + $b->setRequires(array( + new Link('b/b', 'e/e') + )); + $c->setAutoload(array('classmap' => array('src/C.php'))); + $c->setReplaces(array( + new Link('c/c', 'b/b') + )); + $c->setRequires(array( + new Link('c/c', 'd/d') + )); + $d->setAutoload(array('classmap' => array('src/D.php'))); + $e->setAutoload(array('classmap' => array('src/E.php'))); + + $this->repository->expects($this->once()) + ->method('getCanonicalPackages') + ->will($this->returnValue($packages)); + + $this->fs->ensureDirectoryExists($this->vendorDir.'/a/a/src'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/b/b/src'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/c/c/src'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/d/d/src'); + $this->fs->ensureDirectoryExists($this->vendorDir.'/e/e/src'); + + file_put_contents($this->vendorDir.'/a/a/src/A.php', 'vendorDir.'/b/b/src/B.php', 'vendorDir.'/c/c/src/C.php', 'vendorDir.'/d/d/src/D.php', 'vendorDir.'/e/e/src/E.php', 'generator->dump($this->config, $this->repository, $package, $this->im, 'composer', false, '_5'); + + $this->assertAutoloadFiles('classmap9', $this->vendorDir.'/composer', 'classmap'); + } + public function testPharAutoload() { $package = new Package('a', '1.0', '1.0'); diff --git a/tests/Composer/Test/Autoload/Fixtures/autoload_classmap9.php b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap9.php new file mode 100644 index 000000000..f9ad3ca30 --- /dev/null +++ b/tests/Composer/Test/Autoload/Fixtures/autoload_classmap9.php @@ -0,0 +1,12 @@ + $vendorDir . '/a/a/src/A.php', + 'C' => $vendorDir . '/c/c/src/C.php', + 'D' => $vendorDir . '/d/d/src/D.php', +);