diff --git a/composer.json b/composer.json index c53971509..4ba003887 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "symfony/console": "~2.5", "symfony/finder": "~2.2", "symfony/process": "~2.1", + "symfony/filesystem": "~2.5", "seld/phar-utils": "~1.0", "seld/cli-prompt": "~1.0" }, diff --git a/composer.lock b/composer.lock index f05689b63..fc7e1ef34 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "6fbac2ddcd4e9064c84090f6d5514412", + "hash": "3024e89a7e808b8dece156112459a7ea", "packages": [ { "name": "composer/spdx-licenses", @@ -328,6 +328,56 @@ "homepage": "https://symfony.com", "time": "2015-07-26 09:08:40" }, + { + "name": "symfony/filesystem", + "version": "v2.6.11", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "823c035b1a5c13a4924e324d016eb07e70f94735" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/823c035b1a5c13a4924e324d016eb07e70f94735", + "reference": "823c035b1a5c13a4924e324d016eb07e70f94735", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "https://symfony.com", + "time": "2015-07-08 05:59:48" + }, { "name": "symfony/finder", "version": "v2.6.11", diff --git a/src/Composer/Downloader/PathDownloader.php b/src/Composer/Downloader/PathDownloader.php new file mode 100644 index 000000000..fbdb0aebc --- /dev/null +++ b/src/Composer/Downloader/PathDownloader.php @@ -0,0 +1,50 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Downloader; + +use Composer\Package\PackageInterface; +use Symfony\Component\Filesystem\Exception\IOException; +use Symfony\Component\Filesystem\Filesystem; + +/** + * Download a package from a local path. + * + * @author Samuel Roze + * @author Johann Reinke + */ +class PathDownloader extends FileDownloader +{ + /** + * {@inheritdoc} + */ + public function download(PackageInterface $package, $path) + { + $fileSystem = new Filesystem(); + if ($fileSystem->exists($path)) { + $fileSystem->remove($path); + } + + try { + $fileSystem->symlink($package->getDistUrl(), $path); + } catch (IOException $e) { + $fileSystem->mirror($package->getDistUrl(), $path); + } + + $this->io->writeError(sprintf( + ' Downloaded %s (%s) from %s', + $package->getName(), + $package->getFullPrettyVersion(), + $package->getDistUrl() + )); + } +} diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 6f9a5da36..1da6c30da 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -16,6 +16,7 @@ use Composer\Config\JsonConfigSource; use Composer\Json\JsonFile; use Composer\IO\IOInterface; use Composer\Package\Archiver; +use Composer\Package\Version\VersionGuesser; use Composer\Repository\RepositoryManager; use Composer\Repository\WritableRepositoryInterface; use Composer\Util\ProcessExecutor; @@ -264,7 +265,8 @@ class Factory // load package $parser = new VersionParser; - $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, new ProcessExecutor($io)); + $guesser = new VersionGuesser(new ProcessExecutor($io), $parser); + $loader = new Package\Loader\RootPackageLoader($rm, $config, $parser, $guesser); $package = $loader->load($localConfig); $composer->setPackage($package); @@ -331,6 +333,7 @@ class Factory $rm->setRepositoryClass('perforce', 'Composer\Repository\VcsRepository'); $rm->setRepositoryClass('hg', 'Composer\Repository\VcsRepository'); $rm->setRepositoryClass('artifact', 'Composer\Repository\ArtifactRepository'); + $rm->setRepositoryClass('path', 'Composer\Repository\PathRepository'); return $rm; } @@ -403,6 +406,7 @@ class Factory $dm->setDownloader('gzip', new Downloader\GzipDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('phar', new Downloader\PharDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('file', new Downloader\FileDownloader($io, $config, $eventDispatcher, $cache)); + $dm->setDownloader('path', new Downloader\PathDownloader($io, $config, $eventDispatcher, $cache)); return $dm; } diff --git a/src/Composer/Package/Loader/RootPackageLoader.php b/src/Composer/Package/Loader/RootPackageLoader.php index 9c7c436bc..3043a18c5 100644 --- a/src/Composer/Package/Loader/RootPackageLoader.php +++ b/src/Composer/Package/Loader/RootPackageLoader.php @@ -16,13 +16,10 @@ use Composer\Package\BasePackage; use Composer\Package\AliasPackage; use Composer\Config; use Composer\Factory; +use Composer\Package\Version\VersionGuesser; use Composer\Package\Version\VersionParser; use Composer\Repository\RepositoryManager; -use Composer\Repository\Vcs\HgDriver; -use Composer\IO\NullIO; use Composer\Util\ProcessExecutor; -use Composer\Util\Git as GitUtil; -use Composer\Util\Svn as SvnUtil; /** * ArrayLoader built for the sole purpose of loading the root package @@ -33,16 +30,28 @@ use Composer\Util\Svn as SvnUtil; */ class RootPackageLoader extends ArrayLoader { + /** + * @var RepositoryManager + */ private $manager; - private $config; - private $process; - public function __construct(RepositoryManager $manager, Config $config, VersionParser $parser = null, ProcessExecutor $process = null) + /** + * @var Config + */ + private $config; + + /** + * @var VersionGuesser + */ + private $versionGuesser; + + public function __construct(RepositoryManager $manager, Config $config, VersionParser $parser = null, VersionGuesser $versionGuesser = null) { + parent::__construct($parser); + $this->manager = $manager; $this->config = $config; - $this->process = $process ?: new ProcessExecutor(); - parent::__construct($parser); + $this->versionGuesser = $versionGuesser ?: new VersionGuesser(new ProcessExecutor(), $this->versionParser); } public function load(array $config, $class = 'Composer\Package\RootPackage') @@ -56,7 +65,7 @@ class RootPackageLoader extends ArrayLoader if (getenv('COMPOSER_ROOT_VERSION')) { $version = getenv('COMPOSER_ROOT_VERSION'); } else { - $version = $this->guessVersion($config); + $version = $this->versionGuesser->guessVersion($this->config, $config); } if (!$version) { @@ -176,172 +185,4 @@ class RootPackageLoader extends ArrayLoader return $references; } - - private function guessVersion(array $config) - { - if (function_exists('proc_open')) { - $version = $this->guessGitVersion($config); - if (null !== $version) { - return $version; - } - - $version = $this->guessHgVersion($config); - if (null !== $version) { - return $version; - } - - return $this->guessSvnVersion($config); - } - } - - private function guessGitVersion(array $config) - { - GitUtil::cleanEnv(); - - // try to fetch current version from git tags - if (0 === $this->process->execute('git describe --exact-match --tags', $output)) { - try { - return $this->versionParser->normalize(trim($output)); - } catch (\Exception $e) { - } - } - - // try to fetch current version from git branch - if (0 === $this->process->execute('git branch --no-color --no-abbrev -v', $output)) { - $branches = array(); - $isFeatureBranch = false; - $version = null; - - // find current branch and collect all branch names - foreach ($this->process->splitLines($output) as $branch) { - if ($branch && preg_match('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) { - if ($match[1] === '(no branch)' || substr($match[1], 0, 10) === '(detached ') { - $version = 'dev-'.$match[2]; - $isFeatureBranch = true; - } else { - $version = $this->versionParser->normalizeBranch($match[1]); - $isFeatureBranch = 0 === strpos($version, 'dev-'); - if ('9999999-dev' === $version) { - $version = 'dev-'.$match[1]; - } - } - } - - if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) { - if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+) .*$}', $branch, $match)) { - $branches[] = $match[1]; - } - } - } - - if (!$isFeatureBranch) { - return $version; - } - - // try to find the best (nearest) version branch to assume this feature's version - $version = $this->guessFeatureVersion($config, $version, $branches, 'git rev-list %candidate%..%branch%'); - - return $version; - } - } - - private function guessHgVersion(array $config) - { - // try to fetch current version from hg branch - if (0 === $this->process->execute('hg branch', $output)) { - $branch = trim($output); - $version = $this->versionParser->normalizeBranch($branch); - $isFeatureBranch = 0 === strpos($version, 'dev-'); - - if ('9999999-dev' === $version) { - $version = 'dev-'.$branch; - } - - if (!$isFeatureBranch) { - return $version; - } - - // re-use the HgDriver to fetch branches (this properly includes bookmarks) - $config = array('url' => getcwd()); - $driver = new HgDriver($config, new NullIO(), $this->config, $this->process); - $branches = array_keys($driver->getBranches()); - - // try to find the best (nearest) version branch to assume this feature's version - $version = $this->guessFeatureVersion($config, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"'); - - return $version; - } - } - - private function guessFeatureVersion(array $config, $version, array $branches, $scmCmdline) - { - // ignore feature branches if they have no branch-alias or self.version is used - // and find the branch they came from to use as a version instead - if ((isset($config['extra']['branch-alias']) && !isset($config['extra']['branch-alias'][$version])) - || strpos(json_encode($config), '"self.version"') - ) { - $branch = preg_replace('{^dev-}', '', $version); - $length = PHP_INT_MAX; - - $nonFeatureBranches = ''; - if (!empty($config['non-feature-branches'])) { - $nonFeatureBranches = implode('|', $config['non-feature-branches']); - } - - foreach ($branches as $candidate) { - // return directly, if branch is configured to be non-feature branch - if ($candidate === $branch && preg_match('{^(' . $nonFeatureBranches . ')$}', $candidate)) { - return $version; - } - - // do not compare against other feature branches - if ($candidate === $branch || !preg_match('{^(master|trunk|default|develop|\d+\..+)$}', $candidate, $match)) { - continue; - } - - $cmdLine = str_replace(array('%candidate%', '%branch%'), array($candidate, $branch), $scmCmdline); - if (0 !== $this->process->execute($cmdLine, $output)) { - continue; - } - - if (strlen($output) < $length) { - $length = strlen($output); - $version = $this->versionParser->normalizeBranch($candidate); - if ('9999999-dev' === $version) { - $version = 'dev-'.$match[1]; - } - } - } - } - - return $version; - } - - private function guessSvnVersion(array $config) - { - SvnUtil::cleanEnv(); - - // try to fetch current version from svn - if (0 === $this->process->execute('svn info --xml', $output)) { - $trunkPath = isset($config['trunk-path']) ? preg_quote($config['trunk-path'], '#') : 'trunk'; - $branchesPath = isset($config['branches-path']) ? preg_quote($config['branches-path'], '#') : 'branches'; - $tagsPath = isset($config['tags-path']) ? preg_quote($config['tags-path'], '#') : 'tags'; - - $urlPattern = '#.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))#'; - - if (preg_match($urlPattern, $output, $matches)) { - if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) { - // we are in a branches path - $version = $this->versionParser->normalizeBranch($matches[3]); - if ('9999999-dev' === $version) { - $version = 'dev-'.$matches[3]; - } - - return $version; - } - - return $this->versionParser->normalize(trim($matches[1])); - } - } - } } diff --git a/src/Composer/Package/Version/VersionGuesser.php b/src/Composer/Package/Version/VersionGuesser.php new file mode 100644 index 000000000..4a5e2cc0a --- /dev/null +++ b/src/Composer/Package/Version/VersionGuesser.php @@ -0,0 +1,224 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Version; + +use Composer\Config; +use Composer\Repository\Vcs\HgDriver; +use Composer\IO\NullIO; +use Composer\Util\Git as GitUtil; +use Composer\Util\ProcessExecutor; +use Composer\Util\Svn as SvnUtil; + +/** + * Try to guess the current version number based on different VCS configuration. + * + * @author Jordi Boggiano + * @author Samuel Roze + */ +class VersionGuesser +{ + /** + * @var ProcessExecutor + */ + private $process; + + /** + * @var VersionParser + */ + private $versionParser; + + /** + * @var null|string + */ + private $cwd; + + /** + * @param ProcessExecutor $process + * @param VersionParser $versionParser + * @param string $cwd + */ + public function __construct(ProcessExecutor $process, VersionParser $versionParser, $cwd = null) + { + $this->process = $process; + $this->versionParser = $versionParser; + $this->cwd = $cwd ?: getcwd(); + } + + public function guessVersion(Config $config, array $packageConfig) + { + if (function_exists('proc_open')) { + $version = $this->guessGitVersion($packageConfig); + if (null !== $version) { + return $version; + } + + $version = $this->guessHgVersion($config, $packageConfig); + if (null !== $version) { + return $version; + } + + return $this->guessSvnVersion($packageConfig); + } + } + + private function guessGitVersion(array $config) + { + GitUtil::cleanEnv(); + + // try to fetch current version from git tags + if (0 === $this->process->execute('git describe --exact-match --tags', $output, $this->cwd)) { + try { + return $this->versionParser->normalize(trim($output)); + } catch (\Exception $e) { + } + } + + // try to fetch current version from git branch + if (0 === $this->process->execute('git branch --no-color --no-abbrev -v', $output, $this->cwd)) { + $branches = array(); + $isFeatureBranch = false; + $version = null; + + // find current branch and collect all branch names + foreach ($this->process->splitLines($output) as $branch) { + if ($branch && preg_match('{^(?:\* ) *(\(no branch\)|\(detached from \S+\)|\S+) *([a-f0-9]+) .*$}', $branch, $match)) { + if ($match[1] === '(no branch)' || substr($match[1], 0, 10) === '(detached ') { + $version = 'dev-'.$match[2]; + $isFeatureBranch = true; + } else { + $version = $this->versionParser->normalizeBranch($match[1]); + $isFeatureBranch = 0 === strpos($version, 'dev-'); + if ('9999999-dev' === $version) { + $version = 'dev-'.$match[1]; + } + } + } + + if ($branch && !preg_match('{^ *[^/]+/HEAD }', $branch)) { + if (preg_match('{^(?:\* )? *(\S+) *([a-f0-9]+) .*$}', $branch, $match)) { + $branches[] = $match[1]; + } + } + } + + if (!$isFeatureBranch) { + return $version; + } + + // try to find the best (nearest) version branch to assume this feature's version + $version = $this->guessFeatureVersion($config, $version, $branches, 'git rev-list %candidate%..%branch%'); + + return $version; + } + } + + private function guessHgVersion(Config $config, array $packageConfig) + { + // try to fetch current version from hg branch + if (0 === $this->process->execute('hg branch', $output, $this->cwd)) { + $branch = trim($output); + $version = $this->versionParser->normalizeBranch($branch); + $isFeatureBranch = 0 === strpos($version, 'dev-'); + + if ('9999999-dev' === $version) { + $version = 'dev-'.$branch; + } + + if (!$isFeatureBranch) { + return $version; + } + + // re-use the HgDriver to fetch branches (this properly includes bookmarks) + $packageConfig = array('url' => $this->cwd); + $driver = new HgDriver($packageConfig, new NullIO(), $config, $this->process); + $branches = array_keys($driver->getBranches()); + + // try to find the best (nearest) version branch to assume this feature's version + $version = $this->guessFeatureVersion($config, $version, $branches, 'hg log -r "not ancestors(\'%candidate%\') and ancestors(\'%branch%\')" --template "{node}\\n"'); + + return $version; + } + } + + private function guessFeatureVersion(array $config, $version, array $branches, $scmCmdline) + { + // ignore feature branches if they have no branch-alias or self.version is used + // and find the branch they came from to use as a version instead + if ((isset($config['extra']['branch-alias']) && !isset($config['extra']['branch-alias'][$version])) + || strpos(json_encode($config), '"self.version"') + ) { + $branch = preg_replace('{^dev-}', '', $version); + $length = PHP_INT_MAX; + + $nonFeatureBranches = ''; + if (!empty($config['non-feature-branches'])) { + $nonFeatureBranches = implode('|', $config['non-feature-branches']); + } + + foreach ($branches as $candidate) { + // return directly, if branch is configured to be non-feature branch + if ($candidate === $branch && preg_match('{^(' . $nonFeatureBranches . ')$}', $candidate)) { + return $version; + } + + // do not compare against other feature branches + if ($candidate === $branch || !preg_match('{^(master|trunk|default|develop|\d+\..+)$}', $candidate, $match)) { + continue; + } + + $cmdLine = str_replace(array('%candidate%', '%branch%'), array($candidate, $branch), $scmCmdline); + if (0 !== $this->process->execute($cmdLine, $output, $this->cwd)) { + continue; + } + + if (strlen($output) < $length) { + $length = strlen($output); + $version = $this->versionParser->normalizeBranch($candidate); + if ('9999999-dev' === $version) { + $version = 'dev-'.$match[1]; + } + } + } + } + + return $version; + } + + private function guessSvnVersion(array $config) + { + SvnUtil::cleanEnv(); + + // try to fetch current version from svn + if (0 === $this->process->execute('svn info --xml', $output, $this->cwd)) { + $trunkPath = isset($config['trunk-path']) ? preg_quote($config['trunk-path'], '#') : 'trunk'; + $branchesPath = isset($config['branches-path']) ? preg_quote($config['branches-path'], '#') : 'branches'; + $tagsPath = isset($config['tags-path']) ? preg_quote($config['tags-path'], '#') : 'tags'; + + $urlPattern = '#.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))#'; + + if (preg_match($urlPattern, $output, $matches)) { + if (isset($matches[2]) && ($branchesPath === $matches[2] || $tagsPath === $matches[2])) { + // we are in a branches path + $version = $this->versionParser->normalizeBranch($matches[3]); + if ('9999999-dev' === $version) { + $version = 'dev-'.$matches[3]; + } + + return $version; + } + + return $this->versionParser->normalize(trim($matches[1])); + } + } + } +} diff --git a/src/Composer/Repository/PathRepository.php b/src/Composer/Repository/PathRepository.php new file mode 100644 index 000000000..a907c5adb --- /dev/null +++ b/src/Composer/Repository/PathRepository.php @@ -0,0 +1,134 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Config; +use Composer\IO\IOInterface; +use Composer\Json\JsonFile; +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Loader\LoaderInterface; +use Composer\Package\Version\VersionGuesser; +use Composer\Package\Version\VersionParser; +use Composer\Util\ProcessExecutor; +use Symfony\Component\Filesystem\Filesystem; + +/** + * This repository allows installing local packages that are not necessarily under their own VCS. + * + * The local packages will be symlinked when possible, else they will be copied. + * + * @code + * "require": { + * "/": "*" + * }, + * "repositories": [ + * { + * "type": "path", + * "url": "../../relative/path/to/package/" + * }, + * { + * "type": "path", + * "url": "/absolute/path/to/package/" + * } + * ] + * @endcode + * + * @author Samuel Roze + * @author Johann Reinke + */ +class PathRepository extends ArrayRepository +{ + /** + * @var Config + */ + private $config; + + /** + * @var Filesystem + */ + private $fileSystem; + + /** + * @var ArrayLoader + */ + private $loader; + + /** + * @var VersionGuesser + */ + private $versionGuesser; + + /** + * @var array + */ + private $packageConfig; + + /** + * @var string + */ + private $path; + + /** + * Initializes path repository. + * + * @param array $packageConfig + * @param IOInterface $io + * @param Config $config + * @param LoaderInterface $loader + * @param Filesystem $filesystem + * @param VersionGuesser $versionGuesser + */ + public function __construct(array $packageConfig, IOInterface $io, Config $config, LoaderInterface $loader = null, Filesystem $filesystem = null, VersionGuesser $versionGuesser = null) + { + if (!isset($packageConfig['url'])) { + throw new \RuntimeException('You must specify the `url` configuration for the path repository'); + } + + $this->fileSystem = $filesystem ?: new Filesystem(); + $this->loader = $loader ?: new ArrayLoader(); + $this->config = $config; + $this->packageConfig = $packageConfig; + $this->path = realpath(rtrim($packageConfig['url'], '/')) . '/'; + $this->versionGuesser = $versionGuesser ?: new VersionGuesser(new ProcessExecutor($io), new VersionParser(), $this->path); + } + + /** + * Initializes path repository. + * + * This method will basically read the folder and add the found package. + * + */ + protected function initialize() + { + parent::initialize(); + + $composerFilePath = $this->path.'composer.json'; + if (!$this->fileSystem->exists($composerFilePath)) { + throw new \RuntimeException(sprintf('No `composer.json` file found in path repository "%s"', $this->path)); + } + + $json = file_get_contents($composerFilePath); + $package = JsonFile::parseJson($json, $composerFilePath); + $package['dist'] = array( + 'type' => 'path', + 'url' => $this->path, + ); + + if (!isset($package['version'])) { + $package['version'] = $this->versionGuesser->guessVersion($this->config, $this->packageConfig) ?: 'dev-master'; + } + + $package = $this->loader->load($package); + $this->addPackage($package); + } +} diff --git a/src/Composer/Util/Filesystem.php b/src/Composer/Util/Filesystem.php index 92e5e0623..c36347fb7 100644 --- a/src/Composer/Util/Filesystem.php +++ b/src/Composer/Util/Filesystem.php @@ -508,7 +508,14 @@ class Filesystem return unlink($path); } - private function isSymlinkedDirectory($directory) + /** + * return true if that directory is a symlink. + * + * @param string $directory + * + * @return bool + */ + public function isSymlinkedDirectory($directory) { if (!is_dir($directory)) { return false; diff --git a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php index 2a4d9ccf6..77139f928 100644 --- a/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/RootPackageLoaderTest.php @@ -15,175 +15,11 @@ namespace Composer\Test\Package\Loader; use Composer\Config; use Composer\Package\Loader\RootPackageLoader; use Composer\Package\BasePackage; +use Composer\Package\Version\VersionGuesser; +use Composer\Package\Version\VersionParser; class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase { - public function testDetachedHeadBecomesDevHash() - { - if (!function_exists('proc_open')) { - $this->markTestSkipped('proc_open() is not available'); - } - - $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; - - $manager = $this->getMockBuilder('\\Composer\\Repository\\RepositoryManager') - ->disableOriginalConstructor() - ->getMock() - ; - - $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') - ->setMethods(array('execute')) - ->disableArgumentCloning() - ->disableOriginalConstructor() - ->getMock() - ; - - $executor - ->expects($this->at(0)) - ->method('execute') - ->with('git describe --exact-match --tags') - ->willReturn(1) - ; - - $self = $this; - - $executor - ->expects($this->at(1)) - ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self, $commitHash) { - $self->assertEquals('git branch --no-color --no-abbrev -v', $command); - $output = "* (no branch) $commitHash Commit message\n"; - - return 0; - }) - ; - - $config = new Config; - $config->merge(array('repositories' => array('packagist' => false))); - $loader = new RootPackageLoader($manager, $config, null, $executor); - $package = $loader->load(array()); - - $this->assertEquals("dev-$commitHash", $package->getVersion()); - } - - public function testTagBecomesVersion() - { - if (!function_exists('proc_open')) { - $this->markTestSkipped('proc_open() is not available'); - } - - $manager = $this->getMockBuilder('\\Composer\\Repository\\RepositoryManager') - ->disableOriginalConstructor() - ->getMock() - ; - - $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') - ->setMethods(array('execute')) - ->disableArgumentCloning() - ->disableOriginalConstructor() - ->getMock() - ; - - $self = $this; - - $executor - ->expects($this->at(0)) - ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git describe --exact-match --tags', $command); - $output = "v2.0.5-alpha2"; - - return 0; - }) - ; - - $config = new Config; - $config->merge(array('repositories' => array('packagist' => false))); - $loader = new RootPackageLoader($manager, $config, null, $executor); - $package = $loader->load(array()); - - $this->assertEquals("2.0.5.0-alpha2", $package->getVersion()); - } - - public function testInvalidTagBecomesVersion() - { - if (!function_exists('proc_open')) { - $this->markTestSkipped('proc_open() is not available'); - } - - $manager = $this->getMockBuilder('\\Composer\\Repository\\RepositoryManager') - ->disableOriginalConstructor() - ->getMock() - ; - - $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') - ->setMethods(array('execute')) - ->disableArgumentCloning() - ->disableOriginalConstructor() - ->getMock() - ; - - $self = $this; - - $executor - ->expects($this->at(0)) - ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git describe --exact-match --tags', $command); - $output = "foo-bar"; - - return 0; - }) - ; - - $executor - ->expects($this->at(1)) - ->method('execute') - ->willReturnCallback(function ($command, &$output) use ($self) { - $self->assertEquals('git branch --no-color --no-abbrev -v', $command); - $output = "* foo 03a15d220da53c52eddd5f32ffca64a7b3801bea Commit message\n"; - - return 0; - }) - ; - - $config = new Config; - $config->merge(array('repositories' => array('packagist' => false))); - $loader = new RootPackageLoader($manager, $config, null, $executor); - $package = $loader->load(array()); - - $this->assertEquals("dev-foo", $package->getVersion()); - } - - public function testNoVersionIsVisibleInPrettyVersion() - { - $manager = $this->getMockBuilder('\\Composer\\Repository\\RepositoryManager') - ->disableOriginalConstructor() - ->getMock() - ; - - $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') - ->setMethods(array('execute')) - ->disableArgumentCloning() - ->disableOriginalConstructor() - ->getMock() - ; - - $executor - ->expects($this->any()) - ->method('execute') - ->willReturn(null) - ; - - $config = new Config; - $config->merge(array('repositories' => array('packagist' => false))); - $loader = new RootPackageLoader($manager, $config, null, $executor); - $package = $loader->load(array()); - - $this->assertEquals("1.0.0.0", $package->getVersion()); - $this->assertEquals("No version set (parsed as 1.0.0)", $package->getPrettyVersion()); - } - protected function loadPackage($data) { $manager = $this->getMockBuilder('\\Composer\\Repository\\RepositoryManager') @@ -218,6 +54,37 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase ), $package->getStabilityFlags()); } + public function testNoVersionIsVisibleInPrettyVersion() + { + $manager = $this->getMockBuilder('\\Composer\\Repository\\RepositoryManager') + ->disableOriginalConstructor() + ->getMock() + ; + + $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') + ->setMethods(array('execute')) + ->disableArgumentCloning() + ->disableOriginalConstructor() + ->getMock() + ; + + $executor + ->expects($this->any()) + ->method('execute') + ->willReturn(null) + ; + + $config = new Config; + $config->merge(array('repositories' => array('packagist' => false))); + $loader = new RootPackageLoader($manager, $config, null, new VersionGuesser($executor, new VersionParser())); + $package = $loader->load(array()); + + $this->assertEquals("1.0.0.0", $package->getVersion()); + $this->assertEquals("No version set (parsed as 1.0.0)", $package->getPrettyVersion()); + } + + + public function testFeatureBranchPrettyVersion() { if (!function_exists('proc_open')) { @@ -272,7 +139,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase $config = new Config; $config->merge(array('repositories' => array('packagist' => false))); - $loader = new RootPackageLoader($manager, $config, null, $executor); + $loader = new RootPackageLoader($manager, $config, null, new VersionGuesser($executor, new VersionParser())); $package = $loader->load(array('require' => array('foo/bar' => 'self.version'))); $this->assertEquals("dev-master", $package->getPrettyVersion()); @@ -321,7 +188,7 @@ class RootPackageLoaderTest extends \PHPUnit_Framework_TestCase $config = new Config; $config->merge(array('repositories' => array('packagist' => false))); - $loader = new RootPackageLoader($manager, $config, null, $executor); + $loader = new RootPackageLoader($manager, $config, null, new VersionGuesser($executor, new VersionParser())); $package = $loader->load(array('require' => array('foo/bar' => 'self.version'), "non-feature-branches" => array("latest-.*"))); $this->assertEquals("dev-latest-production", $package->getPrettyVersion()); diff --git a/tests/Composer/Test/Package/Version/VersionGuesserTest.php b/tests/Composer/Test/Package/Version/VersionGuesserTest.php new file mode 100644 index 000000000..2c5db1ff5 --- /dev/null +++ b/tests/Composer/Test/Package/Version/VersionGuesserTest.php @@ -0,0 +1,137 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Package\Version; + +use Composer\Config; +use Composer\Package\Version\VersionGuesser; +use Composer\Package\Version\VersionParser; + +class VersionGuesserTest extends \PHPUnit_Framework_TestCase +{ + public function setUp() + { + if (!function_exists('proc_open')) { + $this->markTestSkipped('proc_open() is not available'); + } + } + + public function testDetachedHeadBecomesDevHash() + { + $commitHash = '03a15d220da53c52eddd5f32ffca64a7b3801bea'; + + $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') + ->setMethods(array('execute')) + ->disableArgumentCloning() + ->disableOriginalConstructor() + ->getMock() + ; + + $executor + ->expects($this->at(0)) + ->method('execute') + ->with('git describe --exact-match --tags') + ->willReturn(1) + ; + + $self = $this; + + $executor + ->expects($this->at(1)) + ->method('execute') + ->willReturnCallback(function ($command, &$output) use ($self, $commitHash) { + $self->assertEquals('git branch --no-color --no-abbrev -v', $command); + $output = "* (no branch) $commitHash Commit message\n"; + + return 0; + }) + ; + + $config = new Config; + $config->merge(array('repositories' => array('packagist' => false))); + $guesser = new VersionGuesser($executor, new VersionParser()); + $version = $guesser->guessVersion($config, array()); + + $this->assertEquals("dev-$commitHash", $version); + } + + public function testTagBecomesVersion() + { + $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') + ->setMethods(array('execute')) + ->disableArgumentCloning() + ->disableOriginalConstructor() + ->getMock() + ; + + $self = $this; + + $executor + ->expects($this->at(0)) + ->method('execute') + ->willReturnCallback(function ($command, &$output) use ($self) { + $self->assertEquals('git describe --exact-match --tags', $command); + $output = "v2.0.5-alpha2"; + + return 0; + }) + ; + + $config = new Config; + $config->merge(array('repositories' => array('packagist' => false))); + $guesser = new VersionGuesser($executor, new VersionParser()); + $version = $guesser->guessVersion($config, array()); + + $this->assertEquals("2.0.5.0-alpha2", $version); + } + + public function testInvalidTagBecomesVersion() + { + $executor = $this->getMockBuilder('\\Composer\\Util\\ProcessExecutor') + ->setMethods(array('execute')) + ->disableArgumentCloning() + ->disableOriginalConstructor() + ->getMock() + ; + + $self = $this; + + $executor + ->expects($this->at(0)) + ->method('execute') + ->willReturnCallback(function ($command, &$output) use ($self) { + $self->assertEquals('git describe --exact-match --tags', $command); + $output = "foo-bar"; + + return 0; + }) + ; + + $executor + ->expects($this->at(1)) + ->method('execute') + ->willReturnCallback(function ($command, &$output) use ($self) { + $self->assertEquals('git branch --no-color --no-abbrev -v', $command); + $output = "* foo 03a15d220da53c52eddd5f32ffca64a7b3801bea Commit message\n"; + + return 0; + }) + ; + + $config = new Config; + $config->merge(array('repositories' => array('packagist' => false))); + $guesser = new VersionGuesser($executor, new VersionParser()); + $version = $guesser->guessVersion($config, array()); + + $this->assertEquals("dev-foo", $version); + } +} diff --git a/tests/Composer/Test/Repository/Fixtures/path/with-version/composer.json b/tests/Composer/Test/Repository/Fixtures/path/with-version/composer.json new file mode 100644 index 000000000..fa853d690 --- /dev/null +++ b/tests/Composer/Test/Repository/Fixtures/path/with-version/composer.json @@ -0,0 +1,4 @@ +{ + "name": "test/path", + "version": "0.0.2" +} \ No newline at end of file diff --git a/tests/Composer/Test/Repository/Fixtures/path/without-version/composer.json b/tests/Composer/Test/Repository/Fixtures/path/without-version/composer.json new file mode 100644 index 000000000..2fcbad12a --- /dev/null +++ b/tests/Composer/Test/Repository/Fixtures/path/without-version/composer.json @@ -0,0 +1,3 @@ +{ + "name": "test/path" +} \ No newline at end of file diff --git a/tests/Composer/Test/Repository/PathRepositoryTest.php b/tests/Composer/Test/Repository/PathRepositoryTest.php new file mode 100644 index 000000000..fb64e458b --- /dev/null +++ b/tests/Composer/Test/Repository/PathRepositoryTest.php @@ -0,0 +1,62 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Repository; + +use Composer\Package\Loader\ArrayLoader; +use Composer\Package\Version\VersionParser; +use Composer\TestCase; +use Composer\IO\NullIO; +use Composer\Config; +use Composer\Package\BasePackage; + +class PathRepositoryTest extends TestCase +{ + public function testLoadPackageFromFileSystemWithVersion() + { + $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') + ->getMock(); + + $config = new \Composer\Config(); + $loader = new ArrayLoader(new VersionParser()); + $versionGuesser = null; + + $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', 'with-version')); + $repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config, $loader); + $repository->getPackages(); + + $this->assertEquals(1, $repository->count()); + $this->assertTrue($repository->hasPackage($this->getPackage('test/path', '0.0.2'))); + } + + public function testLoadPackageFromFileSystemWithoutVersion() + { + $ioInterface = $this->getMockBuilder('Composer\IO\IOInterface') + ->getMock(); + + $config = new \Composer\Config(); + $loader = new ArrayLoader(new VersionParser()); + $versionGuesser = null; + + $repositoryUrl = implode(DIRECTORY_SEPARATOR, array(__DIR__, 'Fixtures', 'path', 'without-version')); + $repository = new PathRepository(array('url' => $repositoryUrl), $ioInterface, $config, $loader); + $packages = $repository->getPackages(); + + $this->assertEquals(1, $repository->count()); + + $package = $packages[0]; + $this->assertEquals('test/path', $package->getName()); + + $packageVersion = $package->getVersion(); + $this->assertTrue(!empty($packageVersion)); + } +}