1
0
Fork 0

Move version guessing in `VersionGuesser` class

pull/4365/head
Samuel ROZE 2015-08-18 14:58:18 +01:00
parent ead68d3d49
commit 5261a5fa03
5 changed files with 416 additions and 347 deletions

View File

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

View File

@ -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 = '#<url>.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))</url>#';
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]));
}
}
}
}

View File

@ -0,0 +1,201 @@
<?php
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;
class VersionGuesser
{
/**
* @var ProcessExecutor
*/
private $process;
/**
* @var VersionParser
*/
private $versionParser;
/**
* @param ProcessExecutor $process
* @param VersionParser $versionParser
*/
public function __construct(ProcessExecutor $process, VersionParser $versionParser)
{
$this->process = $process;
$this->versionParser = $versionParser;
}
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)) {
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(Config $config, array $packageConfig)
{
// 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)
$packageConfig = array('url' => getcwd());
$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)) {
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 = '#<url>.*/('.$trunkPath.'|('.$branchesPath.'|'. $tagsPath .')/(.*))</url>#';
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]));
}
}
}
}

View File

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

View File

@ -0,0 +1,158 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Test\Package\Version;
use Composer\Config;
use Composer\Package\Loader\RootPackageLoader;
use Composer\Package\Version\VersionGuesser;
use Composer\Package\Version\VersionParser;
class VersionGuesserTest 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)));
$guesser = new VersionGuesser($executor, new VersionParser());
$version = $guesser->guessVersion($config, []);
$this->assertEquals("dev-$commitHash", $version);
}
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)));
$guesser = new VersionGuesser($executor, new VersionParser());
$version = $guesser->guessVersion($config, []);
$this->assertEquals("2.0.5.0-alpha2", $version);
}
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)));
$guesser = new VersionGuesser($executor, new VersionParser());
$version = $guesser->guessVersion($config, []);
$this->assertEquals("dev-foo", $version);
}
}