1
0
Fork 0

Refactor downloaders, git installs actually work well now

pull/29/head
Jordi Boggiano 2011-09-29 00:48:17 +02:00
parent c863c31881
commit 00a4fe2a14
8 changed files with 270 additions and 243 deletions

View File

@ -93,19 +93,19 @@ class DownloadManager
if (!($preferSource && $sourceType) && $distType) {
$downloader = $this->getDownloader($distType);
$downloader->download(
$package, $targetDir,
$package->getDistUrl(), $package->getDistSha1Checksum(),
$preferSource
);
$package->setInstallationSource('dist');
} elseif ($sourceType) {
$downloader = $this->getDownloader($sourceType);
$downloader->download($package, $targetDir, $package->getSourceUrl(), $preferSource);
$package->setInstallationSource('source');
} else {
throw new \InvalidArgumentException('Package should have dist or source specified');
$downloader->distDownload($package, $targetDir);
return 'dist';
}
if ($sourceType) {
$downloader = $this->getDownloader($sourceType);
$package->setInstallationSource('source');
$downloader->sourceDownload($package, $targetDir);
return 'source';
}
throw new \InvalidArgumentException('Package should have dist or source specified');
}
/**
@ -124,6 +124,7 @@ class DownloadManager
'Package '.$initial.' was not been installed propertly and can not be updated'
);
}
$useSource = 'source' === $installationType;
if (!$useSource) {
@ -137,9 +138,11 @@ class DownloadManager
$downloader = $this->getDownloader($initialType);
if ($initialType === $targetType) {
$downloader->update($initial, $target, $targetDir, $useSource);
$target->setInstallationSource($installationType);
$method = $useSource ? 'sourceUpdate' : 'distUpdate';
$downloader->$method($initial, $target, $targetDir);
} else {
$downloader->remove($initial, $targetDir, $useSource);
$downloader->remove($initial, $targetDir);
$this->download($target, $targetDir, $useSource);
}
}
@ -157,15 +160,9 @@ class DownloadManager
'Package '.$package.' was not been installed propertly and can not be removed'
);
}
$useSource = 'source' === $installationType;
// get proper downloader
if (!$useSource) {
$downloader = $this->getDownloader($package->getDistType());
} else {
$downloader = $this->getDownloader($package->getSourceType());
}
$downloader->remove($package, $targetDir, $useSource);
$downloaderType = $useSource ? $package->getSourceType() : $package->getDistType();
$this->getDownloader($downloaderType)->remove($package, $targetDir);
}
}

View File

@ -18,19 +18,25 @@ use Composer\Package\PackageInterface;
* Downloader interface.
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
interface DownloaderInterface
{
/**
* Downloads specific package into specific folder.
* Downloads specific package into specific folder from dist.
*
* @param PackageInterface $package package instance
* @param string $path download path
* @param string $url download url
* @param string $checksum package checksum (for dists)
* @param Boolean $useSource download as source
*/
function download(PackageInterface $package, $path, $url, $checksum = null, $useSource = false);
function distDownload(PackageInterface $package, $path);
/**
* Downloads specific package into specific folder from source.
*
* @param PackageInterface $package package instance
* @param string $path download path
*/
function sourceDownload(PackageInterface $package, $path);
/**
* Updates specific package in specific folder from initial to target version.
@ -38,16 +44,23 @@ interface DownloaderInterface
* @param PackageInterface $initial initial package
* @param PackageInterface $target updated package
* @param string $path download path
* @param Boolean $useSource download as source
*/
function update(PackageInterface $initial, PackageInterface $target, $path, $useSource = false);
function distUpdate(PackageInterface $initial, PackageInterface $target, $path);
/**
* Updates specific package in specific folder from initial to target version.
*
* @param PackageInterface $initial initial package
* @param PackageInterface $target updated package
* @param string $path download path
*/
function sourceUpdate(PackageInterface $initial, PackageInterface $target, $path);
/**
* Removes specific package from specific folder.
*
* @param PackageInterface $package package instance
* @param string $path download path
* @param Boolean $useSource download as source
*/
function remove(PackageInterface $package, $path, $useSource = false);
function remove(PackageInterface $package, $path);
}

View File

@ -0,0 +1,122 @@
<?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\Downloader;
use Composer\Package\PackageInterface;
/**
* Base downloader for file packages
*
* @author Kirill chEbba Chebunin <iam@chebba.org>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
abstract class FileDownloader implements DownloaderInterface
{
/**
* {@inheritDoc}
*/
public function distDownload(PackageInterface $package, $path)
{
$this->download($package->getDistUrl(), $path, $package->getDistSha1Checksum());
}
/**
* {@inheritDoc}
*/
public function sourceDownload(PackageInterface $package, $path)
{
$this->download($package->getSourceUrl(), $path);
}
/**
* {@inheritDoc}
*/
public function distUpdate(PackageInterface $initial, PackageInterface $target, $path)
{
$fs = new Util\Filesystem();
$fs->remove($path);
$this->download($target->getDistUrl(), $path, $target->getDistSha1Checksum());
}
/**
* {@inheritDoc}
*/
public function sourceUpdate(PackageInterface $initial, PackageInterface $target, $path)
{
$fs = new Util\Filesystem();
$fs->remove($path);
$this->download($target->getSourceUrl(), $path);
}
/**
* {@inheritDoc}
*/
public function remove(PackageInterface $package, $path)
{
$fs = new Util\Filesystem();
$fs->remove($path);
}
public function download($url, $path, $checksum = null)
{
if (!is_dir($path)) {
if (file_exists($path)) {
throw new \UnexpectedValueException($path.' exists and is not a directory');
}
if (!mkdir($path, 0777, true)) {
throw new \UnexpectedValueException($path.' does not exist and could not be created');
}
}
$fileName = rtrim($path.'/'.md5(time().rand()).'.'.pathinfo($url, PATHINFO_EXTENSION), '.');
echo 'Downloading '.$url.' to '.$fileName.PHP_EOL;
copy($url, $fileName);
if (!file_exists($fileName)) {
throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'
.' directory is writable and you have internet connectivity');
}
if ($checksum && hash_file('sha1', $fileName) !== $checksum) {
throw new \UnexpectedValueException('The checksum verification of the archive failed (downloaded from '.$url.')');
}
echo 'Unpacking archive'.PHP_EOL;
$this->extract($fileName, $path);
echo 'Cleaning up'.PHP_EOL;
unlink($fileName);
// If we have only a one dir inside it suppose to be a package itself
$contentDir = glob($path . '/*');
if (1 === count($contentDir)) {
$contentDir = $contentDir[0];
foreach (array_merge(glob($contentDir . '/.*'), glob($contentDir . '/*')) as $file) {
if (trim(basename($file), '.')) {
rename($file, $path . '/' . basename($file));
}
}
rmdir($contentDir);
}
}
/**
* Extract file to directory
*
* @param string $file Extracted file
* @param string $path Directory
*/
protected abstract function extract($file, $path);
}

View File

@ -22,30 +22,63 @@ class GitDownloader implements DownloaderInterface
/**
* {@inheritDoc}
*/
public function download(PackageInterface $package, $path, $url, $checksum = null, $useSource = false)
public function distDownload(PackageInterface $package, $path)
{
system('git clone '.escapeshellarg($url).' -b master '.escapeshellarg($path));
// TODO non-source installs:
// system('git archive --format=tar --prefix='.escapeshellarg($package->getName()).' --remote='.escapeshellarg($url).' master | tar -xf -');
$url = escapeshellarg($package->getDistUrl());
$ref = escapeshellarg($package->getDistReference());
system(sprintf('git archive --format=tar --prefix=%s --remote=%s %s | tar -xf -', $path, $url, $ref));
}
/**
* {@inheritDoc}
*/
public function update(PackageInterface $initial, PackageInterface $target, $path, $useSource = false)
public function sourceDownload(PackageInterface $package, $path)
{
$cwd = getcwd();
chdir($path);
system('git pull');
chdir($cwd);
if (!$package->getSourceReference()) {
throw new \InvalidArgumentException('The given package is missing reference information');
}
$url = escapeshellarg($package->getSourceUrl());
$ref = escapeshellarg($package->getSourceReference());
system(sprintf('git clone %s %s && cd %2$s && git reset --hard %s', $url, $path, $ref));
}
/**
* {@inheritDoc}
*/
public function remove(PackageInterface $package, $path, $useSource = false)
public function distUpdate(PackageInterface $initial, PackageInterface $target, $path)
{
echo 'rm -rf '.$path; // TODO
throw new \Exception('Updating dist installs from git is not implemented yet');
}
/**
* {@inheritDoc}
*/
public function sourceUpdate(PackageInterface $initial, PackageInterface $target, $path)
{
if (!$target->getSourceReference()) {
throw new \InvalidArgumentException('The given package is missing reference information');
}
$this->enforceCleanDirectory($path);
system(sprintf('cd %s && git fetch && git reset --hard %s', $path, $target->getSourceReference()));
}
/**
* {@inheritDoc}
*/
public function remove(PackageInterface $package, $path)
{
$this->enforceCleanDirectory($path);
$fs = new Util\Filesystem();
$fs->remove($path);
}
private function enforceCleanDirectory($path)
{
exec(sprintf('cd %s && git status -s', $path), $output);
if (implode('', $output)) {
throw new \RuntimeException('Source directory has uncommitted changes');
}
}
}

View File

@ -15,79 +15,14 @@ namespace Composer\Downloader;
use Composer\Package\PackageInterface;
/**
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class PearDownloader implements DownloaderInterface
class PearDownloader extends FileDownloader
{
/**
* {@inheritDoc}
*/
public function download(PackageInterface $package, $path, $url, $checksum = null, $useSource = false)
protected function extract($file, $path)
{
$this->downloadTo($package, $url, $path, $checksum);
}
/**
* {@inheritDoc}
*/
public function update(PackageInterface $initial, PackageInterface $target, $path, $useSource = false)
{
// TODO rm old dir
$this->downloadTo($package, $url, $path, $checksum);
}
/**
* {@inheritDoc}
*/
public function remove(PackageInterface $package, $path, $useSource = false)
{
echo 'rm -rf '.$path; // TODO
}
private function downloadTo($package, $url, $targetPath, $checksum = null)
{
if (!is_dir($targetPath)) {
if (file_exists($targetPath)) {
throw new \UnexpectedValueException($targetPath.' exists and is not a directory.');
}
if (!mkdir($targetPath, 0777, true)) {
throw new \UnexpectedValueException($targetPath.' does not exist and could not be created.');
}
}
$cwd = getcwd();
chdir($targetPath);
$tarName = basename($url);
echo 'Downloading '.$url.' to '.$targetPath.'/'.$tarName.PHP_EOL;
copy($url, './'.$tarName);
if (!file_exists($tarName)) {
throw new \UnexpectedValueException($package->getName().' could not be saved into '.$tarName.', make sure the'
.' directory is writable and you have internet connectivity.');
}
if ($checksum && hash_file('sha1', './'.$tarName) !== $checksum) {
throw new \UnexpectedValueException('The checksum verification failed for the '.$package->getName().' archive (downloaded from '.$url.'). Installation aborted.');
}
echo 'Unpacking archive'.PHP_EOL;
exec('tar -xzf "'.escapeshellarg($tarName).'"');
echo 'Cleaning up'.PHP_EOL;
unlink('./'.$tarName);
@unlink('./package.sig');
@unlink('./package.xml');
if (list($dir) = glob('./'.$package->getName().'-*', GLOB_ONLYDIR)) {
foreach (array_merge(glob($dir.'/.*'), glob($dir.'/*')) as $file) {
if (trim(basename($file), '.')) {
rename($file, './'.basename($file));
}
}
rmdir($dir);
}
chdir($cwd);
system(sprintf('tar -zxf %s', escapeshellarg($file)));
@unlink($path . '/package.sig');
@unlink($path . '/package.xml');
}
}

View File

@ -0,0 +1,28 @@
<?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\Downloader\Util;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class Filesystem
{
public function remove($directory)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
system(sprintf('rmdir /S /Q %s', escapeshellarg(realpath($directory))));
} else {
system(sprintf('rm -rf %s', escapeshellarg($directory)));
}
}
}

View File

@ -17,83 +17,21 @@ use Composer\Package\PackageInterface;
/**
* @author Jordi Boggiano <j.boggiano@seld.be>
*/
class ZipDownloader implements DownloaderInterface
class ZipDownloader extends FileDownloader
{
/**
* {@inheritDoc}
*/
public function download(PackageInterface $package, $path, $url, $checksum = null, $useSource = false)
{
$this->downloadTo($url, $path, $checksum);
}
/**
* {@inheritDoc}
*/
public function update(PackageInterface $initial, PackageInterface $target, $path, $useSource = false)
{
// TODO rm old dir
$this->downloadTo($url, $path, $checksum);
}
/**
* {@inheritDoc}
*/
public function remove(PackageInterface $package, $path, $useSource = false)
{
echo 'rm -rf '.$path; // TODO
}
private function downloadTo($url, $targetPath, $checksum = null)
protected function extract($file, $path)
{
if (!class_exists('ZipArchive')) {
throw new \UnexpectedValueException('You need the zip extension enabled to use the ZipDownloader');
}
if (!is_dir($targetPath)) {
if (file_exists($targetPath)) {
throw new \UnexpectedValueException($targetPath.' exists and is not a directory.');
}
if (!mkdir($targetPath, 0777, true)) {
throw new \UnexpectedValueException($targetPath.' does not exist and could not be created.');
}
}
$zipName = $targetPath.'/'.basename($url, '.zip').'.zip';
echo 'Downloading '.$url.' to '.$zipName.PHP_EOL;
copy($url, $zipName);
if (!file_exists($zipName)) {
throw new \UnexpectedValueException($targetPath.' could not be saved into '.$zipName.', make sure the'
.' directory is writable and you have internet connectivity.');
}
if ($checksum && hash_file('sha1', $zipName) !== $checksum) {
throw new \UnexpectedValueException('The checksum verification failed for the '.basename($path).' archive (downloaded from '.$url.'). Installation aborted.');
}
$zipArchive = new \ZipArchive();
echo 'Unpacking archive'.PHP_EOL;
if (true === ($retval = $zipArchive->open($zipName))) {
$zipArchive->extractTo($targetPath);
if (true !== ($retval = $zipArchive->open($file))) {
throw new \UnexpectedValueException($file.' is not a valid zip archive, got error code '.$retval);
}
$zipArchive->extractTo($path);
$zipArchive->close();
echo 'Cleaning up'.PHP_EOL;
unlink($zipName);
if (false !== strpos($url, '//github.com/')) {
$contentDir = glob($targetPath.'/*');
if (1 === count($contentDir)) {
$contentDir = $contentDir[0];
foreach (array_merge(glob($contentDir.'/.*'), glob($contentDir.'/*')) as $file) {
if (trim(basename($file), '.')) {
rename($file, $targetPath.'/'.basename($file));
}
}
rmdir($contentDir);
}
}
} else {
throw new \UnexpectedValueException($zipName.' is not a valid zip archive, got error code '.$retval);
}
}
}

View File

@ -40,15 +40,6 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getDistType')
->will($this->returnValue('pear'));
$package
->expects($this->once())
->method('getDistUrl')
->will($this->returnValue('dist_url'));
$package
->expects($this->once())
->method('getDistSha1Checksum')
->will($this->returnValue('sha1'));
$package
->expects($this->once())
->method('setInstallationSource')
@ -57,8 +48,8 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$pearDownloader = $this->createDownloaderMock();
$pearDownloader
->expects($this->once())
->method('download')
->with($package, 'target_dir', 'dist_url', 'sha1', false);
->method('distDownload')
->with($package, 'target_dir');
$manager = new DownloadManager();
$manager->setDownloader('pear', $pearDownloader);
@ -96,15 +87,6 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getDistType')
->will($this->returnValue('pear'));
$package
->expects($this->once())
->method('getDistUrl')
->will($this->returnValue('dist_url'));
$package
->expects($this->once())
->method('getDistSha1Checksum')
->will($this->returnValue('sha1'));
$package
->expects($this->once())
->method('setInstallationSource')
@ -113,8 +95,8 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$pearDownloader = $this->createDownloaderMock();
$pearDownloader
->expects($this->once())
->method('download')
->with($package, 'target_dir', 'dist_url', 'sha1', false);
->method('distDownload')
->with($package, 'target_dir');
$manager = new DownloadManager();
$manager->setDownloader('pear', $pearDownloader);
@ -133,12 +115,6 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->expects($this->once())
->method('getDistType')
->will($this->returnValue(null));
$package
->expects($this->once())
->method('getSourceUrl')
->will($this->returnValue('source_url'));
$package
->expects($this->once())
->method('setInstallationSource')
@ -147,8 +123,8 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$gitDownloader = $this->createDownloaderMock();
$gitDownloader
->expects($this->once())
->method('download')
->with($package, 'vendor/pkg', 'source_url', false);
->method('sourceDownload')
->with($package, 'vendor/pkg');
$manager = new DownloadManager();
$manager->setDownloader('git', $gitDownloader);
@ -168,11 +144,6 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getDistType')
->will($this->returnValue('pear'));
$package
->expects($this->once())
->method('getSourceUrl')
->will($this->returnValue('source_url'));
$package
->expects($this->once())
->method('setInstallationSource')
@ -181,8 +152,8 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$gitDownloader = $this->createDownloaderMock();
$gitDownloader
->expects($this->once())
->method('download')
->with($package, 'vendor/pkg', 'source_url', true);
->method('sourceDownload')
->with($package, 'vendor/pkg');
$manager = new DownloadManager();
$manager->setDownloader('git', $gitDownloader);
@ -203,15 +174,6 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getDistType')
->will($this->returnValue('pear'));
$package
->expects($this->once())
->method('getDistUrl')
->will($this->returnValue('dist_url'));
$package
->expects($this->once())
->method('getDistSha1Checksum')
->will($this->returnValue('sha1'));
$package
->expects($this->once())
->method('setInstallationSource')
@ -220,8 +182,8 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$pearDownloader = $this->createDownloaderMock();
$pearDownloader
->expects($this->once())
->method('download')
->with($package, 'target_dir', 'dist_url', 'sha1', true);
->method('distDownload')
->with($package, 'target_dir');
$manager = new DownloadManager();
$manager->setDownloader('pear', $pearDownloader);
@ -242,11 +204,6 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->method('getDistType')
->will($this->returnValue(null));
$package
->expects($this->once())
->method('getSourceUrl')
->will($this->returnValue('source_url'));
$package
->expects($this->once())
->method('setInstallationSource')
@ -255,8 +212,8 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$gitDownloader = $this->createDownloaderMock();
$gitDownloader
->expects($this->once())
->method('download')
->with($package, 'vendor/pkg', 'source_url', true);
->method('sourceDownload')
->with($package, 'vendor/pkg');
$manager = new DownloadManager();
$manager->setDownloader('git', $gitDownloader);
@ -301,12 +258,16 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
->expects($this->once())
->method('getDistType')
->will($this->returnValue('pear'));
$target
->expects($this->once())
->method('setInstallationSource')
->with('dist');
$pearDownloader = $this->createDownloaderMock();
$pearDownloader
->expects($this->once())
->method('update')
->with($initial, $target, 'vendor/bundles/FOS/UserBundle', false);
->method('distUpdate')
->with($initial, $target, 'vendor/bundles/FOS/UserBundle');
$manager = new DownloadManager();
$manager->setDownloader('pear', $pearDownloader);
@ -336,7 +297,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$pearDownloader
->expects($this->once())
->method('remove')
->with($initial, 'vendor/bundles/FOS/UserBundle', false);
->with($initial, 'vendor/bundles/FOS/UserBundle');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setMethods(array('download'))
@ -371,8 +332,8 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$svnDownloader = $this->createDownloaderMock();
$svnDownloader
->expects($this->once())
->method('update')
->with($initial, $target, 'vendor/pkg', true);
->method('sourceUpdate')
->with($initial, $target, 'vendor/pkg');
$manager = new DownloadManager();
$manager->setDownloader('svn', $svnDownloader);
@ -402,7 +363,7 @@ class DownloadManagerTest extends \PHPUnit_Framework_TestCase
$svnDownloader
->expects($this->once())
->method('remove')
->with($initial, 'vendor/pkg', true);
->with($initial, 'vendor/pkg');
$manager = $this->getMockBuilder('Composer\Downloader\DownloadManager')
->setMethods(array('download'))