1
0
Fork 0

Add support for mirrors in composer repos

pull/2993/merge
Jordi Boggiano 2013-07-02 01:45:43 +02:00
parent e707dcd92f
commit 77163f66fc
15 changed files with 258 additions and 48 deletions

View File

@ -79,18 +79,38 @@ class FileDownloader implements DownloaderInterface
*/
public function download(PackageInterface $package, $path)
{
$url = $package->getDistUrl();
if (!$url) {
if (!$package->getDistUrl()) {
throw new \InvalidArgumentException('The given package is missing url information');
}
$this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$urls = $package->getDistUrls();
while ($url = array_shift($urls)) {
try {
$this->doDownload($package, $path, $url);
break;
} catch (\Exception $e) {
if ($this->io->isDebug()) {
$this->io->write('');
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
} elseif (count($urls)) {
$this->io->write('');
$this->io->write(' Failed, trying the next URL');
} else {
throw $e;
}
}
}
}
protected function doDownload(PackageInterface $package, $path, $url)
{
$this->filesystem->removeDirectory($path);
$this->filesystem->ensureDirectoryExists($path);
$fileName = $this->getFileName($package, $path);
$this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$processedUrl = $this->processUrl($package, $url);
$hostname = parse_url($processedUrl, PHP_URL_HOST);

View File

@ -26,7 +26,7 @@ class GitDownloader extends VcsDownloader
/**
* {@inheritDoc}
*/
public function doDownload(PackageInterface $package, $path)
public function doDownload(PackageInterface $package, $path, $url)
{
$this->cleanEnv();
$path = $this->normalizePath($path);
@ -40,8 +40,8 @@ class GitDownloader extends VcsDownloader
return sprintf($command, escapeshellarg($url), escapeshellarg($path), escapeshellarg($ref));
};
$this->runCommand($commandCallable, $package->getSourceUrl(), $path, true);
$this->setPushUrl($package, $path);
$this->runCommand($commandCallable, $url, $path, true);
$this->setPushUrl($path, $url);
if ($newRef = $this->updateToCommit($path, $ref, $package->getPrettyVersion(), $package->getReleaseDate())) {
if ($package->getDistReference() === $package->getSourceReference()) {
@ -54,7 +54,7 @@ class GitDownloader extends VcsDownloader
/**
* {@inheritDoc}
*/
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path)
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
{
$this->cleanEnv();
$path = $this->normalizePath($path);
@ -76,7 +76,7 @@ class GitDownloader extends VcsDownloader
return sprintf($command, escapeshellarg($url));
};
$this->runCommand($commandCallable, $target->getSourceUrl(), $path);
$this->runCommand($commandCallable, $url, $path);
if ($newRef = $this->updateToCommit($path, $ref, $target->getPrettyVersion(), $target->getReleaseDate())) {
if ($target->getDistReference() === $target->getSourceReference()) {
$target->setDistReference($newRef);
@ -412,10 +412,10 @@ class GitDownloader extends VcsDownloader
return preg_replace('{://([^@]+?):.+?@}', '://$1:***@', $message);
}
protected function setPushUrl(PackageInterface $package, $path)
protected function setPushUrl($path, $url)
{
// set push url for github projects
if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/([^/]+)/([^/]+?)(?:\.git)?$}', $package->getSourceUrl(), $match)) {
if (preg_match('{^(?:https?|git)://'.$this->getGitHubDomainsRegex().'/([^/]+)/([^/]+?)(?:\.git)?$}', $url, $match)) {
$protocols = $this->config->get('github-protocols');
$pushUrl = 'git@'.$match[1].':'.$match[2].'/'.$match[3].'.git';
if ($protocols[0] !== 'git') {

View File

@ -22,9 +22,9 @@ class HgDownloader extends VcsDownloader
/**
* {@inheritDoc}
*/
public function doDownload(PackageInterface $package, $path)
public function doDownload(PackageInterface $package, $path, $url)
{
$url = escapeshellarg($package->getSourceUrl());
$url = escapeshellarg($url);
$ref = escapeshellarg($package->getSourceReference());
$this->io->write(" Cloning ".$package->getSourceReference());
$command = sprintf('hg clone %s %s', $url, escapeshellarg($path));
@ -40,9 +40,9 @@ class HgDownloader extends VcsDownloader
/**
* {@inheritDoc}
*/
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path)
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
{
$url = escapeshellarg($target->getSourceUrl());
$url = escapeshellarg($url);
$ref = escapeshellarg($target->getSourceReference());
$this->io->write(" Updating to ".$target->getSourceReference());

View File

@ -24,10 +24,9 @@ class SvnDownloader extends VcsDownloader
/**
* {@inheritDoc}
*/
public function doDownload(PackageInterface $package, $path)
public function doDownload(PackageInterface $package, $path, $url)
{
SvnUtil::cleanEnv();
$url = $package->getSourceUrl();
$ref = $package->getSourceReference();
$this->io->write(" Checking out ".$package->getSourceReference());
@ -37,10 +36,9 @@ class SvnDownloader extends VcsDownloader
/**
* {@inheritDoc}
*/
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path)
public function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url)
{
SvnUtil::cleanEnv();
$url = $target->getSourceUrl();
$ref = $target->getSourceReference();
if (!is_dir($path.'/.svn')) {

View File

@ -56,7 +56,23 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
$this->io->write(" - Installing <info>" . $package->getName() . "</info> (<comment>" . VersionParser::formatVersion($package) . "</comment>)");
$this->filesystem->removeDirectory($path);
$this->doDownload($package, $path);
$urls = $package->getSourceUrls();
while ($url = array_shift($urls)) {
try {
$this->doDownload($package, $path, $url);
break;
} catch (\Exception $e) {
if ($this->io->isDebug()) {
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
} elseif (count($urls)) {
$this->io->write(' Failed, trying the next URL');
} else {
throw $e;
}
}
}
$this->io->write('');
}
@ -87,17 +103,28 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
$this->io->write(" - Updating <info>" . $name . "</info> (<comment>" . $from . "</comment> => <comment>" . $to . "</comment>)");
$this->cleanChanges($initial, $path, true);
try {
$this->doUpdate($initial, $target, $path);
} catch (\Exception $e) {
// in case of failed update, try to reapply the changes before aborting
$this->reapplyChanges($path);
$urls = $target->getSourceUrls();
while ($url = array_shift($urls)) {
try {
$this->doUpdate($initial, $target, $path, $url);
break;
} catch (\Exception $e) {
if ($this->io->isDebug()) {
$this->io->write('Failed: ['.get_class($e).'] '.$e->getMessage());
} elseif (count($urls)) {
$this->io->write(' Failed, trying the next URL');
} else {
// in case of failed update, try to reapply the changes before aborting
$this->reapplyChanges($path);
throw $e;
throw $e;
}
}
}
$this->reapplyChanges($path);
//print the commit logs if in verbose mode
// print the commit logs if in verbose mode
if ($this->io->isVerbose()) {
$message = 'Pulling in changes:';
$logs = $this->getCommitLogs($initial->getSourceReference(), $target->getSourceReference(), $path);
@ -176,8 +203,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
*
* @param PackageInterface $package package instance
* @param string $path download path
* @param string $url package url
*/
abstract protected function doDownload(PackageInterface $package, $path);
abstract protected function doDownload(PackageInterface $package, $path, $url);
/**
* Updates specific package in specific folder from initial to target version.
@ -185,8 +213,9 @@ abstract class VcsDownloader implements DownloaderInterface, ChangeReportInterfa
* @param PackageInterface $initial initial package
* @param PackageInterface $target updated package
* @param string $path download path
* @param string $url package url
*/
abstract protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path);
abstract protected function doUpdate(PackageInterface $initial, PackageInterface $target, $path, $url);
/**
* Fetches the commit logs between two commits

View File

@ -209,6 +209,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return $this->aliasOf->getSourceUrl();
}
public function getSourceUrls()
{
return $this->aliasOf->getSourceUrls();
}
public function getSourceReference()
{
return $this->aliasOf->getSourceReference();
@ -217,6 +221,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return $this->aliasOf->setSourceReference($reference);
}
public function setSourceMirrors($mirrors)
{
return $this->aliasOf->setSourceMirrors($mirrors);
}
public function getSourceMirrors()
{
return $this->aliasOf->getSourceMirrors();
}
public function getDistType()
{
return $this->aliasOf->getDistType();
@ -225,6 +237,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return $this->aliasOf->getDistUrl();
}
public function getDistUrls()
{
return $this->aliasOf->getDistUrls();
}
public function getDistReference()
{
return $this->aliasOf->getDistReference();
@ -241,6 +257,14 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
{
return $this->aliasOf->getTransportOptions();
}
public function setDistMirrors($mirrors)
{
return $this->aliasOf->setDistMirrors($mirrors);
}
public function getDistMirrors()
{
return $this->aliasOf->getDistMirrors();
}
public function getScripts()
{
return $this->aliasOf->getScripts();

View File

@ -49,6 +49,9 @@ class ArrayDumper
$data['source']['type'] = $package->getSourceType();
$data['source']['url'] = $package->getSourceUrl();
$data['source']['reference'] = $package->getSourceReference();
if ($mirrors = $package->getSourceMirrors()) {
$data['source']['mirrors'] = $mirrors;
}
}
if ($package->getDistType()) {
@ -56,6 +59,9 @@ class ArrayDumper
$data['dist']['url'] = $package->getDistUrl();
$data['dist']['reference'] = $package->getDistReference();
$data['dist']['shasum'] = $package->getDistSha1Checksum();
if ($mirrors = $package->getDistMirrors()) {
$data['dist']['mirrors'] = $mirrors;
}
}
if ($package->getArchiveExcludes()) {

View File

@ -87,6 +87,9 @@ class ArrayLoader implements LoaderInterface
$package->setSourceType($config['source']['type']);
$package->setSourceUrl($config['source']['url']);
$package->setSourceReference($config['source']['reference']);
if (isset($config['source']['mirrors'])) {
$package->setSourceMirrors($config['source']['mirrors']);
}
}
if (isset($config['dist'])) {
@ -103,6 +106,9 @@ class ArrayLoader implements LoaderInterface
$package->setDistUrl($config['dist']['url']);
$package->setDistReference(isset($config['dist']['reference']) ? $config['dist']['reference'] : null);
$package->setDistSha1Checksum(isset($config['dist']['shasum']) ? $config['dist']['shasum'] : null);
if (isset($config['dist']['mirrors'])) {
$package->setDistMirrors($config['dist']['mirrors']);
}
}
foreach (Package\BasePackage::$supportedLinkTypes as $type => $opts) {

View File

@ -27,10 +27,12 @@ class Package extends BasePackage
protected $sourceType;
protected $sourceUrl;
protected $sourceReference;
protected $sourceMirrors;
protected $distType;
protected $distUrl;
protected $distReference;
protected $distSha1Checksum;
protected $distMirrors;
protected $version;
protected $prettyVersion;
protected $releaseDate;
@ -217,6 +219,30 @@ class Package extends BasePackage
return $this->sourceReference;
}
/**
* @param array|null $mirrors
*/
public function setSourceMirrors($mirrors)
{
$this->sourceMirrors = $mirrors;
}
/**
* {@inheritDoc}
*/
public function getSourceMirrors()
{
return $this->sourceMirrors;
}
/**
* {@inheritDoc}
*/
public function getSourceUrls()
{
return $this->getUrls($this->sourceUrl, $this->sourceMirrors, $this->sourceReference, $this->sourceType);
}
/**
* @param string $type
*/
@ -281,6 +307,30 @@ class Package extends BasePackage
return $this->distSha1Checksum;
}
/**
* @param array|null $mirrors
*/
public function setDistMirrors($mirrors)
{
$this->distMirrors = $mirrors;
}
/**
* {@inheritDoc}
*/
public function getDistMirrors()
{
return $this->distMirrors;
}
/**
* {@inheritDoc}
*/
public function getDistUrls()
{
return $this->getUrls($this->distUrl, $this->distMirrors, $this->distReference, $this->distType);
}
/**
* {@inheritDoc}
*/
@ -528,4 +578,22 @@ class Package extends BasePackage
$this->stability = VersionParser::parseStability($version);
$this->dev = $this->stability === 'dev';
}
protected function getUrls($url, $mirrors, $ref, $type)
{
$urls = array($url);
if ($mirrors) {
foreach ($mirrors as $mirror) {
$mirrorUrl = str_replace(
array('%package%', '%version%', '%reference%', '%type%'),
array($this->name, $this->version, $ref, $type),
$mirror['url']
);
$func = $mirror['preferred'] ? 'array_unshift' : 'array_push';
$func($urls, $mirrorUrl);
}
}
return $urls;
}
}

View File

@ -115,6 +115,13 @@ interface PackageInterface
*/
public function getSourceUrl();
/**
* Returns the repository urls of this package including mirrors, e.g. git://github.com/naderman/composer.git
*
* @return string
*/
public function getSourceUrls();
/**
* Returns the repository reference of this package, e.g. master, 1.0.0 or a commit hash for git
*
@ -122,6 +129,13 @@ interface PackageInterface
*/
public function getSourceReference();
/**
* Returns the source mirrors of this package
*
* @return array|null
*/
public function getSourceMirrors();
/**
* Returns the type of the distribution archive of this version, e.g. zip, tarball
*
@ -136,6 +150,13 @@ interface PackageInterface
*/
public function getDistUrl();
/**
* Returns the urls of the distribution archive of this version, including mirrors
*
* @return string
*/
public function getDistUrls();
/**
* Returns the reference of the distribution archive of this version, e.g. master, 1.0.0 or a commit hash for git
*
@ -150,6 +171,13 @@ interface PackageInterface
*/
public function getDistSha1Checksum();
/**
* Returns the dist mirrors of this package
*
* @return array|null
*/
public function getDistMirrors();
/**
* Returns the version of this package
*

View File

@ -50,6 +50,8 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
protected $rootAliases;
protected $allowSslDowngrade = false;
protected $eventDispatcher;
protected $sourceMirrors;
protected $distMirrors;
private $rawData;
private $minimalPackages;
private $degradedMode = false;
@ -434,6 +436,17 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
$this->searchUrl = $this->canonicalizeUrl($data['search']);
}
if (!empty($data['mirrors'])) {
foreach ($data['mirrors'] as $mirror) {
if (!empty($mirror['source-url'])) {
$this->sourceMirrors[] = array('url' => $mirror['source-url'], 'preferred' => !empty($mirror['preferred']));
}
if (!empty($mirror['dist-url'])) {
$this->distMirrors[] = array('url' => $mirror['dist-url'], 'preferred' => !empty($mirror['preferred']));
}
}
}
if ($this->allowSslDowngrade) {
$this->url = str_replace('https://', 'http://', $this->url);
}
@ -548,6 +561,8 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
}
$package = $this->loader->load($data, 'Composer\Package\CompletePackage');
$package->setSourceMirrors($this->sourceMirrors);
$package->setDistMirrors($this->distMirrors);
$this->configurePackageTransportOptions($package);
return $package;

View File

@ -48,6 +48,10 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getDistUrl')
->will($this->returnValue('url'))
;
$packageMock->expects($this->once())
->method('getDistUrls')
->will($this->returnValue(array('url')))
;
$path = tempnam(sys_get_temp_dir(), 'c');
@ -87,7 +91,11 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getDistUrl')
->will($this->returnValue('http://example.com/script.js'))
->will($this->returnValue($distUrl = 'http://example.com/script.js'))
;
$packageMock->expects($this->once())
->method('getDistUrls')
->will($this->returnValue(array($distUrl)))
;
$packageMock->expects($this->atLeastOnce())
->method('getTransportOptions')
@ -163,7 +171,7 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getDistUrl')
->will($this->returnValue('http://example.com/script.js'))
->will($this->returnValue($distUrl = 'http://example.com/script.js'))
;
$packageMock->expects($this->atLeastOnce())
->method('getTransportOptions')
@ -173,6 +181,10 @@ class FileDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getDistSha1Checksum')
->will($this->returnValue('invalid'))
;
$packageMock->expects($this->once())
->method('getDistUrls')
->will($this->returnValue(array($distUrl)))
;
$filesystem = $this->getMock('Composer\Util\Filesystem');
do {

View File

@ -51,8 +51,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('1234567890123456789012345678901234567890'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://example.com/composer/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://example.com/composer/composer')));
$packageMock->expects($this->any())
->method('getPrettyVersion')
->will($this->returnValue('dev-master'));
@ -90,8 +90,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/composer/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://github.com/composer/composer')));
$packageMock->expects($this->any())
->method('getPrettyVersion')
->will($this->returnValue('1.0.0'));
@ -147,8 +147,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/composer/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://github.com/composer/composer')));
$packageMock->expects($this->any())
->method('getPrettyVersion')
->will($this->returnValue('1.0.0'));
@ -188,8 +188,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://example.com/composer/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://example.com/composer/composer')));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->at(0))
->method('execute')
@ -227,8 +227,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/composer/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://github.com/composer/composer')));
$packageMock->expects($this->any())
->method('getPrettyVersion')
->will($this->returnValue('1.0.0'));
@ -273,8 +273,8 @@ class GitDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/composer/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://github.com/composer/composer')));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$processExecutor->expects($this->at(0))
->method('execute')

View File

@ -48,8 +48,8 @@ class HgDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->once())
->method('getSourceUrl')
->will($this->returnValue('https://mercurial.dev/l3l0/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://mercurial.dev/l3l0/composer')));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$expectedGitCommand = $this->getCmd('hg clone \'https://mercurial.dev/l3l0/composer\' \'composerPath\'');
@ -93,8 +93,8 @@ class HgDownloaderTest extends \PHPUnit_Framework_TestCase
->method('getSourceReference')
->will($this->returnValue('ref'));
$packageMock->expects($this->any())
->method('getSourceUrl')
->will($this->returnValue('https://github.com/l3l0/composer'));
->method('getSourceUrls')
->will($this->returnValue(array('https://github.com/l3l0/composer')));
$processExecutor = $this->getMock('Composer\Util\ProcessExecutor');
$expectedHgCommand = $this->getCmd("hg st");

View File

@ -28,7 +28,11 @@ class ZipDownloaderTest extends \PHPUnit_Framework_TestCase
$packageMock = $this->getMock('Composer\Package\PackageInterface');
$packageMock->expects($this->any())
->method('getDistUrl')
->will($this->returnValue('file://'.__FILE__))
->will($this->returnValue($distUrl = 'file://'.__FILE__))
;
$packageMock->expects($this->any())
->method('getDistUrls')
->will($this->returnValue(array($distUrl)))
;
$packageMock->expects($this->atLeastOnce())
->method('getTransportOptions')