From 2acb0330570f64151174df8e954b4b2bb113bae3 Mon Sep 17 00:00:00 2001 From: Till Klampaeckel Date: Fri, 24 Aug 2012 10:57:38 +0200 Subject: [PATCH 01/43] Initial feature-dist * extends BaseDumper, implements interface * put $keys into BaseDumper * WIP WIP WIP WIP * BaseDumper for utilities * interface to enforce 'dump()' * feature: * supports git * supports zip output * basic test to cover feature * add @todo for later * add vendor namespace to package name * add extension to getFilename() so we don't need to switch in there (HT, @naderman) * add extension (obviously 'zip' in ZipDumper) * create archive in destination dir (provided by __construct()) * condensed ZipDumper * moved code to BaseDumper (hopefully easier re-use) * use ProcessExecutor from BaseDumper * fix assignments in __construct() * allow injection of ProcessExecutor * fix parameters * fix regex * write in 'system temp dir' * update test case (oh look, a duplicate regex) * move working directory related to BaseDumper * add quotes * place holder for these methods * use PharData to create zip/tar when necessary * add placeholder calls * add call to package() using PharData * finish downloadHg(), downloadSvn() * put to use * make BaseDumper abstract (to force extension) * make BaseDumper implement Interface (makes for less code in the implementation) new functionality for dumping as .tar.gz tar instead of tar.gz, new abstract dumpertest class creates a local git repo instead of fetching a remote one more oo-ish version of it no constructor * refactor tests to be less linux-specific (used Composer\Util to wrap calls) * make filename only the version * various cs fixes (idention, tabs/spaces, doc blocks, etc.) * fixed a typo'd exception name * refactored downloading: * removed download*() methods * added dep on Composer\Factory to setup a DownloadManager instance * update CS with feedback from @stof * ArrayDumper doesn't extend BaseDumper anymore (hence no conflict on the interface) * move keys from BaseDumper back to ArrayDumper * interface now declares dump() to always return void Apparently I had to update the lock. CS fixes (tabs for spaces) Bugfix: sprintf() was missing. Fix docblock for @stof. ;) Pull in lock from master. Update lock one more time (hope it still merges). whitespace Revert ArrayDumper static keys --- src/Composer/Package/Dumper/BaseDumper.php | 166 ++++++++++++++++++ .../Package/Dumper/DumperInterface.php | 29 +++ src/Composer/Package/Dumper/TarDumper.php | 57 ++++++ src/Composer/Package/Dumper/ZipDumper.php | 56 ++++++ .../Test/Package/Dumper/DumperTest.php | 97 ++++++++++ .../Test/Package/Dumper/TarDumperTest.php | 43 +++++ .../Test/Package/Dumper/ZipDumperTest.php | 43 +++++ 7 files changed, 491 insertions(+) create mode 100644 src/Composer/Package/Dumper/BaseDumper.php create mode 100644 src/Composer/Package/Dumper/DumperInterface.php create mode 100644 src/Composer/Package/Dumper/TarDumper.php create mode 100644 src/Composer/Package/Dumper/ZipDumper.php create mode 100644 tests/Composer/Test/Package/Dumper/DumperTest.php create mode 100644 tests/Composer/Test/Package/Dumper/TarDumperTest.php create mode 100644 tests/Composer/Test/Package/Dumper/ZipDumperTest.php diff --git a/src/Composer/Package/Dumper/BaseDumper.php b/src/Composer/Package/Dumper/BaseDumper.php new file mode 100644 index 000000000..093e5a3f9 --- /dev/null +++ b/src/Composer/Package/Dumper/BaseDumper.php @@ -0,0 +1,166 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Dumper; + +use Composer\Package\PackageInterface; +use Composer\Util\ProcessExecutor; +use Composer\Downloader\GitDownloader; +use Composer\Downloader\HgDownloader; +use Composer\Downloader\SvnDownloader; +use Composer\IO\NullIO; +use Composer\Factory; + +/** + * @author Till Klampaeckel + */ +abstract class BaseDumper implements DumperInterface +{ + /** + * Format: zip or tar. + * @var string + */ + protected $format = ''; + + /** + * Path to where to dump the export to. + * @var mixed|null + */ + protected $path; + + /** + * @var ProcessExecutor + */ + protected $process; + + /** + * Working directory. + * @var string + */ + protected $temp; + + /** + * @param mixed $path + * @param ProcessExecutor|null $process + * + * @throws \InvalidArgumentException + */ + public function __construct($path = null, ProcessExecutor $process = null) + { + if (!empty($path)) { + if (!is_writable($path)) { + throw new \InvalidArgumentException("Not authorized to write to '{$path}'"); + } + $this->path = $path; + } + $this->process = $process ?: new ProcessExecutor(); + $this->temp = sys_get_temp_dir(); + } + + /** + * @return \Composer\Downloader\DownloadManager + */ + public function getDownloadManager() + { + $factory = new Factory; + $dm = $factory->createDownloadManager(new NullIO()); + return $dm; + } + + /** + * @param PackageInterface $package + * @param string $extension + * + * @return string + * @throws \InvalidArgumentException When unknown 'format' is encountered. + */ + public function getFilename(PackageInterface $package, $extension) + { + $name = $package->getPrettyVersion(); + $fileName = sprintf('%s.%s', $name, $extension); + return $fileName; + } + + /** + * @param PackageInterface $package + * + * @return string + * @throws \RuntimeException + */ + protected function getAndEnsureWorkDirectory(PackageInterface $package) + { + $workDir = sprintf('%s/%s/%s', $this->temp, $this->format, $package->getName()); + if (!file_exists($workDir)) { + mkdir($workDir, 0777, true); + } + if (!file_exists($workDir)) { + throw new \RuntimeException("Could not find '{$workDir}' directory."); + } + return $workDir; + } + + /** + * Package the given directory into an archive. + * + * The format is most likely \Phar::TAR or \Phar::ZIP. + * + * @param string $filename + * @param string $workDir + * @param int $format + * + * @throws \RuntimeException + */ + protected function package($filename, $workDir, $format) + { + try { + $phar = new \PharData($filename, null, null, $format); + $phar->buildFromDirectory($workDir); + } catch (\UnexpectedValueException $e) { + $message = "Original PHAR exception: " . (string) $e; + $message .= PHP_EOL . PHP_EOL; + $message .= sprintf("Could not create archive '%s' from '%s'.", $filename, $workDir); + throw new \RuntimeException($message); + } + } + + /** + * @param string $fileName + * @param string $sourceRef + * @param string $workDir + */ + protected function packageGit($fileName, $sourceRef, $workDir) + { + $command = sprintf( + 'git archive --format %s --output %s %s', + $this->format, + escapeshellarg(sprintf('%s/%s', $this->path, $fileName)), + $sourceRef + ); + $this->process->execute($command, $output, $workDir); + } + + /** + * @param string $fileName + * @param string $sourceRef + * @param string $workDir + */ + protected function packageHg($fileName, $sourceRef, $workDir) + { + $format = ($this->format == 'tarball')?'tar':$this->format; + $command = sprintf( + 'hg archive --rev %s --type %s %s', + $sourceRef, + $format, + escapeshellarg(sprintf('%s/%s', $this->path, $fileName)) + ); + $this->process->execute($command, $output, $workDir); + } +} diff --git a/src/Composer/Package/Dumper/DumperInterface.php b/src/Composer/Package/Dumper/DumperInterface.php new file mode 100644 index 000000000..03a0aa5f9 --- /dev/null +++ b/src/Composer/Package/Dumper/DumperInterface.php @@ -0,0 +1,29 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace Composer\Package\Dumper; + +use Composer\Package\PackageInterface; + +/** + * @author Till Klampaeckel + */ +interface DumperInterface +{ + /** + * Return value depends on implementation - e.g. generating a tar or zip the + * method currently returns void, the ArrayDumper returns an array. + * + * @param PackageInterface $package + * + * @return void + */ + public function dump(PackageInterface $package); +} diff --git a/src/Composer/Package/Dumper/TarDumper.php b/src/Composer/Package/Dumper/TarDumper.php new file mode 100644 index 000000000..4de76c7ab --- /dev/null +++ b/src/Composer/Package/Dumper/TarDumper.php @@ -0,0 +1,57 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Dumper; + +use Composer\Package\Dumper\BaseDumper; +use Composer\Package\Dumper\DumperInterface; +use Composer\Package\PackageInterface; +use Composer\Util\ProcessExecutor; + +/** + * @author Ulf Härnhammar + */ +class TarDumper extends BaseDumper +{ + protected $format = 'tar'; + + /** + * @param PackageInterface $package + * @throws \InvalidArgumentException + */ + public function dump(PackageInterface $package) + { + $workDir = $this->getAndEnsureWorkDirectory($package); + + $fileName = $this->getFilename($package, 'tar'); + $sourceType = $package->getSourceType(); + $sourceRef = $package->getSourceReference(); + + $dm = $this->getDownloadManager(); + $dm->download($package, $workDir, true); + + switch ($sourceType) { + case 'git': + $this->packageGit($fileName, $sourceRef, $workDir); + break; + case 'hg': + $this->packageHg($fileName, $sourceRef, $workDir); + break; + case 'svn': + $dir = $workDir . (substr($sourceRef, 0, 1) !== '/')?'/':'' . $sourceRef; + $this->package($fileName, $dir, \Phar::TAR); + break; + default: + throw new \InvalidArgumentException( + "Unable to handle repositories of type '{$sourceType}'."); + } + } +} diff --git a/src/Composer/Package/Dumper/ZipDumper.php b/src/Composer/Package/Dumper/ZipDumper.php new file mode 100644 index 000000000..6ab55839d --- /dev/null +++ b/src/Composer/Package/Dumper/ZipDumper.php @@ -0,0 +1,56 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Dumper; + +use Composer\Package\Dumper\BaseDumper; +use Composer\Package\Dumper\DumperInterface; +use Composer\Package\PackageInterface; +use Composer\Util\ProcessExecutor; + +/** + * @author Till Klampaeckel + */ +class ZipDumper extends BaseDumper +{ + protected $format = 'zip'; + + /** + * @param PackageInterface $package + * @throws \InvalidArgumentException + */ + public function dump(PackageInterface $package) + { + $workDir = $this->getAndEnsureWorkDirectory($package); + + $fileName = $this->getFilename($package, 'zip'); + $sourceType = $package->getSourceType(); + $sourceRef = $package->getSourceReference(); + + $dm = $this->getDownloadManager(); + $dm->download($package, $workDir, true); + + switch ($sourceType) { + case 'git': + $this->packageGit($fileName, $sourceRef, $workDir); + break; + case 'hg': + $this->packageHg($fileName, $sourceRef, $workDir); + break; + case 'svn': + $dir = $workDir . (substr($sourceRef, 0, 1) !== '/')?'/':'' . $sourceRef; + $this->package($fileName, $dir, \Phar::ZIP); + break; + default: + throw new \InvalidArgumentException("Unable to handle repositories of type '{$sourceType}'."); + } + } +} \ No newline at end of file diff --git a/tests/Composer/Test/Package/Dumper/DumperTest.php b/tests/Composer/Test/Package/Dumper/DumperTest.php new file mode 100644 index 000000000..6db54ea69 --- /dev/null +++ b/tests/Composer/Test/Package/Dumper/DumperTest.php @@ -0,0 +1,97 @@ + + * 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\Dumper; + +use Composer\Package\MemoryPackage; +use Composer\Util\Filesystem; +use Composer\Util\ProcessExecutor; + +abstract class DumperTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var \Composer\Util\Filesystem + */ + protected $fs; + + /** + * @var \Composer\Util\ProcessExecutor + */ + protected $process; + + /** + * @var string + */ + protected $testdir = ''; + + public function setUp() + { + $this->fs = new Filesystem; + $this->process = new ProcessExecutor; + $this->testdir = sys_get_temp_dir() . '/composer_dumpertest_git_repository' . mt_rand(); + } + + protected function getTestDir() + { + return $this->testdir; + } + + /** + * Create local git repository to run tests against! + */ + protected function setupGitRepo() + { + $td = $this->getTestDir(); + + $this->fs->removeDirectory($td); + $this->fs->ensureDirectoryExists($td); + + $currentWorkDir = getcwd(); + chdir($td); + + $result = $this->process->execute("git init -q"); + if ($result > 0) { + throw new \RuntimeException( + "Could not init: " . $this->process->getErrorOutput()); + } + $result = file_put_contents('b', 'a'); + if (false === $result) { + throw new \RuntimeException("Could not save file."); + } + $result = $this->process->execute("git add b && git commit -m 'commit b' -q"); + if ($result > 0) { + throw new \RuntimeException( + "Could not init: " . $this->process->getErrorOutput()); + } + chdir($currentWorkDir); + } + + protected function removeGitRepo() + { + $td = $this->getTestDir(); + $this->fs->removeDirectory($td); + } + + protected function setupPackage() + { + $td = $this->getTestDir(); + $package = new MemoryPackage('dumpertest/dumpertest', 'master', 'master'); + $package->setSourceUrl("file://$td"); + $package->setSourceReference('master'); + $package->setSourceType('git'); + return $package; + } + + protected function getPackageFileName(MemoryPackage $package) + { + return $package->getVersion(); + } +} diff --git a/tests/Composer/Test/Package/Dumper/TarDumperTest.php b/tests/Composer/Test/Package/Dumper/TarDumperTest.php new file mode 100644 index 000000000..cd9917732 --- /dev/null +++ b/tests/Composer/Test/Package/Dumper/TarDumperTest.php @@ -0,0 +1,43 @@ + + * 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\Dumper; + +use Composer\Package\Dumper\TarDumper; + +class TarDumperTest extends DumperTest +{ + public function testThis() + { + $this->setupGitRepo(); + $package = $this->setupPackage(); + $name = $this->getPackageFileName($package); + + $temp = sys_get_temp_dir(); + $tar = new TarDumper($temp); + $tar->dump($package); + + $dist = sprintf('%s/%s.tar', + $temp, $name + ); + $this->assertFileExists($dist); + unlink($dist); + $this->removeGitRepo(); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testException() + { + new TarDumper("/totally-random-" . time()); + } +} diff --git a/tests/Composer/Test/Package/Dumper/ZipDumperTest.php b/tests/Composer/Test/Package/Dumper/ZipDumperTest.php new file mode 100644 index 000000000..2fe71eb69 --- /dev/null +++ b/tests/Composer/Test/Package/Dumper/ZipDumperTest.php @@ -0,0 +1,43 @@ + + * 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\Dumper; + +use Composer\Package\Dumper\ZipDumper; + +class ZipDumperTest extends DumperTest +{ + public function testThis() + { + $this->setupGitRepo(); + $package = $this->setupPackage(); + $name = $this->getPackageFileName($package); + + $temp = sys_get_temp_dir(); + $zip = new ZipDumper($temp); + $zip->dump($package); + + $dist = sprintf('%s/%s.zip', + $temp, $name + ); + $this->assertFileExists($dist); + unlink($dist); + $this->removeGitRepo(); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testException() + { + new ZipDumper("/totally-random-" . time()); + } +} From 3d0ce85db2d2198bbfca0ef535121dd43c992aff Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Mon, 27 Aug 2012 09:33:04 +0200 Subject: [PATCH 02/43] Moved archive Dumpers into its own Archiver package --- .../ArchiverInterface.php} | 6 +++--- .../BaseDumper.php => Archiver/BaseArchiver.php} | 4 ++-- .../{Dumper/TarDumper.php => Archiver/TarArchiver.php} | 8 ++++---- .../{Dumper/ZipDumper.php => Archiver/ZipArchiver.php} | 8 ++++---- .../DumperTest.php => Archiver/ArchiverTest.php} | 8 ++++---- .../TarDumperTest.php => Archiver/TarArchiverTest.php} | 10 +++++----- .../ZipDumperTest.php => Archiver/ZipArchiverTest.php} | 10 +++++----- 7 files changed, 27 insertions(+), 27 deletions(-) rename src/Composer/Package/{Dumper/DumperInterface.php => Archiver/ArchiverInterface.php} (80%) rename src/Composer/Package/{Dumper/BaseDumper.php => Archiver/BaseArchiver.php} (97%) rename src/Composer/Package/{Dumper/TarDumper.php => Archiver/TarArchiver.php} (90%) rename src/Composer/Package/{Dumper/ZipDumper.php => Archiver/ZipArchiver.php} (89%) rename tests/Composer/Test/Package/{Dumper/DumperTest.php => Archiver/ArchiverTest.php} (88%) rename tests/Composer/Test/Package/{Dumper/TarDumperTest.php => Archiver/TarArchiverTest.php} (78%) rename tests/Composer/Test/Package/{Dumper/ZipDumperTest.php => Archiver/ZipArchiverTest.php} (78%) diff --git a/src/Composer/Package/Dumper/DumperInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php similarity index 80% rename from src/Composer/Package/Dumper/DumperInterface.php rename to src/Composer/Package/Archiver/ArchiverInterface.php index 03a0aa5f9..20566140a 100644 --- a/src/Composer/Package/Dumper/DumperInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -8,18 +8,18 @@ * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ -namespace Composer\Package\Dumper; +namespace Composer\Package\Archiver; use Composer\Package\PackageInterface; /** * @author Till Klampaeckel */ -interface DumperInterface +interface ArchiverInterface { /** * Return value depends on implementation - e.g. generating a tar or zip the - * method currently returns void, the ArrayDumper returns an array. + * method currently returns void, the ArrayArchiver returns an array. * * @param PackageInterface $package * diff --git a/src/Composer/Package/Dumper/BaseDumper.php b/src/Composer/Package/Archiver/BaseArchiver.php similarity index 97% rename from src/Composer/Package/Dumper/BaseDumper.php rename to src/Composer/Package/Archiver/BaseArchiver.php index 093e5a3f9..7d9c26d80 100644 --- a/src/Composer/Package/Dumper/BaseDumper.php +++ b/src/Composer/Package/Archiver/BaseArchiver.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Composer\Package\Dumper; +namespace Composer\Package\Archiver; use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; @@ -22,7 +22,7 @@ use Composer\Factory; /** * @author Till Klampaeckel */ -abstract class BaseDumper implements DumperInterface +abstract class BaseArchiver implements ArchiverInterface { /** * Format: zip or tar. diff --git a/src/Composer/Package/Dumper/TarDumper.php b/src/Composer/Package/Archiver/TarArchiver.php similarity index 90% rename from src/Composer/Package/Dumper/TarDumper.php rename to src/Composer/Package/Archiver/TarArchiver.php index 4de76c7ab..72b134e8e 100644 --- a/src/Composer/Package/Dumper/TarDumper.php +++ b/src/Composer/Package/Archiver/TarArchiver.php @@ -9,17 +9,17 @@ * file that was distributed with this source code. */ -namespace Composer\Package\Dumper; +namespace Composer\Package\Archiver; -use Composer\Package\Dumper\BaseDumper; -use Composer\Package\Dumper\DumperInterface; +use Composer\Package\Archiver\BaseArchiver; +use Composer\Package\Archiver\ArchiverInterface; use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; /** * @author Ulf Härnhammar */ -class TarDumper extends BaseDumper +class TarArchiver extends BaseArchiver { protected $format = 'tar'; diff --git a/src/Composer/Package/Dumper/ZipDumper.php b/src/Composer/Package/Archiver/ZipArchiver.php similarity index 89% rename from src/Composer/Package/Dumper/ZipDumper.php rename to src/Composer/Package/Archiver/ZipArchiver.php index 6ab55839d..9a6dcf7d3 100644 --- a/src/Composer/Package/Dumper/ZipDumper.php +++ b/src/Composer/Package/Archiver/ZipArchiver.php @@ -9,17 +9,17 @@ * file that was distributed with this source code. */ -namespace Composer\Package\Dumper; +namespace Composer\Package\Archiver; -use Composer\Package\Dumper\BaseDumper; -use Composer\Package\Dumper\DumperInterface; +use Composer\Package\Archiver\BaseArchiver; +use Composer\Package\Archiver\ArchiverInterface; use Composer\Package\PackageInterface; use Composer\Util\ProcessExecutor; /** * @author Till Klampaeckel */ -class ZipDumper extends BaseDumper +class ZipArchiver extends BaseArchiver { protected $format = 'zip'; diff --git a/tests/Composer/Test/Package/Dumper/DumperTest.php b/tests/Composer/Test/Package/Archiver/ArchiverTest.php similarity index 88% rename from tests/Composer/Test/Package/Dumper/DumperTest.php rename to tests/Composer/Test/Package/Archiver/ArchiverTest.php index 6db54ea69..e8f64cc86 100644 --- a/tests/Composer/Test/Package/Dumper/DumperTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiverTest.php @@ -9,13 +9,13 @@ * file that was distributed with this source code. */ -namespace Composer\Test\Package\Dumper; +namespace Composer\Test\Package\Archiver; use Composer\Package\MemoryPackage; use Composer\Util\Filesystem; use Composer\Util\ProcessExecutor; -abstract class DumperTest extends \PHPUnit_Framework_TestCase +abstract class ArchiverTest extends \PHPUnit_Framework_TestCase { /** * @var \Composer\Util\Filesystem @@ -36,7 +36,7 @@ abstract class DumperTest extends \PHPUnit_Framework_TestCase { $this->fs = new Filesystem; $this->process = new ProcessExecutor; - $this->testdir = sys_get_temp_dir() . '/composer_dumpertest_git_repository' . mt_rand(); + $this->testdir = sys_get_temp_dir() . '/composer_archivertest_git_repository' . mt_rand(); } protected function getTestDir() @@ -83,7 +83,7 @@ abstract class DumperTest extends \PHPUnit_Framework_TestCase protected function setupPackage() { $td = $this->getTestDir(); - $package = new MemoryPackage('dumpertest/dumpertest', 'master', 'master'); + $package = new MemoryPackage('archivertest/archivertest', 'master', 'master'); $package->setSourceUrl("file://$td"); $package->setSourceReference('master'); $package->setSourceType('git'); diff --git a/tests/Composer/Test/Package/Dumper/TarDumperTest.php b/tests/Composer/Test/Package/Archiver/TarArchiverTest.php similarity index 78% rename from tests/Composer/Test/Package/Dumper/TarDumperTest.php rename to tests/Composer/Test/Package/Archiver/TarArchiverTest.php index cd9917732..23cd09e64 100644 --- a/tests/Composer/Test/Package/Dumper/TarDumperTest.php +++ b/tests/Composer/Test/Package/Archiver/TarArchiverTest.php @@ -9,11 +9,11 @@ * file that was distributed with this source code. */ -namespace Composer\Test\Package\Dumper; +namespace Composer\Test\Package\Archiver; -use Composer\Package\Dumper\TarDumper; +use Composer\Package\Archiver\TarArchiver; -class TarDumperTest extends DumperTest +class TarArchiverTest extends ArchiverTest { public function testThis() { @@ -22,7 +22,7 @@ class TarDumperTest extends DumperTest $name = $this->getPackageFileName($package); $temp = sys_get_temp_dir(); - $tar = new TarDumper($temp); + $tar = new TarArchiver($temp); $tar->dump($package); $dist = sprintf('%s/%s.tar', @@ -38,6 +38,6 @@ class TarDumperTest extends DumperTest */ public function testException() { - new TarDumper("/totally-random-" . time()); + new TarArchiver("/totally-random-" . time()); } } diff --git a/tests/Composer/Test/Package/Dumper/ZipDumperTest.php b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php similarity index 78% rename from tests/Composer/Test/Package/Dumper/ZipDumperTest.php rename to tests/Composer/Test/Package/Archiver/ZipArchiverTest.php index 2fe71eb69..5d2fa40af 100644 --- a/tests/Composer/Test/Package/Dumper/ZipDumperTest.php +++ b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php @@ -9,11 +9,11 @@ * file that was distributed with this source code. */ -namespace Composer\Test\Package\Dumper; +namespace Composer\Test\Package\Archiver; -use Composer\Package\Dumper\ZipDumper; +use Composer\Package\Archiver\ZipArchiver; -class ZipDumperTest extends DumperTest +class ZipArchiverTest extends ArchiverTest { public function testThis() { @@ -22,7 +22,7 @@ class ZipDumperTest extends DumperTest $name = $this->getPackageFileName($package); $temp = sys_get_temp_dir(); - $zip = new ZipDumper($temp); + $zip = new ZipArchiver($temp); $zip->dump($package); $dist = sprintf('%s/%s.zip', @@ -38,6 +38,6 @@ class ZipDumperTest extends DumperTest */ public function testException() { - new ZipDumper("/totally-random-" . time()); + new ZipArchiver("/totally-random-" . time()); } } From 20e717f9751e4f0e284be0d2209868873eb2ecef Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Thu, 23 Aug 2012 21:35:17 +0200 Subject: [PATCH 03/43] Refactored the archiver package --- .../Package/Archiver/ArchiveManager.php | 113 +++++++++++++ .../Package/Archiver/ArchiverInterface.php | 20 ++- .../Package/Archiver/BaseArchiver.php | 153 ++---------------- src/Composer/Package/Archiver/GitArchiver.php | 58 +++++++ .../Package/Archiver/MercurialArchiver.php | 60 +++++++ src/Composer/Package/Archiver/TarArchiver.php | 46 ++---- src/Composer/Package/Archiver/VcsArchiver.php | 60 +++++++ src/Composer/Package/Archiver/ZipArchiver.php | 45 ++---- .../Package/Archiver/ArchiveManagerTest.php | 92 +++++++++++ .../Test/Package/Archiver/ArchiverTest.php | 57 +++---- .../Test/Package/Archiver/GitArchiverTest.php | 58 +++++++ .../Archiver/MercurialArchiverTest.php | 103 ++++++++++++ .../Test/Package/Archiver/TarArchiverTest.php | 31 ++-- .../Test/Package/Archiver/ZipArchiverTest.php | 31 ++-- 14 files changed, 654 insertions(+), 273 deletions(-) create mode 100644 src/Composer/Package/Archiver/ArchiveManager.php create mode 100644 src/Composer/Package/Archiver/GitArchiver.php create mode 100644 src/Composer/Package/Archiver/MercurialArchiver.php create mode 100644 src/Composer/Package/Archiver/VcsArchiver.php create mode 100644 tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php create mode 100644 tests/Composer/Test/Package/Archiver/GitArchiverTest.php create mode 100644 tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php new file mode 100644 index 000000000..4bd0b615c --- /dev/null +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -0,0 +1,113 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +use Composer\Downloader\DownloadManager; +use Composer\Factory; +use Composer\IO\NullIO; +use Composer\Package\PackageInterface; +use Composer\Util\Filesystem; + +/** + * @author Matthieu Moquet + * @author Till Klampaeckel + */ +class ArchiveManager +{ + protected $buildDir; + + protected $downloadManager; + + protected $archivers = array(); + + protected $vcsArchivers = array(); + + /** + * @param string $buildDir The directory used to build the archive + * @param DownloadManager $downloadManager A manager used to download package sources + */ + public function __construct($buildDir, DownloadManager $downloadManager = null) + { + $this->buildDir = $buildDir; + + if (null !== $downloadManager) { + $this->downloadManager = $downloadManager; + } else { + $factory = new Factory(); + $this->downloadManager = $factory->createDownloadManager(new NullIO()); + } + } + + /** + * @param ArchiverInterface $archiver + */ + public function addArchiver(ArchiverInterface $archiver) + { + if ($archiver instanceof VcsArchiver) { + $this->vcsArchivers[$archiver->getSourceType()] = $archiver; + } else { + $this->archivers[] = $archiver; + } + } + + /** + * Create an archive of the specified package. + * + * @param PackageInterface $package The package to archive + * @param string $format The format of the archive (zip, tar, ...) + * + * @return string The path of the created archive + */ + public function archive(PackageInterface $package, $format) + { + if (empty($format)) { + throw new \InvalidArgumentException('Format must be specified'); + } + + $filesystem = new Filesystem(); + $packageName = str_replace('/', DIRECTORY_SEPARATOR, $package->getUniqueName()); + + // Directory used to download the sources + $sources = sys_get_temp_dir().DIRECTORY_SEPARATOR.$packageName; + $filesystem->ensureDirectoryExists($sources); + + // Archive filename + $target = $this->buildDir.DIRECTORY_SEPARATOR.$packageName.'.'.$format; + $filesystem->ensureDirectoryExists(dirname($this->buildDir.$target)); + + // Download sources + $this->downloadManager->download($package, $sources, true); + + // Try VCS archivers first + $sourceType = $package->getSourceType(); + if (isset($this->archivers[$sourceType]) && $this->archivers[$sourceType]->supports($format)) { + $archiver = $this->archivers[$sourceType]; + $archiver->setSourceRef($sourceRef); + $archiver->setFormat($format); + $archiver->archive($sources, $target); + + return $target; + } + + // Fallback on default archivers + foreach ($this->archivers as $archiver) { + if ($archiver->supports($format)) { + $archiver->archive($sources, $target); + + return $target; + } + } + + throw new \RuntimeException(sprintf('No archiver found to support %s format', $format)); + } +} diff --git a/src/Composer/Package/Archiver/ArchiverInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php index 20566140a..f1d597787 100644 --- a/src/Composer/Package/Archiver/ArchiverInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -1,4 +1,5 @@ + * @author Matthieu Moquet */ interface ArchiverInterface { /** - * Return value depends on implementation - e.g. generating a tar or zip the - * method currently returns void, the ArrayArchiver returns an array. + * Create an archive from the sources. + * + * @param string $source The sources directory + * @param string $target The target file + */ + public function archive($sources, $target); + + /** + * Format supported by the archiver. * - * @param PackageInterface $package + * @param string $format The format to support * - * @return void + * @return boolean true if the format is supported by the archiver */ - public function dump(PackageInterface $package); + public function supports($format); } diff --git a/src/Composer/Package/Archiver/BaseArchiver.php b/src/Composer/Package/Archiver/BaseArchiver.php index 7d9c26d80..8fadd7056 100644 --- a/src/Composer/Package/Archiver/BaseArchiver.php +++ b/src/Composer/Package/Archiver/BaseArchiver.php @@ -1,4 +1,5 @@ + * @author Matthieu Moquet */ abstract class BaseArchiver implements ArchiverInterface { /** - * Format: zip or tar. - * @var string - */ - protected $format = ''; - - /** - * Path to where to dump the export to. - * @var mixed|null - */ - protected $path; - - /** - * @var ProcessExecutor - */ - protected $process; - - /** - * Working directory. - * @var string - */ - protected $temp; - - /** - * @param mixed $path - * @param ProcessExecutor|null $process + * Create a PHAR archive. * - * @throws \InvalidArgumentException + * @param string $sources Path of the directory to archive + * @param string $target Path of the file archive to create + * @param int $format Format of the archive */ - public function __construct($path = null, ProcessExecutor $process = null) - { - if (!empty($path)) { - if (!is_writable($path)) { - throw new \InvalidArgumentException("Not authorized to write to '{$path}'"); - } - $this->path = $path; - } - $this->process = $process ?: new ProcessExecutor(); - $this->temp = sys_get_temp_dir(); - } - - /** - * @return \Composer\Downloader\DownloadManager - */ - public function getDownloadManager() - { - $factory = new Factory; - $dm = $factory->createDownloadManager(new NullIO()); - return $dm; - } - - /** - * @param PackageInterface $package - * @param string $extension - * - * @return string - * @throws \InvalidArgumentException When unknown 'format' is encountered. - */ - public function getFilename(PackageInterface $package, $extension) - { - $name = $package->getPrettyVersion(); - $fileName = sprintf('%s.%s', $name, $extension); - return $fileName; - } - - /** - * @param PackageInterface $package - * - * @return string - * @throws \RuntimeException - */ - protected function getAndEnsureWorkDirectory(PackageInterface $package) - { - $workDir = sprintf('%s/%s/%s', $this->temp, $this->format, $package->getName()); - if (!file_exists($workDir)) { - mkdir($workDir, 0777, true); - } - if (!file_exists($workDir)) { - throw new \RuntimeException("Could not find '{$workDir}' directory."); - } - return $workDir; - } - - /** - * Package the given directory into an archive. - * - * The format is most likely \Phar::TAR or \Phar::ZIP. - * - * @param string $filename - * @param string $workDir - * @param int $format - * - * @throws \RuntimeException - */ - protected function package($filename, $workDir, $format) + protected function createPharArchive($sources, $target, $format) { try { - $phar = new \PharData($filename, null, null, $format); - $phar->buildFromDirectory($workDir); + $phar = new \PharData($target, null, null, $format); + $phar->buildFromDirectory($sources); } catch (\UnexpectedValueException $e) { - $message = "Original PHAR exception: " . (string) $e; - $message .= PHP_EOL . PHP_EOL; - $message .= sprintf("Could not create archive '%s' from '%s'.", $filename, $workDir); - throw new \RuntimeException($message); + throw new \RuntimeException( + sprintf("Could not create archive '%s' from '%s': %s", + $target, + $sources, + $e->getMessage() + )); } } - - /** - * @param string $fileName - * @param string $sourceRef - * @param string $workDir - */ - protected function packageGit($fileName, $sourceRef, $workDir) - { - $command = sprintf( - 'git archive --format %s --output %s %s', - $this->format, - escapeshellarg(sprintf('%s/%s', $this->path, $fileName)), - $sourceRef - ); - $this->process->execute($command, $output, $workDir); - } - - /** - * @param string $fileName - * @param string $sourceRef - * @param string $workDir - */ - protected function packageHg($fileName, $sourceRef, $workDir) - { - $format = ($this->format == 'tarball')?'tar':$this->format; - $command = sprintf( - 'hg archive --rev %s --type %s %s', - $sourceRef, - $format, - escapeshellarg(sprintf('%s/%s', $this->path, $fileName)) - ); - $this->process->execute($command, $output, $workDir); - } } diff --git a/src/Composer/Package/Archiver/GitArchiver.php b/src/Composer/Package/Archiver/GitArchiver.php new file mode 100644 index 000000000..92cb50839 --- /dev/null +++ b/src/Composer/Package/Archiver/GitArchiver.php @@ -0,0 +1,58 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +/** + * @author Till Klampaeckel + * @author Matthieu Moquet + */ +class GitArchiver extends VcsArchiver +{ + /** + * {@inheritdoc} + */ + public function archive($source, $target) + { + $format = $this->format ?: 'zip'; + $sourceRef = $this->sourceRef ?: 'HEAD'; + + $command = sprintf( + 'git archive --format %s --output %s %s', + $format, + escapeshellarg($target), + $sourceRef + ); + + $this->process->execute($command, $output, $source); + } + + /** + * {@inheritdoc} + */ + public function getSourceType() + { + return 'git'; + } + + /** + * {@inheritdoc} + */ + public function supports($format) + { + return in_array($format, array( + 'zip', + 'tar', + 'tgz', + )); + } +} \ No newline at end of file diff --git a/src/Composer/Package/Archiver/MercurialArchiver.php b/src/Composer/Package/Archiver/MercurialArchiver.php new file mode 100644 index 000000000..92b033e60 --- /dev/null +++ b/src/Composer/Package/Archiver/MercurialArchiver.php @@ -0,0 +1,60 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +/** + * @author Till Klampaeckel + * @author Matthieu Moquet + */ +class MercurialArchiver extends VcsArchiver +{ + /** + * {@inheritdoc} + */ + public function archive($source, $target) + { + $format = $this->format ?: 'zip'; + $sourceRef = $this->sourceRef ?: 'default'; + + $command = sprintf( + 'hg archive --rev %s --type %s %s', + $sourceRef, + $format, + escapeshellarg($target) + ); + + $this->process->execute($command, $output, $source); + } + + /** + * {@inheritdoc} + */ + public function getSourceType() + { + return 'hg'; + } + + /** + * {@inheritdoc} + */ + public function supports($format) + { + return in_array($format, array( + 'tar', + 'tbz2', + 'tgz', + 'uzip', + 'zip', + )); + } +} \ No newline at end of file diff --git a/src/Composer/Package/Archiver/TarArchiver.php b/src/Composer/Package/Archiver/TarArchiver.php index 72b134e8e..538f3dd5c 100644 --- a/src/Composer/Package/Archiver/TarArchiver.php +++ b/src/Composer/Package/Archiver/TarArchiver.php @@ -1,4 +1,5 @@ + * @author Till Klampaeckel + * @author Matthieu Moquet */ class TarArchiver extends BaseArchiver { - protected $format = 'tar'; - /** - * @param PackageInterface $package - * @throws \InvalidArgumentException + * {@inheritdoc} */ - public function dump(PackageInterface $package) + public function archive($sources, $target) { - $workDir = $this->getAndEnsureWorkDirectory($package); - - $fileName = $this->getFilename($package, 'tar'); - $sourceType = $package->getSourceType(); - $sourceRef = $package->getSourceReference(); - - $dm = $this->getDownloadManager(); - $dm->download($package, $workDir, true); + $this->createPharArchive($sources, $target, \Phar::TAR); + } - switch ($sourceType) { - case 'git': - $this->packageGit($fileName, $sourceRef, $workDir); - break; - case 'hg': - $this->packageHg($fileName, $sourceRef, $workDir); - break; - case 'svn': - $dir = $workDir . (substr($sourceRef, 0, 1) !== '/')?'/':'' . $sourceRef; - $this->package($fileName, $dir, \Phar::TAR); - break; - default: - throw new \InvalidArgumentException( - "Unable to handle repositories of type '{$sourceType}'."); - } + /** + * {@inheritdoc} + */ + public function supports($format) + { + return 'tar' === $format; } } diff --git a/src/Composer/Package/Archiver/VcsArchiver.php b/src/Composer/Package/Archiver/VcsArchiver.php new file mode 100644 index 000000000..ae83029a4 --- /dev/null +++ b/src/Composer/Package/Archiver/VcsArchiver.php @@ -0,0 +1,60 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +use Composer\Util\ProcessExecutor; + +/** + * VCS archivers are optimized for a specific source type. + * + * @author Till Klampaeckel + * @author Matthieu Moquet + */ +abstract class VcsArchiver implements ArchiverInterface +{ + protected $process; + protected $sourceRef; + protected $format; + + public function __construct($process = null) + { + $this->process = $process ?: new ProcessExecutor(); + } + + public function getSourceRef() + { + return $this->sourceRef; + } + + public function setSourceRef($sourceRef) + { + $this->sourceRef = $sourceRef; + } + + public function getFormat() + { + return $this->format; + } + + public function setFormat($format) + { + $this->format = $format; + } + + /** + * Get the source type supported by the archiver. + * + * @return string The source type of the archiver + */ + abstract public function getSourceType(); +} \ No newline at end of file diff --git a/src/Composer/Package/Archiver/ZipArchiver.php b/src/Composer/Package/Archiver/ZipArchiver.php index 9a6dcf7d3..f1032d096 100644 --- a/src/Composer/Package/Archiver/ZipArchiver.php +++ b/src/Composer/Package/Archiver/ZipArchiver.php @@ -1,4 +1,5 @@ + * @author Matthieu Moquet */ class ZipArchiver extends BaseArchiver { - protected $format = 'zip'; - /** - * @param PackageInterface $package - * @throws \InvalidArgumentException + * {@inheritdoc} */ - public function dump(PackageInterface $package) + public function archive($sources, $target) { - $workDir = $this->getAndEnsureWorkDirectory($package); - - $fileName = $this->getFilename($package, 'zip'); - $sourceType = $package->getSourceType(); - $sourceRef = $package->getSourceReference(); - - $dm = $this->getDownloadManager(); - $dm->download($package, $workDir, true); + $this->createPharArchive($sources, $target, \Phar::ZIP); + } - switch ($sourceType) { - case 'git': - $this->packageGit($fileName, $sourceRef, $workDir); - break; - case 'hg': - $this->packageHg($fileName, $sourceRef, $workDir); - break; - case 'svn': - $dir = $workDir . (substr($sourceRef, 0, 1) !== '/')?'/':'' . $sourceRef; - $this->package($fileName, $dir, \Phar::ZIP); - break; - default: - throw new \InvalidArgumentException("Unable to handle repositories of type '{$sourceType}'."); - } + /** + * {@inheritdoc} + */ + public function supports($format) + { + return 'zip' === $format; } -} \ No newline at end of file +} diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php new file mode 100644 index 000000000..75f1259c5 --- /dev/null +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -0,0 +1,92 @@ + + * 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\Archiver; + +use Composer\Package\Archiver; +use Composer\Package\Archiver\ArchiveManager; +use Composer\Package\PackageInterface; + +/** + * @author Till Klampaeckel + * @author Matthieu Moquet + */ +class ArchiveManagerTest extends ArchiverTest +{ + protected $manager; + + protected $workDir; + + public function setUp() + { + parent::setUp(); + + $this->workDir = sys_get_temp_dir(); + + $this->manager = new ArchiveManager($this->workDir); + $this->manager->addArchiver(new Archiver\GitArchiver); + $this->manager->addArchiver(new Archiver\MercurialArchiver); + $this->manager->addArchiver(new Archiver\TarArchiver); + $this->manager->addArchiver(new Archiver\ZipArchiver); + } + + public function testUnknownFormat() + { + $this->setExpectedException('RuntimeException'); + + $package = $this->setupPackage(); + + $this->manager->archive($package, '__unknown_format__'); + } + + public function testArchiveTarWithVcs() + { + $this->setupGitRepo(); + + $package = $this->setupPackage(); + + // The package is source from git, + // so it should `git archive --format tar` + $this->manager->archive($package, 'tar'); + + $target = $this->getTargetName($package, 'tar'); + $this->assertFileExists($target); + + unlink($target); + $this->removeGitRepo(); + } + + public function testArchiveTarWithoutVcs() + { + $this->setupGitRepo(); + + $package = $this->setupPackage(); + + // This should use the TarArchiver + $this->manager->archive($package, 'tar'); + + $package->setSourceType('__unknown_type__'); // disable VCS recognition + $target = $this->getTargetName($package, 'tar'); + $this->assertFileExists($target); + + unlink($target); + $this->removeGitRepo(); + } + + protected function getTargetName(PackageInterface $package, $format) + { + $packageName = str_replace('/', DIRECTORY_SEPARATOR, $package->getUniqueName()); + $target = $this->workDir.DIRECTORY_SEPARATOR.$packageName.'.'.$format; + + return $target; + } +} diff --git a/tests/Composer/Test/Package/Archiver/ArchiverTest.php b/tests/Composer/Test/Package/Archiver/ArchiverTest.php index e8f64cc86..f1cc3d43a 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiverTest.php @@ -1,4 +1,5 @@ + * @author Matthieu Moquet + */ abstract class ArchiverTest extends \PHPUnit_Framework_TestCase { /** * @var \Composer\Util\Filesystem */ - protected $fs; + protected $filesystem; /** * @var \Composer\Util\ProcessExecutor @@ -30,18 +35,13 @@ abstract class ArchiverTest extends \PHPUnit_Framework_TestCase /** * @var string */ - protected $testdir = ''; + protected $testDir; public function setUp() { - $this->fs = new Filesystem; - $this->process = new ProcessExecutor; - $this->testdir = sys_get_temp_dir() . '/composer_archivertest_git_repository' . mt_rand(); - } - - protected function getTestDir() - { - return $this->testdir; + $this->filesystem = new Filesystem(); + $this->process = new ProcessExecutor(); + $this->testDir = sys_get_temp_dir().'/composer_archivertest_git_repository'.mt_rand(); } /** @@ -49,49 +49,42 @@ abstract class ArchiverTest extends \PHPUnit_Framework_TestCase */ protected function setupGitRepo() { - $td = $this->getTestDir(); - - $this->fs->removeDirectory($td); - $this->fs->ensureDirectoryExists($td); + $this->filesystem->removeDirectory($this->testDir); + $this->filesystem->ensureDirectoryExists($this->testDir); $currentWorkDir = getcwd(); - chdir($td); + chdir($this->testDir); - $result = $this->process->execute("git init -q"); + $result = $this->process->execute('git init -q'); if ($result > 0) { - throw new \RuntimeException( - "Could not init: " . $this->process->getErrorOutput()); + throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); } + $result = file_put_contents('b', 'a'); if (false === $result) { - throw new \RuntimeException("Could not save file."); + throw new \RuntimeException('Could not save file.'); } - $result = $this->process->execute("git add b && git commit -m 'commit b' -q"); + + $result = $this->process->execute('git add b && git commit -m "commit b" -q'); if ($result > 0) { - throw new \RuntimeException( - "Could not init: " . $this->process->getErrorOutput()); + throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); } + chdir($currentWorkDir); } protected function removeGitRepo() { - $td = $this->getTestDir(); - $this->fs->removeDirectory($td); + $this->filesystem->removeDirectory($this->testDir); } protected function setupPackage() { - $td = $this->getTestDir(); $package = new MemoryPackage('archivertest/archivertest', 'master', 'master'); - $package->setSourceUrl("file://$td"); + $package->setSourceUrl(realpath($this->testDir)); $package->setSourceReference('master'); $package->setSourceType('git'); - return $package; - } - protected function getPackageFileName(MemoryPackage $package) - { - return $package->getVersion(); + return $package; } } diff --git a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php new file mode 100644 index 000000000..3ac47e22c --- /dev/null +++ b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php @@ -0,0 +1,58 @@ + + * 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\Archiver; + +use Composer\Package\Archiver\GitArchiver; + +/** + * @author Till Klampaeckel + * @author Matthieu Moquet + */ +class GitArchiverTest extends ArchiverTest +{ + public function testZipArchive() + { + $this->setupGitRepo(); + + $package = $this->setupPackage(); + $target = sys_get_temp_dir().'/composer_archiver_test.zip'; + + // Test archive + $archiver = new GitArchiver(); + $archiver->setFormat('zip'); + $archiver->setSourceRef('master'); + $archiver->archive($package->getSourceUrl(), $target); + $this->assertFileExists($target); + + unlink($target); + $this->removeGitRepo(); + } + + public function testTarArchive() + { + $this->setupGitRepo(); + + $package = $this->setupPackage(); + $target = sys_get_temp_dir().'/composer_archiver_test.tar'; + + // Test archive + $archiver = new GitArchiver(); + $archiver->setFormat('tar'); + $archiver->setSourceRef('master'); + $archiver->archive($package->getSourceUrl(), $target); + $this->assertFileExists($target); + + unlink($target); + $this->removeGitRepo(); + } +} diff --git a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php new file mode 100644 index 000000000..b7b0eca64 --- /dev/null +++ b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php @@ -0,0 +1,103 @@ + + * 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\Archiver; + +use Composer\Package\Archiver\MercurialArchiver; +use Composer\Package\MemoryPackage; + +/** + * @author Matthieu Moquet + * @author Till Klampaeckel + */ +class MercurialArchiverTest extends ArchiverTest +{ + public function testZipArchive() + { + $this->setupMercurialRepo(); + + $package = $this->setupMercurialPackage(); + $target = sys_get_temp_dir().'/composer_archiver_test.zip'; + + // Test archive + $archiver = new MercurialArchiver(); + $archiver->setFormat('zip'); + $archiver->setSourceRef('default'); + $archiver->archive($package->getSourceUrl(), $target); + $this->assertFileExists($target); + + unlink($target); + $this->removeMercurialRepo(); + } + + public function testTarArchive() + { + $this->setupMercurialRepo(); + + $package = $this->setupMercurialPackage(); + $target = sys_get_temp_dir().'/composer_archiver_test.tar'; + + // Test archive + $archiver = new MercurialArchiver(); + $archiver->setFormat('tar'); + $archiver->setSourceRef('default'); + $archiver->archive($package->getSourceUrl(), $target); + $this->assertFileExists($target); + + unlink($target); + $this->removeMercurialRepo(); + } + + /** + * Create local git repository to run tests against! + */ + protected function setupMercurialRepo() + { + $this->filesystem->removeDirectory($this->testDir); + $this->filesystem->ensureDirectoryExists($this->testDir); + + $currentWorkDir = getcwd(); + chdir($this->testDir); + + $result = $this->process->execute('hg init -q'); + if ($result > 0) { + throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); + } + + $result = file_put_contents('b', 'a'); + if (false === $result) { + throw new \RuntimeException('Could not save file.'); + } + + $result = $this->process->execute('hg add b && hg commit -m "commit b" --config ui.username=test -q'); + if ($result > 0) { + throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); + } + + chdir($currentWorkDir); + } + + protected function removeMercurialRepo() + { + $this->filesystem->removeDirectory($this->testDir); + } + + protected function setupMercurialPackage() + { + $package = new MemoryPackage('archivertest/archivertest', 'master', 'master'); + $package->setSourceUrl(realpath($this->testDir)); + $package->setSourceReference('default'); + $package->setSourceType('hg'); + + return $package; + } +} diff --git a/tests/Composer/Test/Package/Archiver/TarArchiverTest.php b/tests/Composer/Test/Package/Archiver/TarArchiverTest.php index 23cd09e64..caf8d5e98 100644 --- a/tests/Composer/Test/Package/Archiver/TarArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/TarArchiverTest.php @@ -1,4 +1,5 @@ + * @author Matthieu Moquet + */ class TarArchiverTest extends ArchiverTest { - public function testThis() + public function testArchive() { $this->setupGitRepo(); + $package = $this->setupPackage(); - $name = $this->getPackageFileName($package); + $target = sys_get_temp_dir().'/composer_archiver_test.tar'; - $temp = sys_get_temp_dir(); - $tar = new TarArchiver($temp); - $tar->dump($package); + // Test archive + $archiver = new TarArchiver(); + $archiver->archive($package->getSourceUrl(), $target); + $this->assertFileExists($target); - $dist = sprintf('%s/%s.tar', - $temp, $name - ); - $this->assertFileExists($dist); - unlink($dist); + unlink($target); $this->removeGitRepo(); } - - /** - * @expectedException \InvalidArgumentException - */ - public function testException() - { - new TarArchiver("/totally-random-" . time()); - } } diff --git a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php index 5d2fa40af..0e7403f59 100644 --- a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php @@ -1,4 +1,5 @@ + * @author Matthieu Moquet + */ class ZipArchiverTest extends ArchiverTest { - public function testThis() + public function testArchive() { $this->setupGitRepo(); + $package = $this->setupPackage(); - $name = $this->getPackageFileName($package); + $target = sys_get_temp_dir().'/composer_archiver_test.zip'; - $temp = sys_get_temp_dir(); - $zip = new ZipArchiver($temp); - $zip->dump($package); + // Test archive + $archiver = new ZipArchiver(); + $archiver->archive($package->getSourceUrl(), $target); + $this->assertFileExists($target); - $dist = sprintf('%s/%s.zip', - $temp, $name - ); - $this->assertFileExists($dist); - unlink($dist); + unlink($target); $this->removeGitRepo(); } - - /** - * @expectedException \InvalidArgumentException - */ - public function testException() - { - new ZipArchiver("/totally-random-" . time()); - } } From 2fd17ecff8909ef8caa0647746599b8a780b8221 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Mon, 27 Aug 2012 10:00:31 +0200 Subject: [PATCH 04/43] Changed Package class due to upstream changes --- tests/Composer/Test/Package/Archiver/ArchiverTest.php | 4 ++-- .../Composer/Test/Package/Archiver/MercurialArchiverTest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Composer/Test/Package/Archiver/ArchiverTest.php b/tests/Composer/Test/Package/Archiver/ArchiverTest.php index f1cc3d43a..4b8d91c51 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiverTest.php @@ -14,7 +14,7 @@ namespace Composer\Test\Package\Archiver; use Composer\Util\Filesystem; use Composer\Util\ProcessExecutor; -use Composer\Package\MemoryPackage; +use Composer\Package\Package; /** * @author Till Klampaeckel @@ -80,7 +80,7 @@ abstract class ArchiverTest extends \PHPUnit_Framework_TestCase protected function setupPackage() { - $package = new MemoryPackage('archivertest/archivertest', 'master', 'master'); + $package = new Package('archivertest/archivertest', 'master', 'master'); $package->setSourceUrl(realpath($this->testDir)); $package->setSourceReference('master'); $package->setSourceType('git'); diff --git a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php index b7b0eca64..936d8b24b 100644 --- a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php @@ -13,7 +13,7 @@ namespace Composer\Test\Package\Archiver; use Composer\Package\Archiver\MercurialArchiver; -use Composer\Package\MemoryPackage; +use Composer\Package\Package; /** * @author Matthieu Moquet @@ -93,7 +93,7 @@ class MercurialArchiverTest extends ArchiverTest protected function setupMercurialPackage() { - $package = new MemoryPackage('archivertest/archivertest', 'master', 'master'); + $package = new Package('archivertest/archivertest', 'master', 'master'); $package->setSourceUrl(realpath($this->testDir)); $package->setSourceReference('default'); $package->setSourceType('hg'); From 3b2279105932c7b78ad3d58bf662e7f9ce8e87a6 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Mon, 27 Aug 2012 10:59:30 +0200 Subject: [PATCH 05/43] Checks process execution --- src/Composer/Package/Archiver/BaseArchiver.php | 13 +++++++------ src/Composer/Package/Archiver/GitArchiver.php | 8 +++++++- src/Composer/Package/Archiver/MercurialArchiver.php | 8 +++++++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Composer/Package/Archiver/BaseArchiver.php b/src/Composer/Package/Archiver/BaseArchiver.php index 8fadd7056..69fb892a8 100644 --- a/src/Composer/Package/Archiver/BaseArchiver.php +++ b/src/Composer/Package/Archiver/BaseArchiver.php @@ -34,12 +34,13 @@ abstract class BaseArchiver implements ArchiverInterface $phar = new \PharData($target, null, null, $format); $phar->buildFromDirectory($sources); } catch (\UnexpectedValueException $e) { - throw new \RuntimeException( - sprintf("Could not create archive '%s' from '%s': %s", - $target, - $sources, - $e->getMessage() - )); + $message = sprintf("Could not create archive '%s' from '%s': %s", + $target, + $sources, + $e->getMessage() + ); + + throw new \RuntimeException($message, $e->getCode(), $e); } } } diff --git a/src/Composer/Package/Archiver/GitArchiver.php b/src/Composer/Package/Archiver/GitArchiver.php index 92cb50839..50086c266 100644 --- a/src/Composer/Package/Archiver/GitArchiver.php +++ b/src/Composer/Package/Archiver/GitArchiver.php @@ -33,7 +33,13 @@ class GitArchiver extends VcsArchiver $sourceRef ); - $this->process->execute($command, $output, $source); + $exitCode = $this->process->execute($command, $output, $source); + + if (0 !== $exitCode) { + throw new \RuntimeException( + sprintf('The command `%s` returned %s', $command, $exitCode) + ); + } } /** diff --git a/src/Composer/Package/Archiver/MercurialArchiver.php b/src/Composer/Package/Archiver/MercurialArchiver.php index 92b033e60..91695df85 100644 --- a/src/Composer/Package/Archiver/MercurialArchiver.php +++ b/src/Composer/Package/Archiver/MercurialArchiver.php @@ -33,7 +33,13 @@ class MercurialArchiver extends VcsArchiver escapeshellarg($target) ); - $this->process->execute($command, $output, $source); + $exitCode = $this->process->execute($command, $output, $source); + + if (0 !== $exitCode) { + throw new \RuntimeException( + sprintf('The command `%s` returned %s', $command, $exitCode) + ); + } } /** From bfd2275cb0f676d9a7d406637715a64aaee49557 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Tue, 28 Aug 2012 22:00:28 +0200 Subject: [PATCH 06/43] Update interface to merge vcs with basic archivers --- .../Package/Archiver/ArchiveManager.php | 24 ++------ .../Package/Archiver/ArchiverInterface.php | 14 +++-- src/Composer/Package/Archiver/GitArchiver.php | 34 ++++++----- .../Package/Archiver/MercurialArchiver.php | 32 +++++----- src/Composer/Package/Archiver/TarArchiver.php | 4 +- src/Composer/Package/Archiver/VcsArchiver.php | 60 ------------------- src/Composer/Package/Archiver/ZipArchiver.php | 4 +- .../Test/Package/Archiver/GitArchiverTest.php | 8 +-- .../Archiver/MercurialArchiverTest.php | 8 +-- .../Test/Package/Archiver/TarArchiverTest.php | 2 +- .../Test/Package/Archiver/ZipArchiverTest.php | 2 +- 11 files changed, 59 insertions(+), 133 deletions(-) delete mode 100644 src/Composer/Package/Archiver/VcsArchiver.php diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 4bd0b615c..73d273505 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -30,8 +30,6 @@ class ArchiveManager protected $archivers = array(); - protected $vcsArchivers = array(); - /** * @param string $buildDir The directory used to build the archive * @param DownloadManager $downloadManager A manager used to download package sources @@ -53,11 +51,7 @@ class ArchiveManager */ public function addArchiver(ArchiverInterface $archiver) { - if ($archiver instanceof VcsArchiver) { - $this->vcsArchivers[$archiver->getSourceType()] = $archiver; - } else { - $this->archivers[] = $archiver; - } + $this->archivers[] = $archiver; } /** @@ -88,21 +82,11 @@ class ArchiveManager // Download sources $this->downloadManager->download($package, $sources, true); - // Try VCS archivers first $sourceType = $package->getSourceType(); - if (isset($this->archivers[$sourceType]) && $this->archivers[$sourceType]->supports($format)) { - $archiver = $this->archivers[$sourceType]; - $archiver->setSourceRef($sourceRef); - $archiver->setFormat($format); - $archiver->archive($sources, $target); - - return $target; - } - - // Fallback on default archivers + $sourceRef = $package->getSourceReference(); foreach ($this->archivers as $archiver) { - if ($archiver->supports($format)) { - $archiver->archive($sources, $target); + if ($archiver->supports($format, $sourceType)) { + $archiver->archive($sources, $target, $format, $sourceRef); return $target; } diff --git a/src/Composer/Package/Archiver/ArchiverInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php index f1d597787..596402154 100644 --- a/src/Composer/Package/Archiver/ArchiverInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -23,17 +23,21 @@ interface ArchiverInterface /** * Create an archive from the sources. * - * @param string $source The sources directory - * @param string $target The target file + * @param string $source The sources directory + * @param string $target The target file + * @param string $format The format used for archive + * @param string $sourceRef The reference of the source to archive or null + * for the current reference */ - public function archive($sources, $target); + public function archive($sources, $target, $format, $sourceRef = null); /** * Format supported by the archiver. * - * @param string $format The format to support + * @param string $format The archive format + * @param string $sourceType The source type (git, svn, hg, etc.) * * @return boolean true if the format is supported by the archiver */ - public function supports($format); + public function supports($format, $sourceType); } diff --git a/src/Composer/Package/Archiver/GitArchiver.php b/src/Composer/Package/Archiver/GitArchiver.php index 50086c266..0a33ab756 100644 --- a/src/Composer/Package/Archiver/GitArchiver.php +++ b/src/Composer/Package/Archiver/GitArchiver.php @@ -12,19 +12,31 @@ namespace Composer\Package\Archiver; +use Composer\Util\ProcessExecutor; + /** * @author Till Klampaeckel * @author Matthieu Moquet */ -class GitArchiver extends VcsArchiver +class GitArchiver implements ArchiverInterface { + protected $process; + + public function __construct($process = null) + { + $this->process = $process ?: new ProcessExecutor(); + } + /** * {@inheritdoc} */ - public function archive($source, $target) + public function archive($sources, $target, $format, $sourceRef = null) { - $format = $this->format ?: 'zip'; - $sourceRef = $this->sourceRef ?: 'HEAD'; + // Since git-archive no longer works with a commit ID in git 1.7.10, + // use by default the HEAD reference instead of the commit sha1 + if (null === $sourceRef || preg_match('/^[0-9a-f]{40}$/i',$sourceRef)) { + $sourceRef = 'HEAD'; + } $command = sprintf( 'git archive --format %s --output %s %s', @@ -33,7 +45,7 @@ class GitArchiver extends VcsArchiver $sourceRef ); - $exitCode = $this->process->execute($command, $output, $source); + $exitCode = $this->process->execute($command, $output, $sources); if (0 !== $exitCode) { throw new \RuntimeException( @@ -45,17 +57,9 @@ class GitArchiver extends VcsArchiver /** * {@inheritdoc} */ - public function getSourceType() - { - return 'git'; - } - - /** - * {@inheritdoc} - */ - public function supports($format) + public function supports($format, $sourceType) { - return in_array($format, array( + return 'git' === $sourceType && in_array($format, array( 'zip', 'tar', 'tgz', diff --git a/src/Composer/Package/Archiver/MercurialArchiver.php b/src/Composer/Package/Archiver/MercurialArchiver.php index 91695df85..7de0430e1 100644 --- a/src/Composer/Package/Archiver/MercurialArchiver.php +++ b/src/Composer/Package/Archiver/MercurialArchiver.php @@ -12,19 +12,29 @@ namespace Composer\Package\Archiver; +use Composer\Util\ProcessExecutor; + /** * @author Till Klampaeckel * @author Matthieu Moquet */ -class MercurialArchiver extends VcsArchiver +class MercurialArchiver implements ArchiverInterface { + protected $process; + + public function __construct($process = null) + { + $this->process = $process ?: new ProcessExecutor(); + } + /** * {@inheritdoc} */ - public function archive($source, $target) + public function archive($sources, $target, $format, $sourceRef = null) { - $format = $this->format ?: 'zip'; - $sourceRef = $this->sourceRef ?: 'default'; + if (null === $sourceRef) { + $sourceRef = 'default'; + } $command = sprintf( 'hg archive --rev %s --type %s %s', @@ -33,7 +43,7 @@ class MercurialArchiver extends VcsArchiver escapeshellarg($target) ); - $exitCode = $this->process->execute($command, $output, $source); + $exitCode = $this->process->execute($command, $output, $sources); if (0 !== $exitCode) { throw new \RuntimeException( @@ -45,17 +55,9 @@ class MercurialArchiver extends VcsArchiver /** * {@inheritdoc} */ - public function getSourceType() - { - return 'hg'; - } - - /** - * {@inheritdoc} - */ - public function supports($format) + public function supports($format, $sourceType) { - return in_array($format, array( + return 'hg' === $sourceType && in_array($format, array( 'tar', 'tbz2', 'tgz', diff --git a/src/Composer/Package/Archiver/TarArchiver.php b/src/Composer/Package/Archiver/TarArchiver.php index 538f3dd5c..a7c4e9b36 100644 --- a/src/Composer/Package/Archiver/TarArchiver.php +++ b/src/Composer/Package/Archiver/TarArchiver.php @@ -24,7 +24,7 @@ class TarArchiver extends BaseArchiver /** * {@inheritdoc} */ - public function archive($sources, $target) + public function archive($sources, $target, $format, $sourceRef = null) { $this->createPharArchive($sources, $target, \Phar::TAR); } @@ -32,7 +32,7 @@ class TarArchiver extends BaseArchiver /** * {@inheritdoc} */ - public function supports($format) + public function supports($format, $sourceType) { return 'tar' === $format; } diff --git a/src/Composer/Package/Archiver/VcsArchiver.php b/src/Composer/Package/Archiver/VcsArchiver.php deleted file mode 100644 index ae83029a4..000000000 --- a/src/Composer/Package/Archiver/VcsArchiver.php +++ /dev/null @@ -1,60 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\Archiver; - -use Composer\Util\ProcessExecutor; - -/** - * VCS archivers are optimized for a specific source type. - * - * @author Till Klampaeckel - * @author Matthieu Moquet - */ -abstract class VcsArchiver implements ArchiverInterface -{ - protected $process; - protected $sourceRef; - protected $format; - - public function __construct($process = null) - { - $this->process = $process ?: new ProcessExecutor(); - } - - public function getSourceRef() - { - return $this->sourceRef; - } - - public function setSourceRef($sourceRef) - { - $this->sourceRef = $sourceRef; - } - - public function getFormat() - { - return $this->format; - } - - public function setFormat($format) - { - $this->format = $format; - } - - /** - * Get the source type supported by the archiver. - * - * @return string The source type of the archiver - */ - abstract public function getSourceType(); -} \ No newline at end of file diff --git a/src/Composer/Package/Archiver/ZipArchiver.php b/src/Composer/Package/Archiver/ZipArchiver.php index f1032d096..1664b2a8d 100644 --- a/src/Composer/Package/Archiver/ZipArchiver.php +++ b/src/Composer/Package/Archiver/ZipArchiver.php @@ -24,7 +24,7 @@ class ZipArchiver extends BaseArchiver /** * {@inheritdoc} */ - public function archive($sources, $target) + public function archive($sources, $target, $format, $sourceRef = null) { $this->createPharArchive($sources, $target, \Phar::ZIP); } @@ -32,7 +32,7 @@ class ZipArchiver extends BaseArchiver /** * {@inheritdoc} */ - public function supports($format) + public function supports($format, $sourceType) { return 'zip' === $format; } diff --git a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php index 3ac47e22c..9ddb9d4f4 100644 --- a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php @@ -29,9 +29,7 @@ class GitArchiverTest extends ArchiverTest // Test archive $archiver = new GitArchiver(); - $archiver->setFormat('zip'); - $archiver->setSourceRef('master'); - $archiver->archive($package->getSourceUrl(), $target); + $archiver->archive($package->getSourceUrl(), $target, 'zip', 'master'); $this->assertFileExists($target); unlink($target); @@ -47,9 +45,7 @@ class GitArchiverTest extends ArchiverTest // Test archive $archiver = new GitArchiver(); - $archiver->setFormat('tar'); - $archiver->setSourceRef('master'); - $archiver->archive($package->getSourceUrl(), $target); + $archiver->archive($package->getSourceUrl(), $target, 'tar', 'master'); $this->assertFileExists($target); unlink($target); diff --git a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php index 936d8b24b..4934572da 100644 --- a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php @@ -30,9 +30,7 @@ class MercurialArchiverTest extends ArchiverTest // Test archive $archiver = new MercurialArchiver(); - $archiver->setFormat('zip'); - $archiver->setSourceRef('default'); - $archiver->archive($package->getSourceUrl(), $target); + $archiver->archive($package->getSourceUrl(), $target, 'zip', 'default'); $this->assertFileExists($target); unlink($target); @@ -48,9 +46,7 @@ class MercurialArchiverTest extends ArchiverTest // Test archive $archiver = new MercurialArchiver(); - $archiver->setFormat('tar'); - $archiver->setSourceRef('default'); - $archiver->archive($package->getSourceUrl(), $target); + $archiver->archive($package->getSourceUrl(), $target, 'tar', 'default'); $this->assertFileExists($target); unlink($target); diff --git a/tests/Composer/Test/Package/Archiver/TarArchiverTest.php b/tests/Composer/Test/Package/Archiver/TarArchiverTest.php index caf8d5e98..faab116ef 100644 --- a/tests/Composer/Test/Package/Archiver/TarArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/TarArchiverTest.php @@ -29,7 +29,7 @@ class TarArchiverTest extends ArchiverTest // Test archive $archiver = new TarArchiver(); - $archiver->archive($package->getSourceUrl(), $target); + $archiver->archive($package->getSourceUrl(), $target, 'tar'); $this->assertFileExists($target); unlink($target); diff --git a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php index 0e7403f59..ed919b21a 100644 --- a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php @@ -29,7 +29,7 @@ class ZipArchiverTest extends ArchiverTest // Test archive $archiver = new ZipArchiver(); - $archiver->archive($package->getSourceUrl(), $target); + $archiver->archive($package->getSourceUrl(), $target, 'zip'); $this->assertFileExists($target); unlink($target); From b21bb1dcc5de19fe87f518eeab5e4ddf7c94daed Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Tue, 28 Aug 2012 22:27:25 +0200 Subject: [PATCH 07/43] Checks support before downloading the package --- .../Package/Archiver/ArchiveManager.php | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 73d273505..f5123f7ef 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -68,6 +68,20 @@ class ArchiveManager throw new \InvalidArgumentException('Format must be specified'); } + $usableArchiver = null; + $sourceType = $package->getSourceType(); + + foreach ($this->archivers as $archiver) { + if ($archiver->supports($format, $package->getSourceType())) { + $usableArchiver = $archiver; + } + } + + // Checks the format/source type are supported before downloading the package + if (null === $usableArchiver) { + throw new \RuntimeException(sprintf('No archiver found to support %s format', $format)); + } + $filesystem = new Filesystem(); $packageName = str_replace('/', DIRECTORY_SEPARATOR, $package->getUniqueName()); @@ -82,16 +96,7 @@ class ArchiveManager // Download sources $this->downloadManager->download($package, $sources, true); - $sourceType = $package->getSourceType(); - $sourceRef = $package->getSourceReference(); - foreach ($this->archivers as $archiver) { - if ($archiver->supports($format, $sourceType)) { - $archiver->archive($sources, $target, $format, $sourceRef); - - return $target; - } - } - - throw new \RuntimeException(sprintf('No archiver found to support %s format', $format)); + $sourceRef = $package->getSourceReference(); + $usableArchiver->archive($sources, $target, $format, $sourceRef); } } From a733d76b33a4dba221b25694c7038fe9d13db2f3 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Tue, 28 Aug 2012 22:32:26 +0200 Subject: [PATCH 08/43] Merged zip & tar archivers --- .../{TarArchiver.php => PharArchiver.php} | 12 ++++-- src/Composer/Package/Archiver/ZipArchiver.php | 39 ------------------- .../Package/Archiver/ArchiveManagerTest.php | 3 +- ...rArchiverTest.php => PharArchiverTest.php} | 24 ++++++++++-- .../Test/Package/Archiver/ZipArchiverTest.php | 38 ------------------ 5 files changed, 30 insertions(+), 86 deletions(-) rename src/Composer/Package/Archiver/{TarArchiver.php => PharArchiver.php} (67%) delete mode 100644 src/Composer/Package/Archiver/ZipArchiver.php rename tests/Composer/Test/Package/Archiver/{TarArchiverTest.php => PharArchiverTest.php} (56%) delete mode 100644 tests/Composer/Test/Package/Archiver/ZipArchiverTest.php diff --git a/src/Composer/Package/Archiver/TarArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php similarity index 67% rename from src/Composer/Package/Archiver/TarArchiver.php rename to src/Composer/Package/Archiver/PharArchiver.php index a7c4e9b36..f8a369b52 100644 --- a/src/Composer/Package/Archiver/TarArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -19,14 +19,20 @@ use Composer\Package\PackageInterface; * @author Till Klampaeckel * @author Matthieu Moquet */ -class TarArchiver extends BaseArchiver +class PharArchiver extends BaseArchiver { + static public $formats = array( + 'zip' => \Phar::ZIP, + 'tar' => \Phar::TAR, + ); + /** * {@inheritdoc} */ public function archive($sources, $target, $format, $sourceRef = null) { - $this->createPharArchive($sources, $target, \Phar::TAR); + // source reference is useless for this archiver + $this->createPharArchive($sources, $target, static::$formats[$format]); } /** @@ -34,6 +40,6 @@ class TarArchiver extends BaseArchiver */ public function supports($format, $sourceType) { - return 'tar' === $format; + return in_array($format, array_keys(static::$formats)); } } diff --git a/src/Composer/Package/Archiver/ZipArchiver.php b/src/Composer/Package/Archiver/ZipArchiver.php deleted file mode 100644 index 1664b2a8d..000000000 --- a/src/Composer/Package/Archiver/ZipArchiver.php +++ /dev/null @@ -1,39 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\Archiver; - -use Composer\Package\BasePackage; -use Composer\Package\PackageInterface; - -/** - * @author Till Klampaeckel - * @author Matthieu Moquet - */ -class ZipArchiver extends BaseArchiver -{ - /** - * {@inheritdoc} - */ - public function archive($sources, $target, $format, $sourceRef = null) - { - $this->createPharArchive($sources, $target, \Phar::ZIP); - } - - /** - * {@inheritdoc} - */ - public function supports($format, $sourceType) - { - return 'zip' === $format; - } -} diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index 75f1259c5..34c595991 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -35,8 +35,7 @@ class ArchiveManagerTest extends ArchiverTest $this->manager = new ArchiveManager($this->workDir); $this->manager->addArchiver(new Archiver\GitArchiver); $this->manager->addArchiver(new Archiver\MercurialArchiver); - $this->manager->addArchiver(new Archiver\TarArchiver); - $this->manager->addArchiver(new Archiver\ZipArchiver); + $this->manager->addArchiver(new Archiver\PharArchiver); } public function testUnknownFormat() diff --git a/tests/Composer/Test/Package/Archiver/TarArchiverTest.php b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php similarity index 56% rename from tests/Composer/Test/Package/Archiver/TarArchiverTest.php rename to tests/Composer/Test/Package/Archiver/PharArchiverTest.php index faab116ef..625626b28 100644 --- a/tests/Composer/Test/Package/Archiver/TarArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php @@ -12,15 +12,15 @@ namespace Composer\Test\Package\Archiver; -use Composer\Package\Archiver\TarArchiver; +use Composer\Package\Archiver\PharArchiver; /** * @author Till Klampaeckel * @author Matthieu Moquet */ -class TarArchiverTest extends ArchiverTest +class PharArchiverTest extends ArchiverTest { - public function testArchive() + public function testTarArchive() { $this->setupGitRepo(); @@ -28,11 +28,27 @@ class TarArchiverTest extends ArchiverTest $target = sys_get_temp_dir().'/composer_archiver_test.tar'; // Test archive - $archiver = new TarArchiver(); + $archiver = new PharArchiver(); $archiver->archive($package->getSourceUrl(), $target, 'tar'); $this->assertFileExists($target); unlink($target); $this->removeGitRepo(); } + + public function testZipArchive() + { + $this->setupGitRepo(); + + $package = $this->setupPackage(); + $target = sys_get_temp_dir().'/composer_archiver_test.zip'; + + // Test archive + $archiver = new PharArchiver(); + $archiver->archive($package->getSourceUrl(), $target, 'zip'); + $this->assertFileExists($target); + + unlink($target); + $this->removeGitRepo(); + } } diff --git a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php b/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php deleted file mode 100644 index ed919b21a..000000000 --- a/tests/Composer/Test/Package/Archiver/ZipArchiverTest.php +++ /dev/null @@ -1,38 +0,0 @@ - - * 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\Archiver; - -use Composer\Package\Archiver\ZipArchiver; - -/** - * @author Till Klampaeckel - * @author Matthieu Moquet - */ -class ZipArchiverTest extends ArchiverTest -{ - public function testArchive() - { - $this->setupGitRepo(); - - $package = $this->setupPackage(); - $target = sys_get_temp_dir().'/composer_archiver_test.zip'; - - // Test archive - $archiver = new ZipArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'zip'); - $this->assertFileExists($target); - - unlink($target); - $this->removeGitRepo(); - } -} From d1d77dd13d9b7755d6e192c8ff8b608f1a95c391 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Tue, 28 Aug 2012 23:37:50 +0200 Subject: [PATCH 09/43] Fixed several typos - break at first archiver supports - use standard directory separator - change exception message - remove the BaseArchiver since tar & zip archivers have been merged - plus coding style --- .../Package/Archiver/ArchiveManager.php | 13 +++--- .../Package/Archiver/BaseArchiver.php | 46 ------------------- src/Composer/Package/Archiver/GitArchiver.php | 10 ++-- .../Package/Archiver/MercurialArchiver.php | 10 +--- .../Package/Archiver/PharArchiver.php | 30 ++++++++++-- 5 files changed, 37 insertions(+), 72 deletions(-) delete mode 100644 src/Composer/Package/Archiver/BaseArchiver.php diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index f5123f7ef..ddeb0dd4f 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -69,11 +69,10 @@ class ArchiveManager } $usableArchiver = null; - $sourceType = $package->getSourceType(); - foreach ($this->archivers as $archiver) { if ($archiver->supports($format, $package->getSourceType())) { $usableArchiver = $archiver; + break; } } @@ -82,20 +81,20 @@ class ArchiveManager throw new \RuntimeException(sprintf('No archiver found to support %s format', $format)); } - $filesystem = new Filesystem(); - $packageName = str_replace('/', DIRECTORY_SEPARATOR, $package->getUniqueName()); - // Directory used to download the sources - $sources = sys_get_temp_dir().DIRECTORY_SEPARATOR.$packageName; + $filesystem = new Filesystem(); + $packageName = $package->getUniqueName(); + $sources = sys_get_temp_dir().'/'.$packageName; $filesystem->ensureDirectoryExists($sources); // Archive filename - $target = $this->buildDir.DIRECTORY_SEPARATOR.$packageName.'.'.$format; + $target = $this->buildDir.'/'.$packageName.'.'.$format; $filesystem->ensureDirectoryExists(dirname($this->buildDir.$target)); // Download sources $this->downloadManager->download($package, $sources, true); + // Create the archive $sourceRef = $package->getSourceReference(); $usableArchiver->archive($sources, $target, $format, $sourceRef); } diff --git a/src/Composer/Package/Archiver/BaseArchiver.php b/src/Composer/Package/Archiver/BaseArchiver.php deleted file mode 100644 index 69fb892a8..000000000 --- a/src/Composer/Package/Archiver/BaseArchiver.php +++ /dev/null @@ -1,46 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\Archiver; - -use Composer\Package\BasePackage; -use Composer\Package\PackageInterface; - -/** - * @author Till Klampaeckel - * @author Matthieu Moquet - */ -abstract class BaseArchiver implements ArchiverInterface -{ - /** - * Create a PHAR archive. - * - * @param string $sources Path of the directory to archive - * @param string $target Path of the file archive to create - * @param int $format Format of the archive - */ - protected function createPharArchive($sources, $target, $format) - { - try { - $phar = new \PharData($target, null, null, $format); - $phar->buildFromDirectory($sources); - } catch (\UnexpectedValueException $e) { - $message = sprintf("Could not create archive '%s' from '%s': %s", - $target, - $sources, - $e->getMessage() - ); - - throw new \RuntimeException($message, $e->getCode(), $e); - } - } -} diff --git a/src/Composer/Package/Archiver/GitArchiver.php b/src/Composer/Package/Archiver/GitArchiver.php index 0a33ab756..922d9af19 100644 --- a/src/Composer/Package/Archiver/GitArchiver.php +++ b/src/Composer/Package/Archiver/GitArchiver.php @@ -34,7 +34,7 @@ class GitArchiver implements ArchiverInterface { // Since git-archive no longer works with a commit ID in git 1.7.10, // use by default the HEAD reference instead of the commit sha1 - if (null === $sourceRef || preg_match('/^[0-9a-f]{40}$/i',$sourceRef)) { + if (null === $sourceRef || preg_match('/^[0-9a-f]{40}$/i', $sourceRef)) { $sourceRef = 'HEAD'; } @@ -49,7 +49,7 @@ class GitArchiver implements ArchiverInterface if (0 !== $exitCode) { throw new \RuntimeException( - sprintf('The command `%s` returned %s', $command, $exitCode) + sprintf('Impossible to build the archive: `%s` returned %s', $command, $exitCode) ); } } @@ -59,10 +59,6 @@ class GitArchiver implements ArchiverInterface */ public function supports($format, $sourceType) { - return 'git' === $sourceType && in_array($format, array( - 'zip', - 'tar', - 'tgz', - )); + return 'git' === $sourceType && in_array($format, array('zip', 'tar', 'tgz')); } } \ No newline at end of file diff --git a/src/Composer/Package/Archiver/MercurialArchiver.php b/src/Composer/Package/Archiver/MercurialArchiver.php index 7de0430e1..9a67694df 100644 --- a/src/Composer/Package/Archiver/MercurialArchiver.php +++ b/src/Composer/Package/Archiver/MercurialArchiver.php @@ -47,7 +47,7 @@ class MercurialArchiver implements ArchiverInterface if (0 !== $exitCode) { throw new \RuntimeException( - sprintf('The command `%s` returned %s', $command, $exitCode) + sprintf('Impossible to build the archive: `%s` returned %s', $command, $exitCode) ); } } @@ -57,12 +57,6 @@ class MercurialArchiver implements ArchiverInterface */ public function supports($format, $sourceType) { - return 'hg' === $sourceType && in_array($format, array( - 'tar', - 'tbz2', - 'tgz', - 'uzip', - 'zip', - )); + return 'hg' === $sourceType && in_array($format, array('tar', 'tbz2', 'tgz', 'uzip', 'zip')); } } \ No newline at end of file diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index f8a369b52..02aaa0ab3 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -19,9 +19,9 @@ use Composer\Package\PackageInterface; * @author Till Klampaeckel * @author Matthieu Moquet */ -class PharArchiver extends BaseArchiver +class PharArchiver implements ArchiverInterface { - static public $formats = array( + static protected $formats = array( 'zip' => \Phar::ZIP, 'tar' => \Phar::TAR, ); @@ -31,7 +31,6 @@ class PharArchiver extends BaseArchiver */ public function archive($sources, $target, $format, $sourceRef = null) { - // source reference is useless for this archiver $this->createPharArchive($sources, $target, static::$formats[$format]); } @@ -40,6 +39,29 @@ class PharArchiver extends BaseArchiver */ public function supports($format, $sourceType) { - return in_array($format, array_keys(static::$formats)); + return isset(static::$formats[$format]); + } + + /** + * Create a PHAR archive. + * + * @param string $sources Path of the directory to archive + * @param string $target Path of the file archive to create + * @param int $format Format of the archive + */ + protected function createPharArchive($sources, $target, $format) + { + try { + $phar = new \PharData($target, null, null, $format); + $phar->buildFromDirectory($sources); + } catch (\UnexpectedValueException $e) { + $message = sprintf("Could not create archive '%s' from '%s': %s", + $target, + $sources, + $e->getMessage() + ); + + throw new \RuntimeException($message, $e->getCode(), $e); + } } } From 60b1cc7d24a1950e0c005fbed2d076aae6b2d7d3 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Tue, 28 Aug 2012 23:55:26 +0200 Subject: [PATCH 10/43] Create ArchiveManager with the Factory --- src/Composer/Factory.php | 25 +++++++++++++++++++ .../Package/Archiver/ArchiveManager.php | 10 ++------ .../Package/Archiver/ArchiveManagerTest.php | 10 ++++---- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index e9b7fbe55..458fdc63f 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -15,6 +15,7 @@ namespace Composer; use Composer\Config\JsonConfigSource; use Composer\Json\JsonFile; use Composer\IO\IOInterface; +use Composer\Package\Archiver; use Composer\Repository\ComposerRepository; use Composer\Repository\RepositoryManager; use Composer\Util\ProcessExecutor; @@ -317,6 +318,30 @@ class Factory return $dm; } + /** + * @param string $workDir Directory used to download sources + * @param Downloader\DownloadManager $dm Manager use to download sources + * + * @return Archiver\ArchiveManager + */ + public function createArchiveManager($workDir = null, DownloadManager $dm = null) + { + if (null === $dm) { + $dm = $this->createDownloadManager(new IO\NullIO()); + } + + if (null === $workDir) { + $workDir = sys_get_temp_dir(); + } + + $am = new Archiver\ArchiveManager($workDir, $dm); + $am->addArchiver(new Archiver\GitArchiver); + $am->addArchiver(new Archiver\MercurialArchiver); + $am->addArchiver(new Archiver\PharArchiver); + + return $am; + } + /** * @return Installer\InstallationManager */ diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index ddeb0dd4f..a70f31c54 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -34,16 +34,10 @@ class ArchiveManager * @param string $buildDir The directory used to build the archive * @param DownloadManager $downloadManager A manager used to download package sources */ - public function __construct($buildDir, DownloadManager $downloadManager = null) + public function __construct($buildDir, DownloadManager $downloadManager) { $this->buildDir = $buildDir; - - if (null !== $downloadManager) { - $this->downloadManager = $downloadManager; - } else { - $factory = new Factory(); - $this->downloadManager = $factory->createDownloadManager(new NullIO()); - } + $this->downloadManager = $downloadManager; } /** diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index 34c595991..626373a20 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -12,6 +12,8 @@ namespace Composer\Test\Package\Archiver; +use Composer\Factory; +use Composer\IO\NullIO; use Composer\Package\Archiver; use Composer\Package\Archiver\ArchiveManager; use Composer\Package\PackageInterface; @@ -30,12 +32,10 @@ class ArchiveManagerTest extends ArchiverTest { parent::setUp(); - $this->workDir = sys_get_temp_dir(); + $factory = new Factory(); - $this->manager = new ArchiveManager($this->workDir); - $this->manager->addArchiver(new Archiver\GitArchiver); - $this->manager->addArchiver(new Archiver\MercurialArchiver); - $this->manager->addArchiver(new Archiver\PharArchiver); + $this->workDir = sys_get_temp_dir(); + $this->manager = $factory->createArchiveManager($this->workDir); } public function testUnknownFormat() From c248115e046fe41787b3e659b5ebf7f0ec8aade2 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Sun, 21 Oct 2012 12:16:28 +0200 Subject: [PATCH 11/43] Fix how download manager is constructed This fixes tests due to upstream changes. The createDownloadManager in the Factory now takes the config as extra parameter. --- src/Composer/Factory.php | 12 ++++------- .../Package/Archiver/ArchiveManager.php | 20 +++++++++---------- .../Package/Archiver/ArchiveManagerTest.php | 18 ++++++++--------- 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 458fdc63f..a9132c743 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -319,22 +319,18 @@ class Factory } /** - * @param string $workDir Directory used to download sources * @param Downloader\DownloadManager $dm Manager use to download sources + * @param Config $config The configuration * * @return Archiver\ArchiveManager */ - public function createArchiveManager($workDir = null, DownloadManager $dm = null) + public function createArchiveManager(DownloadManager $dm = null, Config $config) { if (null === $dm) { - $dm = $this->createDownloadManager(new IO\NullIO()); + $dm = $this->createDownloadManager(new IO\NullIO(), $config); } - if (null === $workDir) { - $workDir = sys_get_temp_dir(); - } - - $am = new Archiver\ArchiveManager($workDir, $dm); + $am = new Archiver\ArchiveManager($dm); $am->addArchiver(new Archiver\GitArchiver); $am->addArchiver(new Archiver\MercurialArchiver); $am->addArchiver(new Archiver\PharArchiver); diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index a70f31c54..679d52586 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -24,19 +24,15 @@ use Composer\Util\Filesystem; */ class ArchiveManager { - protected $buildDir; - protected $downloadManager; protected $archivers = array(); /** - * @param string $buildDir The directory used to build the archive * @param DownloadManager $downloadManager A manager used to download package sources */ - public function __construct($buildDir, DownloadManager $downloadManager) + public function __construct(DownloadManager $downloadManager) { - $this->buildDir = $buildDir; $this->downloadManager = $downloadManager; } @@ -51,17 +47,19 @@ class ArchiveManager /** * Create an archive of the specified package. * - * @param PackageInterface $package The package to archive - * @param string $format The format of the archive (zip, tar, ...) + * @param PackageInterface $package The package to archive + * @param string $format The format of the archive (zip, tar, ...) + * @param string $targetDir The diretory where to build the archive * * @return string The path of the created archive */ - public function archive(PackageInterface $package, $format) + public function archive(PackageInterface $package, $format, $targetDir) { if (empty($format)) { throw new \InvalidArgumentException('Format must be specified'); } + // Search for the most appropriate archiver $usableArchiver = null; foreach ($this->archivers as $archiver) { if ($archiver->supports($format, $package->getSourceType())) { @@ -78,12 +76,12 @@ class ArchiveManager // Directory used to download the sources $filesystem = new Filesystem(); $packageName = $package->getUniqueName(); - $sources = sys_get_temp_dir().'/'.$packageName; + $sources = sys_get_temp_dir().'/composer_archiver/'.$packageName; $filesystem->ensureDirectoryExists($sources); // Archive filename - $target = $this->buildDir.'/'.$packageName.'.'.$format; - $filesystem->ensureDirectoryExists(dirname($this->buildDir.$target)); + $target = $targetDir.'/'.$packageName.'.'.$format; + $filesystem->ensureDirectoryExists(dirname($target)); // Download sources $this->downloadManager->download($package, $sources, true); diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index 626373a20..e289d7f9a 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -25,17 +25,15 @@ use Composer\Package\PackageInterface; class ArchiveManagerTest extends ArchiverTest { protected $manager; - - protected $workDir; + protected $targetDir; public function setUp() { parent::setUp(); $factory = new Factory(); - - $this->workDir = sys_get_temp_dir(); - $this->manager = $factory->createArchiveManager($this->workDir); + $this->manager = $factory->createArchiveManager(null, $factory->createConfig()); + $this->targetDir = sys_get_temp_dir().'/composer_archiver_tests'; } public function testUnknownFormat() @@ -44,7 +42,7 @@ class ArchiveManagerTest extends ArchiverTest $package = $this->setupPackage(); - $this->manager->archive($package, '__unknown_format__'); + $this->manager->archive($package, '__unknown_format__', $this->targetDir); } public function testArchiveTarWithVcs() @@ -55,7 +53,7 @@ class ArchiveManagerTest extends ArchiverTest // The package is source from git, // so it should `git archive --format tar` - $this->manager->archive($package, 'tar'); + $this->manager->archive($package, 'tar', $this->targetDir); $target = $this->getTargetName($package, 'tar'); $this->assertFileExists($target); @@ -71,7 +69,7 @@ class ArchiveManagerTest extends ArchiverTest $package = $this->setupPackage(); // This should use the TarArchiver - $this->manager->archive($package, 'tar'); + $this->manager->archive($package, 'tar', $this->targetDir); $package->setSourceType('__unknown_type__'); // disable VCS recognition $target = $this->getTargetName($package, 'tar'); @@ -83,8 +81,8 @@ class ArchiveManagerTest extends ArchiverTest protected function getTargetName(PackageInterface $package, $format) { - $packageName = str_replace('/', DIRECTORY_SEPARATOR, $package->getUniqueName()); - $target = $this->workDir.DIRECTORY_SEPARATOR.$packageName.'.'.$format; + $packageName = $package->getUniqueName(); + $target = $this->targetDir.'/'.$packageName.'.'.$format; return $target; } From 9d24e17003e932a6aec87d0d52996313d3c0527c Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Sun, 21 Oct 2012 16:17:43 +0200 Subject: [PATCH 12/43] Fix workflow & typos --- .../Package/Archiver/ArchiveManager.php | 8 ++--- .../Package/Archiver/ArchiverInterface.php | 2 +- src/Composer/Package/Archiver/GitArchiver.php | 2 +- .../Package/Archiver/MercurialArchiver.php | 2 +- .../Package/Archiver/PharArchiver.php | 30 ++++++------------- .../Test/Package/Archiver/ArchiverTest.php | 3 ++ 6 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 679d52586..ff8e2da95 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -76,18 +76,18 @@ class ArchiveManager // Directory used to download the sources $filesystem = new Filesystem(); $packageName = $package->getUniqueName(); - $sources = sys_get_temp_dir().'/composer_archiver/'.$packageName; - $filesystem->ensureDirectoryExists($sources); + $sourcePath = sys_get_temp_dir().'/composer_archiver/'.$packageName; + $filesystem->ensureDirectoryExists($sourcePath); // Archive filename $target = $targetDir.'/'.$packageName.'.'.$format; $filesystem->ensureDirectoryExists(dirname($target)); // Download sources - $this->downloadManager->download($package, $sources, true); + $this->downloadManager->download($package, $sourcePath, true); // Create the archive $sourceRef = $package->getSourceReference(); - $usableArchiver->archive($sources, $target, $format, $sourceRef); + $usableArchiver->archive($sourcePath, $target, $format, $sourceRef); } } diff --git a/src/Composer/Package/Archiver/ArchiverInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php index 596402154..38eb2cdd3 100644 --- a/src/Composer/Package/Archiver/ArchiverInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -23,7 +23,7 @@ interface ArchiverInterface /** * Create an archive from the sources. * - * @param string $source The sources directory + * @param string $sources The sources directory * @param string $target The target file * @param string $format The format used for archive * @param string $sourceRef The reference of the source to archive or null diff --git a/src/Composer/Package/Archiver/GitArchiver.php b/src/Composer/Package/Archiver/GitArchiver.php index 922d9af19..c18ab37a5 100644 --- a/src/Composer/Package/Archiver/GitArchiver.php +++ b/src/Composer/Package/Archiver/GitArchiver.php @@ -42,7 +42,7 @@ class GitArchiver implements ArchiverInterface 'git archive --format %s --output %s %s', $format, escapeshellarg($target), - $sourceRef + escapeshellarg($sourceRef) ); $exitCode = $this->process->execute($command, $output, $sources); diff --git a/src/Composer/Package/Archiver/MercurialArchiver.php b/src/Composer/Package/Archiver/MercurialArchiver.php index 9a67694df..b21750b45 100644 --- a/src/Composer/Package/Archiver/MercurialArchiver.php +++ b/src/Composer/Package/Archiver/MercurialArchiver.php @@ -38,7 +38,7 @@ class MercurialArchiver implements ArchiverInterface $command = sprintf( 'hg archive --rev %s --type %s %s', - $sourceRef, + escapeshellarg($sourceRef), $format, escapeshellarg($target) ); diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 02aaa0ab3..24e376d2a 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -30,29 +30,9 @@ class PharArchiver implements ArchiverInterface * {@inheritdoc} */ public function archive($sources, $target, $format, $sourceRef = null) - { - $this->createPharArchive($sources, $target, static::$formats[$format]); - } - - /** - * {@inheritdoc} - */ - public function supports($format, $sourceType) - { - return isset(static::$formats[$format]); - } - - /** - * Create a PHAR archive. - * - * @param string $sources Path of the directory to archive - * @param string $target Path of the file archive to create - * @param int $format Format of the archive - */ - protected function createPharArchive($sources, $target, $format) { try { - $phar = new \PharData($target, null, null, $format); + $phar = new \PharData($target, null, null, static::$formats[$format]); $phar->buildFromDirectory($sources); } catch (\UnexpectedValueException $e) { $message = sprintf("Could not create archive '%s' from '%s': %s", @@ -64,4 +44,12 @@ class PharArchiver implements ArchiverInterface throw new \RuntimeException($message, $e->getCode(), $e); } } + + /** + * {@inheritdoc} + */ + public function supports($format, $sourceType) + { + return isset(static::$formats[$format]); + } } diff --git a/tests/Composer/Test/Package/Archiver/ArchiverTest.php b/tests/Composer/Test/Package/Archiver/ArchiverTest.php index 4b8d91c51..62e4bb914 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiverTest.php @@ -57,16 +57,19 @@ abstract class ArchiverTest extends \PHPUnit_Framework_TestCase $result = $this->process->execute('git init -q'); if ($result > 0) { + chdir($currentWorkDir); throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); } $result = file_put_contents('b', 'a'); if (false === $result) { + chdir($currentWorkDir); throw new \RuntimeException('Could not save file.'); } $result = $this->process->execute('git add b && git commit -m "commit b" -q'); if ($result > 0) { + chdir($currentWorkDir); throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); } From a2b404e421a1b892f99c529864dffb5cd95d6698 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Sun, 21 Oct 2012 18:23:35 +0200 Subject: [PATCH 13/43] Cleaned archiver tests --- .../Package/Archiver/ArchiveManagerTest.php | 51 +++++++++++-------- .../Test/Package/Archiver/ArchiverTest.php | 40 +++------------ .../Test/Package/Archiver/GitArchiverTest.php | 37 ++++++++++++-- .../Archiver/MercurialArchiverTest.php | 28 ++++------ .../Package/Archiver/PharArchiverTest.php | 27 +++++++--- 5 files changed, 102 insertions(+), 81 deletions(-) diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index e289d7f9a..d8f864bd2 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -33,7 +33,7 @@ class ArchiveManagerTest extends ArchiverTest $factory = new Factory(); $this->manager = $factory->createArchiveManager(null, $factory->createConfig()); - $this->targetDir = sys_get_temp_dir().'/composer_archiver_tests'; + $this->targetDir = $this->testDir.'/composer_archiver_tests'; } public function testUnknownFormat() @@ -45,7 +45,7 @@ class ArchiveManagerTest extends ArchiverTest $this->manager->archive($package, '__unknown_format__', $this->targetDir); } - public function testArchiveTarWithVcs() + public function testArchiveTar() { $this->setupGitRepo(); @@ -59,24 +59,6 @@ class ArchiveManagerTest extends ArchiverTest $this->assertFileExists($target); unlink($target); - $this->removeGitRepo(); - } - - public function testArchiveTarWithoutVcs() - { - $this->setupGitRepo(); - - $package = $this->setupPackage(); - - // This should use the TarArchiver - $this->manager->archive($package, 'tar', $this->targetDir); - - $package->setSourceType('__unknown_type__'); // disable VCS recognition - $target = $this->getTargetName($package, 'tar'); - $this->assertFileExists($target); - - unlink($target); - $this->removeGitRepo(); } protected function getTargetName(PackageInterface $package, $format) @@ -86,4 +68,33 @@ class ArchiveManagerTest extends ArchiverTest return $target; } + + /** + * Create local git repository to run tests against! + */ + protected function setupGitRepo() + { + $currentWorkDir = getcwd(); + chdir($this->testDir); + + $result = $this->process->execute('git init -q'); + if ($result > 0) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); + } + + $result = file_put_contents('b', 'a'); + if (false === $result) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not save file.'); + } + + $result = $this->process->execute('git add b && git commit -m "commit b" -q'); + if ($result > 0) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); + } + + chdir($currentWorkDir); + } } diff --git a/tests/Composer/Test/Package/Archiver/ArchiverTest.php b/tests/Composer/Test/Package/Archiver/ArchiverTest.php index 62e4bb914..993244788 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiverTest.php @@ -41,46 +41,20 @@ abstract class ArchiverTest extends \PHPUnit_Framework_TestCase { $this->filesystem = new Filesystem(); $this->process = new ProcessExecutor(); - $this->testDir = sys_get_temp_dir().'/composer_archivertest_git_repository'.mt_rand(); - } - - /** - * Create local git repository to run tests against! - */ - protected function setupGitRepo() - { - $this->filesystem->removeDirectory($this->testDir); + $this->testDir = sys_get_temp_dir().'/composer_archiver_test_'.mt_rand(); $this->filesystem->ensureDirectoryExists($this->testDir); - - $currentWorkDir = getcwd(); - chdir($this->testDir); - - $result = $this->process->execute('git init -q'); - if ($result > 0) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); - } - - $result = file_put_contents('b', 'a'); - if (false === $result) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not save file.'); - } - - $result = $this->process->execute('git add b && git commit -m "commit b" -q'); - if ($result > 0) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); - } - - chdir($currentWorkDir); } - protected function removeGitRepo() + public function tearDown() { $this->filesystem->removeDirectory($this->testDir); } + /** + * Util method to quickly setup a package using the source path built. + * + * @return \Composer\Package\Package + */ protected function setupPackage() { $package = new Package('archivertest/archivertest', 'master', 'master'); diff --git a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php index 9ddb9d4f4..6934bd2de 100644 --- a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php @@ -20,10 +20,12 @@ use Composer\Package\Archiver\GitArchiver; */ class GitArchiverTest extends ArchiverTest { + protected $targetFile; + public function testZipArchive() { + // Set up repository $this->setupGitRepo(); - $package = $this->setupPackage(); $target = sys_get_temp_dir().'/composer_archiver_test.zip'; @@ -33,13 +35,12 @@ class GitArchiverTest extends ArchiverTest $this->assertFileExists($target); unlink($target); - $this->removeGitRepo(); } public function testTarArchive() { + // Set up repository $this->setupGitRepo(); - $package = $this->setupPackage(); $target = sys_get_temp_dir().'/composer_archiver_test.tar'; @@ -49,6 +50,34 @@ class GitArchiverTest extends ArchiverTest $this->assertFileExists($target); unlink($target); - $this->removeGitRepo(); + } + + /** + * Create local git repository to run tests against! + */ + protected function setupGitRepo() + { + $currentWorkDir = getcwd(); + chdir($this->testDir); + + $result = $this->process->execute('git init -q'); + if ($result > 0) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); + } + + $result = file_put_contents('b', 'a'); + if (false === $result) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not save file.'); + } + + $result = $this->process->execute('git add b && git commit -m "commit b" -q'); + if ($result > 0) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); + } + + chdir($currentWorkDir); } } diff --git a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php index 4934572da..e3b316a6b 100644 --- a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php @@ -23,9 +23,9 @@ class MercurialArchiverTest extends ArchiverTest { public function testZipArchive() { + // Set up repository $this->setupMercurialRepo(); - - $package = $this->setupMercurialPackage(); + $package = $this->setupPackage(); $target = sys_get_temp_dir().'/composer_archiver_test.zip'; // Test archive @@ -34,14 +34,13 @@ class MercurialArchiverTest extends ArchiverTest $this->assertFileExists($target); unlink($target); - $this->removeMercurialRepo(); } public function testTarArchive() { + // Set up repository $this->setupMercurialRepo(); - - $package = $this->setupMercurialPackage(); + $package = $this->setupPackage(); $target = sys_get_temp_dir().'/composer_archiver_test.tar'; // Test archive @@ -50,47 +49,40 @@ class MercurialArchiverTest extends ArchiverTest $this->assertFileExists($target); unlink($target); - $this->removeMercurialRepo(); } /** - * Create local git repository to run tests against! + * Create local mercurial repository to run tests against! */ protected function setupMercurialRepo() { - $this->filesystem->removeDirectory($this->testDir); - $this->filesystem->ensureDirectoryExists($this->testDir); - $currentWorkDir = getcwd(); chdir($this->testDir); $result = $this->process->execute('hg init -q'); if ($result > 0) { + chdir($currentWorkDir); throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); } $result = file_put_contents('b', 'a'); if (false === $result) { + chdir($currentWorkDir); throw new \RuntimeException('Could not save file.'); } $result = $this->process->execute('hg add b && hg commit -m "commit b" --config ui.username=test -q'); if ($result > 0) { + chdir($currentWorkDir); throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); } chdir($currentWorkDir); } - protected function removeMercurialRepo() - { - $this->filesystem->removeDirectory($this->testDir); - } - - protected function setupMercurialPackage() + protected function setupPackage() { - $package = new Package('archivertest/archivertest', 'master', 'master'); - $package->setSourceUrl(realpath($this->testDir)); + $package = parent::setupPackage(); $package->setSourceReference('default'); $package->setSourceType('hg'); diff --git a/tests/Composer/Test/Package/Archiver/PharArchiverTest.php b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php index 625626b28..0e5099668 100644 --- a/tests/Composer/Test/Package/Archiver/PharArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php @@ -22,8 +22,8 @@ class PharArchiverTest extends ArchiverTest { public function testTarArchive() { - $this->setupGitRepo(); - + // Set up repository + $this->setupDummyRepo(); $package = $this->setupPackage(); $target = sys_get_temp_dir().'/composer_archiver_test.tar'; @@ -33,13 +33,12 @@ class PharArchiverTest extends ArchiverTest $this->assertFileExists($target); unlink($target); - $this->removeGitRepo(); } public function testZipArchive() { - $this->setupGitRepo(); - + // Set up repository + $this->setupDummyRepo(); $package = $this->setupPackage(); $target = sys_get_temp_dir().'/composer_archiver_test.zip'; @@ -49,6 +48,22 @@ class PharArchiverTest extends ArchiverTest $this->assertFileExists($target); unlink($target); - $this->removeGitRepo(); + } + + /** + * Create a local dummy repository to run tests against! + */ + protected function setupDummyRepo() + { + $currentWorkDir = getcwd(); + chdir($this->testDir); + + $result = file_put_contents('b', 'a'); + if (false === $result) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not save file.'); + } + + chdir($currentWorkDir); } } From ba510276803dbdedc609a2c5846b4511bdb7b4c6 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 09:39:11 +0100 Subject: [PATCH 14/43] Reorder ArchiveManager parameters to make the download manager optional --- src/Composer/Factory.php | 4 ++-- tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index a9132c743..92e89f5a5 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -319,12 +319,12 @@ class Factory } /** - * @param Downloader\DownloadManager $dm Manager use to download sources * @param Config $config The configuration + * @param Downloader\DownloadManager $dm Manager use to download sources * * @return Archiver\ArchiveManager */ - public function createArchiveManager(DownloadManager $dm = null, Config $config) + public function createArchiveManager(Config $config, DownloadManager $dm = null) { if (null === $dm) { $dm = $this->createDownloadManager(new IO\NullIO(), $config); diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index d8f864bd2..f1eb73be1 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -32,7 +32,7 @@ class ArchiveManagerTest extends ArchiverTest parent::setUp(); $factory = new Factory(); - $this->manager = $factory->createArchiveManager(null, $factory->createConfig()); + $this->manager = $factory->createArchiveManager($factory->createConfig()); $this->targetDir = $this->testDir.'/composer_archiver_tests'; } From bcbc50c0d60bd7dff181a7838c29e83b57c8bab0 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 09:45:51 +0100 Subject: [PATCH 15/43] Git can handle commit references in git archive just fine so use them --- src/Composer/Package/Archiver/GitArchiver.php | 4 +--- .../Test/Package/Archiver/GitArchiverTest.php | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Composer/Package/Archiver/GitArchiver.php b/src/Composer/Package/Archiver/GitArchiver.php index c18ab37a5..1279a8c80 100644 --- a/src/Composer/Package/Archiver/GitArchiver.php +++ b/src/Composer/Package/Archiver/GitArchiver.php @@ -32,9 +32,7 @@ class GitArchiver implements ArchiverInterface */ public function archive($sources, $target, $format, $sourceRef = null) { - // Since git-archive no longer works with a commit ID in git 1.7.10, - // use by default the HEAD reference instead of the commit sha1 - if (null === $sourceRef || preg_match('/^[0-9a-f]{40}$/i', $sourceRef)) { + if (null === $sourceRef) { $sourceRef = 'HEAD'; } diff --git a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php index 6934bd2de..587ed9158 100644 --- a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php @@ -31,7 +31,7 @@ class GitArchiverTest extends ArchiverTest // Test archive $archiver = new GitArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'zip', 'master'); + $archiver->archive($package->getSourceUrl(), $target, 'zip', 'master^1'); $this->assertFileExists($target); unlink($target); @@ -46,7 +46,7 @@ class GitArchiverTest extends ArchiverTest // Test archive $archiver = new GitArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'tar', 'master'); + $archiver->archive($package->getSourceUrl(), $target, 'tar', 'master^1'); $this->assertFileExists($target); unlink($target); @@ -78,6 +78,18 @@ class GitArchiverTest extends ArchiverTest throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); } + $result = file_put_contents('d', 'c'); + if (false === $result) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not save file.'); + } + + $result = $this->process->execute('git add d && git commit -m "commit d" -q'); + if ($result > 0) { + chdir($currentWorkDir); + throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); + } + chdir($currentWorkDir); } } From 33828b38dff2435d0ce6a46887ba7c66f5015a1d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 11:36:49 +0100 Subject: [PATCH 16/43] Use a saner file name for package archives --- src/Composer/Package/Archiver/ArchiveManager.php | 5 +++-- tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index ff8e2da95..5da4d89ae 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -75,12 +75,13 @@ class ArchiveManager // Directory used to download the sources $filesystem = new Filesystem(); - $packageName = $package->getUniqueName(); + $packageName = preg_replace('#[^a-z0-9-_.]#i', '-', $package->getPrettyString()); $sourcePath = sys_get_temp_dir().'/composer_archiver/'.$packageName; $filesystem->ensureDirectoryExists($sourcePath); // Archive filename - $target = $targetDir.'/'.$packageName.'.'.$format; + $filesystem->ensureDirectoryExists($targetDir); + $target = realpath($targetDir).'/'.$packageName.'.'.$format; $filesystem->ensureDirectoryExists(dirname($target)); // Download sources diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index f1eb73be1..54caa0186 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -63,7 +63,7 @@ class ArchiveManagerTest extends ArchiverTest protected function getTargetName(PackageInterface $package, $format) { - $packageName = $package->getUniqueName(); + $packageName = preg_replace('#[^a-z0-9-_.]#i', '-', $package->getPrettyString()); $target = $this->targetDir.'/'.$packageName.'.'.$format; return $target; From 526f48ecb88e647a3965f7973fb6078625530ed4 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 11:37:27 +0100 Subject: [PATCH 17/43] Implement a basic archive command It allows creating archives with the archive manager given a package/version pair. --- src/Composer/Command/ArchiveCommand.php | 119 ++++++++++++++++++++++++ src/Composer/Console/Application.php | 1 + src/Composer/Factory.php | 2 +- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/Composer/Command/ArchiveCommand.php diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php new file mode 100644 index 000000000..c305ce8da --- /dev/null +++ b/src/Composer/Command/ArchiveCommand.php @@ -0,0 +1,119 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Composer\Factory; +use Composer\IO\IOInterface; +use Composer\DependencyResolver\Pool; +use Composer\Package\LinkConstraint\VersionConstraint; +use Composer\Repository\CompositeRepository; + +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Install a package as new project into new directory. + * + * @author Nils Adermann + */ +class ArchiveCommand extends Command +{ + protected function configure() + { + $this + ->setName('archive') + ->setDescription('Create an archive of this composer package') + ->setDefinition(array( + new InputArgument('package', InputArgument::REQUIRED, 'The package to archive'), + new InputArgument('version', InputArgument::OPTIONAL, 'The package version to archive'), + new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip', 'tar'), + new InputOption('dir', false, InputOption::VALUE_REQUIRED, 'Write the archive to this directory', '.'), + )) + ->setHelp(<<archive command creates an archive of the specified format +containing the files and directories of the Composer project or the specified +package and writes it to the specified directory. + +php composer.phar archive [--format=zip] [--dir=/foo] package version + +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + return $this->archive( + $this->getIO(), + $input->getArgument('package'), + $input->getArgument('version'), + $input->getOption('format'), + $input->getOption('dir') + ); + } + + public function archive(IOInterface $io, $packageName, $version = false, $format = 'tar', $dest = '.') + { + $config = Factory::createConfig(); + $factory = new Factory; + $archiveManager = $factory->createArchiveManager($config); + + $package = $this->selectPackage($io, $packageName, $version); + + if (!$package) { + return 1; + } + + $io->write('Creating the archive.'); + $archiveManager->archive($package, $format, $dest); + + return 0; + } + + protected function selectPackage(IOInterface $io, $packageName, $version = false) + { + $io->write('Searching for the specified package.'); + + if ($composer = $this->getComposer(false)) { + $localRepo = $composer->getRepositoryManager()->getLocalRepository(); + $repos = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories())); + } else { + $defaultRepos = Factory::createDefaultRepositories($this->getIO()); + $output->writeln('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos))); + $repos = new CompositeRepository($defaultRepos); + } + + $pool = new Pool(); + $pool->addRepository($repos); + + $constraint = ($version) ? new VersionConstraint('>=', $version) : null; + $packages = $pool->whatProvides($packageName, $constraint); + + if (count($packages) > 1) { + $package = $packages[0]; + $io->write('Found multiple matches, selected '.$package.'.'); + $io->write('Alternatives were '.implode(', ', $packages).'.'); + $io->write('Please use a more specific constraint to pick a different package.'); + } elseif ($packages) { + $package = $packages[0]; + $io->write('Found an exact match '.$package.'.'); + } else { + $io->write('Could not find a package matching '.$packageName.'.'); + return false; + } + + return $package; + } +} diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 55d078cee..40dfd9eb9 100755 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -194,6 +194,7 @@ class Application extends BaseApplication $commands[] = new Command\RequireCommand(); $commands[] = new Command\DumpAutoloadCommand(); $commands[] = new Command\StatusCommand(); + $commands[] = new Command\ArchiveCommand(); if ('phar:' === substr(__FILE__, 0, 5)) { $commands[] = new Command\SelfUpdateCommand(); diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 92e89f5a5..b77d6bb1d 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -324,7 +324,7 @@ class Factory * * @return Archiver\ArchiveManager */ - public function createArchiveManager(Config $config, DownloadManager $dm = null) + public function createArchiveManager(Config $config, Downloader\DownloadManager $dm = null) { if (null === $dm) { $dm = $this->createDownloadManager(new IO\NullIO(), $config); From 3e26502561af34a13bcda568bff4d27e6237783f Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 15:36:47 +0100 Subject: [PATCH 18/43] Remove Mercurial and Git Archivers as they cannot implement exclude rules --- src/Composer/Factory.php | 2 - src/Composer/Package/Archiver/GitArchiver.php | 62 ------------ .../Package/Archiver/MercurialArchiver.php | 62 ------------ .../Test/Package/Archiver/GitArchiverTest.php | 95 ------------------- .../Archiver/MercurialArchiverTest.php | 91 ------------------ 5 files changed, 312 deletions(-) delete mode 100644 src/Composer/Package/Archiver/GitArchiver.php delete mode 100644 src/Composer/Package/Archiver/MercurialArchiver.php delete mode 100644 tests/Composer/Test/Package/Archiver/GitArchiverTest.php delete mode 100644 tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index b77d6bb1d..9234f46f8 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -331,8 +331,6 @@ class Factory } $am = new Archiver\ArchiveManager($dm); - $am->addArchiver(new Archiver\GitArchiver); - $am->addArchiver(new Archiver\MercurialArchiver); $am->addArchiver(new Archiver\PharArchiver); return $am; diff --git a/src/Composer/Package/Archiver/GitArchiver.php b/src/Composer/Package/Archiver/GitArchiver.php deleted file mode 100644 index 1279a8c80..000000000 --- a/src/Composer/Package/Archiver/GitArchiver.php +++ /dev/null @@ -1,62 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\Archiver; - -use Composer\Util\ProcessExecutor; - -/** - * @author Till Klampaeckel - * @author Matthieu Moquet - */ -class GitArchiver implements ArchiverInterface -{ - protected $process; - - public function __construct($process = null) - { - $this->process = $process ?: new ProcessExecutor(); - } - - /** - * {@inheritdoc} - */ - public function archive($sources, $target, $format, $sourceRef = null) - { - if (null === $sourceRef) { - $sourceRef = 'HEAD'; - } - - $command = sprintf( - 'git archive --format %s --output %s %s', - $format, - escapeshellarg($target), - escapeshellarg($sourceRef) - ); - - $exitCode = $this->process->execute($command, $output, $sources); - - if (0 !== $exitCode) { - throw new \RuntimeException( - sprintf('Impossible to build the archive: `%s` returned %s', $command, $exitCode) - ); - } - } - - /** - * {@inheritdoc} - */ - public function supports($format, $sourceType) - { - return 'git' === $sourceType && in_array($format, array('zip', 'tar', 'tgz')); - } -} \ No newline at end of file diff --git a/src/Composer/Package/Archiver/MercurialArchiver.php b/src/Composer/Package/Archiver/MercurialArchiver.php deleted file mode 100644 index b21750b45..000000000 --- a/src/Composer/Package/Archiver/MercurialArchiver.php +++ /dev/null @@ -1,62 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Package\Archiver; - -use Composer\Util\ProcessExecutor; - -/** - * @author Till Klampaeckel - * @author Matthieu Moquet - */ -class MercurialArchiver implements ArchiverInterface -{ - protected $process; - - public function __construct($process = null) - { - $this->process = $process ?: new ProcessExecutor(); - } - - /** - * {@inheritdoc} - */ - public function archive($sources, $target, $format, $sourceRef = null) - { - if (null === $sourceRef) { - $sourceRef = 'default'; - } - - $command = sprintf( - 'hg archive --rev %s --type %s %s', - escapeshellarg($sourceRef), - $format, - escapeshellarg($target) - ); - - $exitCode = $this->process->execute($command, $output, $sources); - - if (0 !== $exitCode) { - throw new \RuntimeException( - sprintf('Impossible to build the archive: `%s` returned %s', $command, $exitCode) - ); - } - } - - /** - * {@inheritdoc} - */ - public function supports($format, $sourceType) - { - return 'hg' === $sourceType && in_array($format, array('tar', 'tbz2', 'tgz', 'uzip', 'zip')); - } -} \ No newline at end of file diff --git a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php b/tests/Composer/Test/Package/Archiver/GitArchiverTest.php deleted file mode 100644 index 587ed9158..000000000 --- a/tests/Composer/Test/Package/Archiver/GitArchiverTest.php +++ /dev/null @@ -1,95 +0,0 @@ - - * 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\Archiver; - -use Composer\Package\Archiver\GitArchiver; - -/** - * @author Till Klampaeckel - * @author Matthieu Moquet - */ -class GitArchiverTest extends ArchiverTest -{ - protected $targetFile; - - public function testZipArchive() - { - // Set up repository - $this->setupGitRepo(); - $package = $this->setupPackage(); - $target = sys_get_temp_dir().'/composer_archiver_test.zip'; - - // Test archive - $archiver = new GitArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'zip', 'master^1'); - $this->assertFileExists($target); - - unlink($target); - } - - public function testTarArchive() - { - // Set up repository - $this->setupGitRepo(); - $package = $this->setupPackage(); - $target = sys_get_temp_dir().'/composer_archiver_test.tar'; - - // Test archive - $archiver = new GitArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'tar', 'master^1'); - $this->assertFileExists($target); - - unlink($target); - } - - /** - * Create local git repository to run tests against! - */ - protected function setupGitRepo() - { - $currentWorkDir = getcwd(); - chdir($this->testDir); - - $result = $this->process->execute('git init -q'); - if ($result > 0) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); - } - - $result = file_put_contents('b', 'a'); - if (false === $result) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not save file.'); - } - - $result = $this->process->execute('git add b && git commit -m "commit b" -q'); - if ($result > 0) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); - } - - $result = file_put_contents('d', 'c'); - if (false === $result) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not save file.'); - } - - $result = $this->process->execute('git add d && git commit -m "commit d" -q'); - if ($result > 0) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); - } - - chdir($currentWorkDir); - } -} diff --git a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php b/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php deleted file mode 100644 index e3b316a6b..000000000 --- a/tests/Composer/Test/Package/Archiver/MercurialArchiverTest.php +++ /dev/null @@ -1,91 +0,0 @@ - - * 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\Archiver; - -use Composer\Package\Archiver\MercurialArchiver; -use Composer\Package\Package; - -/** - * @author Matthieu Moquet - * @author Till Klampaeckel - */ -class MercurialArchiverTest extends ArchiverTest -{ - public function testZipArchive() - { - // Set up repository - $this->setupMercurialRepo(); - $package = $this->setupPackage(); - $target = sys_get_temp_dir().'/composer_archiver_test.zip'; - - // Test archive - $archiver = new MercurialArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'zip', 'default'); - $this->assertFileExists($target); - - unlink($target); - } - - public function testTarArchive() - { - // Set up repository - $this->setupMercurialRepo(); - $package = $this->setupPackage(); - $target = sys_get_temp_dir().'/composer_archiver_test.tar'; - - // Test archive - $archiver = new MercurialArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'tar', 'default'); - $this->assertFileExists($target); - - unlink($target); - } - - /** - * Create local mercurial repository to run tests against! - */ - protected function setupMercurialRepo() - { - $currentWorkDir = getcwd(); - chdir($this->testDir); - - $result = $this->process->execute('hg init -q'); - if ($result > 0) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); - } - - $result = file_put_contents('b', 'a'); - if (false === $result) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not save file.'); - } - - $result = $this->process->execute('hg add b && hg commit -m "commit b" --config ui.username=test -q'); - if ($result > 0) { - chdir($currentWorkDir); - throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); - } - - chdir($currentWorkDir); - } - - protected function setupPackage() - { - $package = parent::setupPackage(); - $package->setSourceReference('default'); - $package->setSourceType('hg'); - - return $package; - } -} From afcdad4b2381957052c81837a7353a19b08a0ec9 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 15:45:58 +0100 Subject: [PATCH 19/43] Define an option to exclude files in the archive command --- doc/04-schema.md | 25 ++++++++ res/composer-schema.json | 14 +++++ src/Composer/Package/AliasPackage.php | 4 ++ .../Package/Archiver/ArchiveManager.php | 2 +- .../Package/Archiver/ArchiverInterface.php | 3 +- .../Package/Archiver/PharArchiver.php | 59 ++++++++++++++++++- src/Composer/Package/Dumper/ArrayDumper.php | 4 ++ src/Composer/Package/Loader/ArrayLoader.php | 4 ++ src/Composer/Package/Package.php | 19 ++++++ src/Composer/Package/PackageInterface.php | 7 +++ .../Package/Archiver/PharArchiverTest.php | 21 +++++-- .../Test/Package/Dumper/ArrayDumperTest.php | 8 +++ .../Test/Package/Loader/ArrayLoaderTest.php | 3 + .../Loader/ValidatingArrayLoaderTest.php | 3 + 14 files changed, 168 insertions(+), 8 deletions(-) diff --git a/doc/04-schema.md b/doc/04-schema.md index a222c06e0..f7de1a3df 100644 --- a/doc/04-schema.md +++ b/doc/04-schema.md @@ -656,4 +656,29 @@ See [Vendor Binaries](articles/vendor-binaries.md) for more details. Optional. +### archive + +A set of options for creating package archives. + +The following options are supported: + +* **exclude:** Allows configuring a list of patterns for excluded paths. The + pattern syntax matches .gitignore files. A leading exclamation mark (!) will + result in any matching files to be included even if a previous pattern + excluded them. A leading slash will only match at the beginning of the project + relative path. An asterisk will not expand to a directory separator. + +Example: + + { + "archive": { + "exclude": ["/foo/bar", "baz", "/*.test", "!/foo/bar/baz"] + } + } + +The example will include `/dir/foo/bar/file`, `/foo/bar/baz`, `/file.php`, +`/foo/my.test` but it will exclude `/foo/bar/any`, `/foo/baz`, and `/my.test`. + +Optional. + ← [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) → diff --git a/res/composer-schema.json b/res/composer-schema.json index aefaa0463..3eb22cff0 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -202,6 +202,20 @@ } } }, + "archive": { + "type": ["object"], + "description": "Options for creating package archives for distribution.", + "properties": { + "exclude": { + "type": "array", + "description": "A list of paths to exclude." + }, + "include": { + "type": "array", + "description": "A list of paths to include even though an exclude rule exists for them." + } + } + }, "repositories": { "type": ["object", "array"], "description": "A set of additional repositories where packages can be found.", diff --git a/src/Composer/Package/AliasPackage.php b/src/Composer/Package/AliasPackage.php index e2f748092..7f16aaac9 100644 --- a/src/Composer/Package/AliasPackage.php +++ b/src/Composer/Package/AliasPackage.php @@ -311,6 +311,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface { return $this->aliasOf->getNotificationUrl(); } + public function getArchiveExcludes() + { + return $this->aliasOf->getArchiveExcludes(); + } public function __toString() { return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')'; diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 5da4d89ae..c3a2c818e 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -89,6 +89,6 @@ class ArchiveManager // Create the archive $sourceRef = $package->getSourceReference(); - $usableArchiver->archive($sourcePath, $target, $format, $sourceRef); + $usableArchiver->archive($sourcePath, $target, $format, $sourceRef, $package->getArchiveExcludes()); } } diff --git a/src/Composer/Package/Archiver/ArchiverInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php index 38eb2cdd3..7a688d251 100644 --- a/src/Composer/Package/Archiver/ArchiverInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -28,8 +28,9 @@ interface ArchiverInterface * @param string $format The format used for archive * @param string $sourceRef The reference of the source to archive or null * for the current reference + * @param array $excludes A list of patterns for files to exclude */ - public function archive($sources, $target, $format, $sourceRef = null); + public function archive($sources, $target, $format, $sourceRef = null, $excludes = array()); /** * Format supported by the archiver. diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 24e376d2a..950832ce2 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -15,8 +15,11 @@ namespace Composer\Package\Archiver; use Composer\Package\BasePackage; use Composer\Package\PackageInterface; +use Symfony\Component\Finder; + /** * @author Till Klampaeckel + * @author Nils Adermann * @author Matthieu Moquet */ class PharArchiver implements ArchiverInterface @@ -29,11 +32,34 @@ class PharArchiver implements ArchiverInterface /** * {@inheritdoc} */ - public function archive($sources, $target, $format, $sourceRef = null) + public function archive($sources, $target, $format, $sourceRef = null, $excludes = array()) { + $sources = realpath($sources); + + $excludePatterns = $this->generatePatterns($excludes); + try { + if (file_exists($target)) { + unlink($target); + } $phar = new \PharData($target, null, null, static::$formats[$format]); - $phar->buildFromDirectory($sources); + $finder = new Finder\Finder(); + $finder + ->in($sources) + ->filter(function (\SplFileInfo $file) use ($sources, $excludePatterns) { + $relativePath = preg_replace('#^'.preg_quote($sources, '#').'#', '', $file->getRealPath()); + + $include = true; + foreach ($excludePatterns as $patternData) { + list($pattern, $negate) = $patternData; + if (preg_match($pattern, $relativePath)) { + $include = $negate; + } + } + return $include; + }) + ->ignoreVCS(true); + $phar->buildFromIterator($finder->getIterator(), $sources); } catch (\UnexpectedValueException $e) { $message = sprintf("Could not create archive '%s' from '%s': %s", $target, @@ -45,6 +71,35 @@ class PharArchiver implements ArchiverInterface } } + /** + * Generates a set of PCRE patterns from a set of exclude rules. + * + * @param array $rules A list of exclude rules similar to gitignore syntax + */ + protected function generatePatterns($rules) + { + $patterns = array(); + foreach ($rules as $rule) { + $negate = false; + $pattern = '#'; + + if (strlen($rule) && $rule[0] === '!') { + $negate = true; + $rule = substr($rule, 1); + } + + if (strlen($rule) && $rule[0] === '/') { + $pattern .= '^/'; + $rule = substr($rule, 1); + } + + $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2); + $patterns[] = array($pattern . '#', $negate); + } + + return $patterns; + } + /** * {@inheritdoc} */ diff --git a/src/Composer/Package/Dumper/ArrayDumper.php b/src/Composer/Package/Dumper/ArrayDumper.php index 22d62381b..bca932d71 100644 --- a/src/Composer/Package/Dumper/ArrayDumper.php +++ b/src/Composer/Package/Dumper/ArrayDumper.php @@ -58,6 +58,10 @@ class ArrayDumper $data['dist']['shasum'] = $package->getDistSha1Checksum(); } + if ($package->getArchiveExcludes()) { + $data['archive']['exclude'] = $package->getArchiveExcludes(); + } + foreach (BasePackage::$supportedLinkTypes as $type => $opts) { if ($links = $package->{'get'.ucfirst($opts['method'])}()) { foreach ($links as $link) { diff --git a/src/Composer/Package/Loader/ArrayLoader.php b/src/Composer/Package/Loader/ArrayLoader.php index 559fd86fe..ffb0aa126 100644 --- a/src/Composer/Package/Loader/ArrayLoader.php +++ b/src/Composer/Package/Loader/ArrayLoader.php @@ -150,6 +150,10 @@ class ArrayLoader implements LoaderInterface $package->setNotificationUrl($config['notification-url']); } + if (!empty($config['archive']['exclude'])) { + $package->setArchiveExcludes($config['archive']['exclude']); + } + if ($package instanceof Package\CompletePackageInterface) { if (isset($config['scripts']) && is_array($config['scripts'])) { foreach ($config['scripts'] as $event => $listeners) { diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index ddd4b4ec5..be57df64f 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -51,6 +51,7 @@ class Package extends BasePackage protected $suggests = array(); protected $autoload = array(); protected $includePaths = array(); + protected $archiveExcludes = array(); /** * Creates a new in memory package. @@ -525,4 +526,22 @@ class Package extends BasePackage { return $this->notificationUrl; } + + /** + * Sets a list of patterns to be excluded from archives + * + * @param array $excludes + */ + public function setArchiveExcludes($excludes) + { + $this->archiveExcludes = $excludes; + } + + /** + * {@inheritDoc} + */ + public function getArchiveExcludes() + { + return $this->archiveExcludes; + } } diff --git a/src/Composer/Package/PackageInterface.php b/src/Composer/Package/PackageInterface.php index 6c2b48b4f..227ce42c3 100644 --- a/src/Composer/Package/PackageInterface.php +++ b/src/Composer/Package/PackageInterface.php @@ -308,4 +308,11 @@ interface PackageInterface * @return string */ public function getPrettyString(); + + /** + * Returns a list of patterns to exclude from package archives + * + * @return array + */ + public function getArchiveExcludes(); } diff --git a/tests/Composer/Test/Package/Archiver/PharArchiverTest.php b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php index 0e5099668..97910938d 100644 --- a/tests/Composer/Test/Package/Archiver/PharArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php @@ -29,7 +29,7 @@ class PharArchiverTest extends ArchiverTest // Test archive $archiver = new PharArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'tar'); + $archiver->archive($package->getSourceUrl(), $target, 'tar', null, array('foo/bar', 'baz', '!/foo/bar/baz')); $this->assertFileExists($target); unlink($target); @@ -58,12 +58,25 @@ class PharArchiverTest extends ArchiverTest $currentWorkDir = getcwd(); chdir($this->testDir); - $result = file_put_contents('b', 'a'); + $this->writeFile('file.txt', 'content', $currentWorkDir); + $this->writeFile('foo/bar/baz', 'content', $currentWorkDir); + $this->writeFile('foo/bar/ignoreme', 'content', $currentWorkDir); + $this->writeFile('x/baz', 'content', $currentWorkDir); + $this->writeFile('x/includeme', 'content', $currentWorkDir); + + chdir($currentWorkDir); + } + + protected function writeFile($path, $content, $currentWorkDir) + { + if (!file_exists(dirname($path))) { + mkdir(dirname($path), 0777, true); + } + + $result = file_put_contents($path, 'a'); if (false === $result) { chdir($currentWorkDir); throw new \RuntimeException('Could not save file.'); } - - chdir($currentWorkDir); } } diff --git a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php index ec80984be..4b9877523 100644 --- a/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php +++ b/tests/Composer/Test/Package/Dumper/ArrayDumperTest.php @@ -130,6 +130,14 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase 'extra', array('class' => 'MyVendor\\Installer') ), + array( + 'archive', + array('/foo/bar', 'baz', '!/foo/bar/baz'), + 'archiveExcludes', + array( + 'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'), + ), + ), array( 'require', array(new Link('foo', 'foo/bar', new VersionConstraint('=', '1.0.0.0'), 'requires', '1.0.0')), diff --git a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php index ef1295ff8..68aed0a23 100644 --- a/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php @@ -114,6 +114,9 @@ class ArrayLoaderTest extends \PHPUnit_Framework_TestCase 'target-dir' => 'some/prefix', 'extra' => array('random' => array('things' => 'of', 'any' => 'shape')), 'bin' => array('bin1', 'bin/foo'), + 'archive' => array( + 'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'), + ), ); $package = $this->loader->load($config); diff --git a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php index e095f6e3d..23686d08f 100644 --- a/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php +++ b/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php @@ -123,6 +123,9 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase 'vendor-dir' => 'vendor', 'process-timeout' => 10000, ), + 'archive' => array( + 'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'), + ), 'scripts' => array( 'post-update-cmd' => 'Foo\\Bar\\Baz::doSomething', 'post-install-cmd' => array( From ba375b6867e20f6618adc1262cb6a03952b58821 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 17:45:03 +0100 Subject: [PATCH 20/43] Allow archiving the current project with composer archive --- src/Composer/Command/ArchiveCommand.php | 18 +++++++++++------- .../Package/Archiver/ArchiveManager.php | 16 +++++++++++----- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index c305ce8da..9a97289f8 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -36,7 +36,7 @@ class ArchiveCommand extends Command ->setName('archive') ->setDescription('Create an archive of this composer package') ->setDefinition(array( - new InputArgument('package', InputArgument::REQUIRED, 'The package to archive'), + new InputArgument('package', InputArgument::OPTIONAL, 'The package to archive instead of the current project'), new InputArgument('version', InputArgument::OPTIONAL, 'The package version to archive'), new InputOption('format', 'f', InputOption::VALUE_REQUIRED, 'Format of the resulting archive: tar or zip', 'tar'), new InputOption('dir', false, InputOption::VALUE_REQUIRED, 'Write the archive to this directory', '.'), @@ -44,9 +44,9 @@ class ArchiveCommand extends Command ->setHelp(<<archive command creates an archive of the specified format containing the files and directories of the Composer project or the specified -package and writes it to the specified directory. +package in the specified version and writes it to the specified directory. -php composer.phar archive [--format=zip] [--dir=/foo] package version +php composer.phar archive [--format=zip] [--dir=/foo] [package] [version] EOT ) @@ -64,16 +64,20 @@ EOT ); } - public function archive(IOInterface $io, $packageName, $version = false, $format = 'tar', $dest = '.') + public function archive(IOInterface $io, $packageName = false, $version = false, $format = 'tar', $dest = '.') { $config = Factory::createConfig(); $factory = new Factory; $archiveManager = $factory->createArchiveManager($config); - $package = $this->selectPackage($io, $packageName, $version); + if ($packageName) { + $package = $this->selectPackage($io, $packageName, $version); - if (!$package) { - return 1; + if (!$package) { + return 1; + } + } else { + $package = $this->getComposer()->getPackage(); } $io->write('Creating the archive.'); diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index c3a2c818e..66aefdba6 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -16,6 +16,7 @@ use Composer\Downloader\DownloadManager; use Composer\Factory; use Composer\IO\NullIO; use Composer\Package\PackageInterface; +use Composer\Package\RootPackage; use Composer\Util\Filesystem; /** @@ -73,19 +74,24 @@ class ArchiveManager throw new \RuntimeException(sprintf('No archiver found to support %s format', $format)); } - // Directory used to download the sources $filesystem = new Filesystem(); $packageName = preg_replace('#[^a-z0-9-_.]#i', '-', $package->getPrettyString()); - $sourcePath = sys_get_temp_dir().'/composer_archiver/'.$packageName; - $filesystem->ensureDirectoryExists($sourcePath); // Archive filename $filesystem->ensureDirectoryExists($targetDir); $target = realpath($targetDir).'/'.$packageName.'.'.$format; $filesystem->ensureDirectoryExists(dirname($target)); - // Download sources - $this->downloadManager->download($package, $sourcePath, true); + if ($package instanceof RootPackage) { + $sourcePath = realpath('.'); + } else { + // Directory used to download the sources + $sourcePath = sys_get_temp_dir().'/composer_archiver/'.$packageName; + $filesystem->ensureDirectoryExists($sourcePath); + + // Download sources + $this->downloadManager->download($package, $sourcePath, true); + } // Create the archive $sourceRef = $package->getSourceReference(); From 735b59c1d6b5d2cc5d985b5d7867c6a902ea0e78 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 17:50:09 +0100 Subject: [PATCH 21/43] Skip the vendor dir when archiving the current project --- src/Composer/Command/ArchiveCommand.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 9a97289f8..2cd7e1f50 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -78,6 +78,11 @@ EOT } } else { $package = $this->getComposer()->getPackage(); + + // also ignore the vendor dir + $excludes = $package->getArchiveExcludes(); + $excludes[] = '/'.$this->getComposer()->getConfig()->get('vendor-dir'); + $package->setArchiveExcludes($excludes); } $io->write('Creating the archive.'); From 51135468f032abef8d7f5376a076e729f4f8b1da Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 7 Feb 2013 17:54:06 +0100 Subject: [PATCH 22/43] Clarify composer archive argument optionality --- src/Composer/Command/ArchiveCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 2cd7e1f50..a31878d99 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -46,7 +46,7 @@ The archive command creates an archive of the specified format containing the files and directories of the Composer project or the specified package in the specified version and writes it to the specified directory. -php composer.phar archive [--format=zip] [--dir=/foo] [package] [version] +php composer.phar archive [--format=zip] [--dir=/foo] [package [version]] EOT ) From 285603359c3c319f863e4999963ac96afc8d9b4c Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 17 Feb 2013 17:36:06 +0100 Subject: [PATCH 23/43] Add a missing array typehint --- src/Composer/Package/Package.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Package.php b/src/Composer/Package/Package.php index be57df64f..802fa74aa 100644 --- a/src/Composer/Package/Package.php +++ b/src/Composer/Package/Package.php @@ -532,7 +532,7 @@ class Package extends BasePackage * * @param array $excludes */ - public function setArchiveExcludes($excludes) + public function setArchiveExcludes(array $excludes) { $this->archiveExcludes = $excludes; } From 838edd6e7a279e20119e380c6f54bca0116343f2 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 17 Feb 2013 17:36:32 +0100 Subject: [PATCH 24/43] Fix class description of archive command --- src/Composer/Command/ArchiveCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index a31878d99..9c81832bd 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -24,7 +24,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** - * Install a package as new project into new directory. + * Creates an archive of a package for distribution. * * @author Nils Adermann */ From 0b23643a44e26cfcbf7af5473914429f4c28046d Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sun, 17 Feb 2013 17:39:48 +0100 Subject: [PATCH 25/43] Update json schema as archive include was removed --- res/composer-schema.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/res/composer-schema.json b/res/composer-schema.json index 3eb22cff0..5ae75c922 100644 --- a/res/composer-schema.json +++ b/res/composer-schema.json @@ -208,11 +208,7 @@ "properties": { "exclude": { "type": "array", - "description": "A list of paths to exclude." - }, - "include": { - "type": "array", - "description": "A list of paths to include even though an exclude rule exists for them." + "description": "A list of patterns for paths to exclude or include if prefixed with an exclamation mark." } } }, From 48dd55b7590c34b5e93727f529aebfe0417aafb8 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 25 Feb 2013 16:20:19 +0100 Subject: [PATCH 26/43] Generate a properly unique archive filename for dev revisions --- .../Package/Archiver/ArchiveManager.php | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 66aefdba6..76df21c55 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -45,6 +45,29 @@ class ArchiveManager $this->archivers[] = $archiver; } + /** + * Generate a distinct filename for a particular version of a package. + * + * @param PackageInterface $package The package to get a name for + * + * @return string A filename without an extension + */ + protected function getPackageFilename(PackageInterface $package) + { + $nameParts = array(preg_replace('#[^a-z0-9-_.]#i', '-', $package->getName())); + + if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) { + $nameParts = array_merge($nameParts, array($package->getDistReference(), $package->getDistType())); + } else { + $nameParts = array_merge($nameParts, array($package->getPrettyVersion(), $package->getDistReference(), $package->getDistType())); + } + + return implode('-', array_filter($nameParts, function ($p) { + return !empty($p); + })); + + } + /** * Create an archive of the specified package. * @@ -75,7 +98,7 @@ class ArchiveManager } $filesystem = new Filesystem(); - $packageName = preg_replace('#[^a-z0-9-_.]#i', '-', $package->getPrettyString()); + $packageName = $this->getPackageFilename($package); // Archive filename $filesystem->ensureDirectoryExists($targetDir); From 935f7271f819721088671be180774947198bdcbb Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Mon, 25 Feb 2013 16:25:24 +0100 Subject: [PATCH 27/43] The ArchiveManager should return the written path for library usage --- src/Composer/Package/Archiver/ArchiveManager.php | 2 +- src/Composer/Package/Archiver/ArchiverInterface.php | 2 ++ src/Composer/Package/Archiver/PharArchiver.php | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 76df21c55..a875c4f97 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -118,6 +118,6 @@ class ArchiveManager // Create the archive $sourceRef = $package->getSourceReference(); - $usableArchiver->archive($sourcePath, $target, $format, $sourceRef, $package->getArchiveExcludes()); + return $usableArchiver->archive($sourcePath, $target, $format, $sourceRef, $package->getArchiveExcludes()); } } diff --git a/src/Composer/Package/Archiver/ArchiverInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php index 7a688d251..ce9f6778c 100644 --- a/src/Composer/Package/Archiver/ArchiverInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -29,6 +29,8 @@ interface ArchiverInterface * @param string $sourceRef The reference of the source to archive or null * for the current reference * @param array $excludes A list of patterns for files to exclude + * + * @return string The path to the written archive file */ public function archive($sources, $target, $format, $sourceRef = null, $excludes = array()); diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 950832ce2..071424b15 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -60,6 +60,7 @@ class PharArchiver implements ArchiverInterface }) ->ignoreVCS(true); $phar->buildFromIterator($finder->getIterator(), $sources); + return $target; } catch (\UnexpectedValueException $e) { $message = sprintf("Could not create archive '%s' from '%s': %s", $target, From 074af5dc54ddb8e68f72f835816717e143772d51 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 26 Feb 2013 14:53:49 +0100 Subject: [PATCH 28/43] Remove unnecessary dist type information from archive files --- src/Composer/Package/Archiver/ArchiveManager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index a875c4f97..ef10bdc86 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -59,7 +59,7 @@ class ArchiveManager if (preg_match('{^[a-f0-9]{40}$}', $package->getDistReference())) { $nameParts = array_merge($nameParts, array($package->getDistReference(), $package->getDistType())); } else { - $nameParts = array_merge($nameParts, array($package->getPrettyVersion(), $package->getDistReference(), $package->getDistType())); + $nameParts = array_merge($nameParts, array($package->getPrettyVersion(), $package->getDistReference())); } return implode('-', array_filter($nameParts, function ($p) { From 6ee08a2046fd8df93ffb75256d57e9d6e7929c48 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 26 Feb 2013 14:58:21 +0100 Subject: [PATCH 29/43] Remove unecessary unlink before writing archive --- src/Composer/Package/Archiver/PharArchiver.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 071424b15..4dfd6f0bd 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -39,9 +39,6 @@ class PharArchiver implements ArchiverInterface $excludePatterns = $this->generatePatterns($excludes); try { - if (file_exists($target)) { - unlink($target); - } $phar = new \PharData($target, null, null, static::$formats[$format]); $finder = new Finder\Finder(); $finder From 64941b0a64d4fff76d530f849a678429c955a95e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Sat, 2 Mar 2013 01:26:14 +0100 Subject: [PATCH 30/43] Make overwriting files an ArchiveManager option, use sourceRef in names --- .../Package/Archiver/ArchiveManager.php | 30 +++++++++++++++++-- .../Package/Archiver/ArchiverInterface.php | 4 +-- .../Package/Archiver/PharArchiver.php | 2 +- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index ef10bdc86..27fe85d64 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -29,6 +29,11 @@ class ArchiveManager protected $archivers = array(); + /** + * @var bool + */ + protected $overwriteFiles = true; + /** * @param DownloadManager $downloadManager A manager used to download package sources */ @@ -45,6 +50,19 @@ class ArchiveManager $this->archivers[] = $archiver; } + /** + * Set whether existing archives should be overwritten + * + * @param bool $overwriteFiles New setting + * + * @return $this + */ + public function setOverwriteFiles($overwriteFiles) + { + $this->overwriteFiles = $overwriteFiles; + return $this; + } + /** * Generate a distinct filename for a particular version of a package. * @@ -62,10 +80,13 @@ class ArchiveManager $nameParts = array_merge($nameParts, array($package->getPrettyVersion(), $package->getDistReference())); } + if ($package->getSourceReference()) { + $nameParts[] = substr(sha1($package->getSourceReference()), 0, 6); + } + return implode('-', array_filter($nameParts, function ($p) { return !empty($p); })); - } /** @@ -105,6 +126,10 @@ class ArchiveManager $target = realpath($targetDir).'/'.$packageName.'.'.$format; $filesystem->ensureDirectoryExists(dirname($target)); + if (!$this->overwriteFiles && file_exists($target)) { + return $target; + } + if ($package instanceof RootPackage) { $sourcePath = realpath('.'); } else { @@ -117,7 +142,6 @@ class ArchiveManager } // Create the archive - $sourceRef = $package->getSourceReference(); - return $usableArchiver->archive($sourcePath, $target, $format, $sourceRef, $package->getArchiveExcludes()); + return $usableArchiver->archive($sourcePath, $target, $format, $package->getArchiveExcludes()); } } diff --git a/src/Composer/Package/Archiver/ArchiverInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php index ce9f6778c..ffc93e448 100644 --- a/src/Composer/Package/Archiver/ArchiverInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -26,13 +26,11 @@ interface ArchiverInterface * @param string $sources The sources directory * @param string $target The target file * @param string $format The format used for archive - * @param string $sourceRef The reference of the source to archive or null - * for the current reference * @param array $excludes A list of patterns for files to exclude * * @return string The path to the written archive file */ - public function archive($sources, $target, $format, $sourceRef = null, $excludes = array()); + public function archive($sources, $target, $format, $excludes = array()); /** * Format supported by the archiver. diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 4dfd6f0bd..6ee1156fe 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -32,7 +32,7 @@ class PharArchiver implements ArchiverInterface /** * {@inheritdoc} */ - public function archive($sources, $target, $format, $sourceRef = null, $excludes = array()) + public function archive($sources, $target, $format, $excludes = array()) { $sources = realpath($sources); From deae50392f6480c7924e51504674d88d17c737d2 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 26 Mar 2013 12:15:39 +0100 Subject: [PATCH 31/43] Respect gitignore, gitattributes and hgignore files in archiving --- .../Archiver/ArchivableFilesFinder.php | 78 +++++++ .../Package/Archiver/ArchiveManager.php | 2 +- .../Package/Archiver/ArchiverInterface.php | 3 +- .../Archiver/ComposerExcludeFilter.php | 31 +++ .../Package/Archiver/ExcludeFilterBase.php | 141 ++++++++++++ .../Package/Archiver/GitExcludeFilter.php | 80 +++++++ .../Package/Archiver/HgExcludeFilter.php | 104 +++++++++ .../Package/Archiver/PharArchiver.php | 54 +---- .../Archiver/ArchivableFilesFinderTest.php | 206 ++++++++++++++++++ .../Package/Archiver/ArchiveManagerTest.php | 4 +- .../Package/Archiver/HgExcludeFilterTest.php | 42 ++++ .../Package/Archiver/PharArchiverTest.php | 2 +- 12 files changed, 690 insertions(+), 57 deletions(-) create mode 100644 src/Composer/Package/Archiver/ArchivableFilesFinder.php create mode 100644 src/Composer/Package/Archiver/ComposerExcludeFilter.php create mode 100644 src/Composer/Package/Archiver/ExcludeFilterBase.php create mode 100644 src/Composer/Package/Archiver/GitExcludeFilter.php create mode 100644 src/Composer/Package/Archiver/HgExcludeFilter.php create mode 100644 tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php create mode 100644 tests/Composer/Test/Package/Archiver/HgExcludeFilterTest.php diff --git a/src/Composer/Package/Archiver/ArchivableFilesFinder.php b/src/Composer/Package/Archiver/ArchivableFilesFinder.php new file mode 100644 index 000000000..e4f5818b5 --- /dev/null +++ b/src/Composer/Package/Archiver/ArchivableFilesFinder.php @@ -0,0 +1,78 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +use Composer\Package\BasePackage; +use Composer\Package\PackageInterface; + +use Symfony\Component\Finder; + +/** + * A Symfony Finder wrapper which locates files that should go into archives + * + * Handles .gitignore, .gitattributes and .hgignore files as well as composer's + * own exclude rules from composer.json + * + * @author Nils Adermann + */ +class ArchivableFilesFinder +{ + /** + * @var Symfony\Component\Finder\Finder + */ + protected $finder; + + /** + * Initializes the internal Symfony Finder with appropriate filters + * + * @param string $sources Path to source files to be archived + * @param array $excludes Composer's own exclude rules from composer.json + */ + public function __construct($sources, array $excludes) + { + $sources = realpath($sources); + + $filters = array( + new HgExcludeFilter($sources), + new GitExcludeFilter($sources), + new ComposerExcludeFilter($sources, $excludes), + ); + + $this->finder = new Finder\Finder(); + $this->finder + ->in($sources) + ->filter(function (\SplFileInfo $file) use ($sources, $filters) { + $relativePath = preg_replace( + '#^'.preg_quote($sources, '#').'#', + '', + str_replace(PATH_SEPARATOR, '/', $file->getRealPath()) + ); + + $exclude = false; + foreach ($filters as $filter) { + $exclude = $filter->filter($relativePath, $exclude); + } + return !$exclude; + }) + ->ignoreVCS(true) + ->ignoreDotFiles(false); + } + + /** + * @return Symfony\Component\Finder\Finder + */ + public function getIterator() + { + return $this->finder->getIterator(); + } +} diff --git a/src/Composer/Package/Archiver/ArchiveManager.php b/src/Composer/Package/Archiver/ArchiveManager.php index 27fe85d64..e896199ac 100644 --- a/src/Composer/Package/Archiver/ArchiveManager.php +++ b/src/Composer/Package/Archiver/ArchiveManager.php @@ -70,7 +70,7 @@ class ArchiveManager * * @return string A filename without an extension */ - protected function getPackageFilename(PackageInterface $package) + public function getPackageFilename(PackageInterface $package) { $nameParts = array(preg_replace('#[^a-z0-9-_.]#i', '-', $package->getName())); diff --git a/src/Composer/Package/Archiver/ArchiverInterface.php b/src/Composer/Package/Archiver/ArchiverInterface.php index ffc93e448..5858c6892 100644 --- a/src/Composer/Package/Archiver/ArchiverInterface.php +++ b/src/Composer/Package/Archiver/ArchiverInterface.php @@ -17,6 +17,7 @@ use Composer\Package\PackageInterface; /** * @author Till Klampaeckel * @author Matthieu Moquet + * @author Nils Adermann */ interface ArchiverInterface { @@ -30,7 +31,7 @@ interface ArchiverInterface * * @return string The path to the written archive file */ - public function archive($sources, $target, $format, $excludes = array()); + public function archive($sources, $target, $format, array $excludes = array()); /** * Format supported by the archiver. diff --git a/src/Composer/Package/Archiver/ComposerExcludeFilter.php b/src/Composer/Package/Archiver/ComposerExcludeFilter.php new file mode 100644 index 000000000..ba4cbe6b8 --- /dev/null +++ b/src/Composer/Package/Archiver/ComposerExcludeFilter.php @@ -0,0 +1,31 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +/** + * An exclude filter which processes composer's own exclude rules + * + * @author Nils Adermann + */ +class ComposerExcludeFilter extends ExcludeFilterBase +{ + /** + * @param string $sourcePath Directory containing sources to be filtered + * @param array $excludeRules An array of exclude rules from composer.json + */ + public function __construct($sourcePath, array $excludeRules) + { + parent::__construct($sourcePath); + $this->excludePatterns = $this->generatePatterns($excludeRules); + } +} diff --git a/src/Composer/Package/Archiver/ExcludeFilterBase.php b/src/Composer/Package/Archiver/ExcludeFilterBase.php new file mode 100644 index 000000000..79a132a39 --- /dev/null +++ b/src/Composer/Package/Archiver/ExcludeFilterBase.php @@ -0,0 +1,141 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +use Symfony\Component\Finder; + +/** + * @author Nils Adermann + */ +abstract class ExcludeFilterBase +{ + /** + * @var string + */ + protected $sourcePath; + + /** + * @var array + */ + protected $excludePatterns; + + /** + * @param string $sourcePath Directory containing sources to be filtered + */ + public function __construct($sourcePath) + { + $this->sourcePath = $sourcePath; + $this->excludePatterns = array(); + } + + /** + * Checks the given path against all exclude patterns in this filter + * + * Negated patterns overwrite exclude decisions of previous filters. + * + * @param string $relativePath The file's path relative to the sourcePath + * @param bool $exclude Whether a previous filter wants to exclude this file + * + * @return bool Whether the file should be excluded + */ + public function filter($relativePath, $exclude) + { + foreach ($this->excludePatterns as $patternData) { + list($pattern, $negate, $stripLeadingSlash) = $patternData; + + if ($stripLeadingSlash) { + $path = substr($relativePath, 1); + } else { + $path = $relativePath; + } + + if (preg_match($pattern, $path)) { + $exclude = !$negate; + } + } + return $exclude; + } + + /** + * Processes a file containing exclude rules of different formats per line + * + * @param array $lines A set of lines to be parsed + * @param callback $lineParser The parser to be used on each line + * + * @return array Exclude patterns to be used in filter() + */ + protected function parseLines(array $lines, $lineParser) + { + return array_filter( + array_map( + function ($line) use ($lineParser) { + $line = trim($line); + + $commentHash = strpos($line, '#'); + if ($commentHash !== false) { + $line = substr($line, 0, $commentHash); + } + + if ($line) { + return call_user_func($lineParser, $line); + } + return null; + }, $lines), + function ($pattern) { + return $pattern !== null; + } + ); + } + + /** + * Generates a set of exclude patterns for filter() from gitignore rules + * + * @param array $rules A list of exclude rules in gitignore syntax + * + * @return array Exclude patterns + */ + protected function generatePatterns($rules) + { + $patterns = array(); + foreach ($rules as $rule) { + $patterns[] = $this->generatePattern($rule); + } + return $patterns; + } + + /** + * Generates an exclude pattern for filter() from a gitignore rule + * + * @param string An exclude rule in gitignore syntax + * + * @param array An exclude pattern + */ + protected function generatePattern($rule) + { + $negate = false; + $pattern = '#'; + + if (strlen($rule) && $rule[0] === '!') { + $negate = true; + $rule = substr($rule, 1); + } + + if (strlen($rule) && $rule[0] === '/') { + $pattern .= '^/'; + $rule = substr($rule, 1); + } + + $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2); + return array($pattern . '#', $negate, false); + } +} diff --git a/src/Composer/Package/Archiver/GitExcludeFilter.php b/src/Composer/Package/Archiver/GitExcludeFilter.php new file mode 100644 index 000000000..1f6123e7b --- /dev/null +++ b/src/Composer/Package/Archiver/GitExcludeFilter.php @@ -0,0 +1,80 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +/** + * An exclude filter that processes gitignore and gitattributes + * + * It respects export-ignore git attributes + * + * @author Nils Adermann + */ +class GitExcludeFilter extends ExcludeFilterBase +{ + /** + * Parses .gitignore and .gitattributes files if they exist + * + * @param string $sourcePath + */ + public function __construct($sourcePath) + { + parent::__construct($sourcePath); + + if (file_exists($sourcePath.'/.gitignore')) { + $this->excludePatterns = $this->parseLines( + file($sourcePath.'/.gitignore'), + array($this, 'parseGitIgnoreLine') + ); + } + if (file_exists($sourcePath.'/.gitattributes')) { + $this->excludePatterns = array_merge( + $this->excludePatterns, + $this->parseLines( + file($sourcePath.'/.gitattributes'), + array($this, 'parseGitAttributesLine') + )); + } + } + + /** + * Callback line parser which process gitignore lines + * + * @param string $line A line from .gitignore + * + * @return array An exclude pattern for filter() + */ + protected function parseGitIgnoreLine($line) + { + return $this->generatePattern($line); + } + + /** + * Callback parser which finds export-ignore rules in git attribute lines + * + * @param string $line A line from .gitattributes + * + * @return array An exclude pattern for filter() + */ + protected function parseGitAttributesLine($line) + { + $parts = preg_split('#\s+#', $line); + + if (count($parts) != 2) { + return null; + } + + if ($parts[1] === 'export-ignore') { + return $this->generatePattern($parts[0]); + } + } +} diff --git a/src/Composer/Package/Archiver/HgExcludeFilter.php b/src/Composer/Package/Archiver/HgExcludeFilter.php new file mode 100644 index 000000000..66c9d6b2e --- /dev/null +++ b/src/Composer/Package/Archiver/HgExcludeFilter.php @@ -0,0 +1,104 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Package\Archiver; + +use Symfony\Component\Finder; + +/** + * An exclude filter that processes hgignore files + * + * @author Nils Adermann + */ +class HgExcludeFilter extends ExcludeFilterBase +{ + const HG_IGNORE_REGEX = 1; + const HG_IGNORE_GLOB = 2; + + /** + * Either HG_IGNORE_REGEX or HG_IGNORE_GLOB + * @var integer + */ + protected $patternMode; + + /** + * Parses .hgignore file if it exist + * + * @param string $sourcePath + */ + public function __construct($sourcePath) + { + parent::__construct($sourcePath); + + $this->patternMode = self::HG_IGNORE_REGEX; + + if (file_exists($sourcePath.'/.hgignore')) { + $this->excludePatterns = $this->parseLines( + file($sourcePath.'/.hgignore'), + array($this, 'parseHgIgnoreLine') + ); + } + } + + /** + * Callback line parser which process hgignore lines + * + * @param string $line A line from .hgignore + * + * @return array An exclude pattern for filter() + */ + public function parseHgIgnoreLine($line) + { + if (preg_match('#^syntax\s*:\s*(glob|regexp)$#', $line, $matches)) { + if ($matches[1] === 'glob') { + $this->patternMode = self::HG_IGNORE_GLOB; + } else { + $this->patternMode = self::HG_IGNORE_REGEX; + } + return null; + } + + if ($this->patternMode == self::HG_IGNORE_GLOB) { + return $this->patternFromGlob($line); + } else { + return $this->patternFromRegex($line); + } + } + + /** + * Generates an exclude pattern for filter() from a hg glob expression + * + * @param string $line A line from .hgignore in glob mode + * + * @return array An exclude pattern for filter() + */ + protected function patternFromGlob($line) + { + $pattern = '#'.substr(Finder\Glob::toRegex($line), 2, -1).'#'; + $pattern = str_replace('[^/]*', '.*', $pattern); + return array($pattern, false, true); + } + + /** + * Generates an exclude pattern for filter() from a hg regexp expression + * + * @param string $line A line from .hgignore in regexp mode + * + * @return array An exclude pattern for filter() + */ + public function patternFromRegex($line) + { + // WTF need to escape the delimiter safely + $pattern = '#'.preg_replace('/((?:\\\\\\\\)*)(\\\\?)#/', '\1\2\2\\#', $line).'#'; + return array($pattern, false, true); + } +} diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 6ee1156fe..ef7e52fce 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -15,8 +15,6 @@ namespace Composer\Package\Archiver; use Composer\Package\BasePackage; use Composer\Package\PackageInterface; -use Symfony\Component\Finder; - /** * @author Till Klampaeckel * @author Nils Adermann @@ -32,31 +30,14 @@ class PharArchiver implements ArchiverInterface /** * {@inheritdoc} */ - public function archive($sources, $target, $format, $excludes = array()) + public function archive($sources, $target, $format, array $excludes = array()) { $sources = realpath($sources); - $excludePatterns = $this->generatePatterns($excludes); - try { $phar = new \PharData($target, null, null, static::$formats[$format]); - $finder = new Finder\Finder(); - $finder - ->in($sources) - ->filter(function (\SplFileInfo $file) use ($sources, $excludePatterns) { - $relativePath = preg_replace('#^'.preg_quote($sources, '#').'#', '', $file->getRealPath()); - - $include = true; - foreach ($excludePatterns as $patternData) { - list($pattern, $negate) = $patternData; - if (preg_match($pattern, $relativePath)) { - $include = $negate; - } - } - return $include; - }) - ->ignoreVCS(true); - $phar->buildFromIterator($finder->getIterator(), $sources); + $files = new ArchivableFilesFinder($sources, $excludes); + $phar->buildFromIterator($files->getIterator(), $sources); return $target; } catch (\UnexpectedValueException $e) { $message = sprintf("Could not create archive '%s' from '%s': %s", @@ -69,35 +50,6 @@ class PharArchiver implements ArchiverInterface } } - /** - * Generates a set of PCRE patterns from a set of exclude rules. - * - * @param array $rules A list of exclude rules similar to gitignore syntax - */ - protected function generatePatterns($rules) - { - $patterns = array(); - foreach ($rules as $rule) { - $negate = false; - $pattern = '#'; - - if (strlen($rule) && $rule[0] === '!') { - $negate = true; - $rule = substr($rule, 1); - } - - if (strlen($rule) && $rule[0] === '/') { - $pattern .= '^/'; - $rule = substr($rule, 1); - } - - $pattern .= substr(Finder\Glob::toRegex($rule), 2, -2); - $patterns[] = array($pattern . '#', $negate); - } - - return $patterns; - } - /** * {@inheritdoc} */ diff --git a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php new file mode 100644 index 000000000..49d3dd642 --- /dev/null +++ b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php @@ -0,0 +1,206 @@ + + * 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\Archiver; + +use Composer\Package\Archiver\ArchivableFilesFinder; +use Composer\Util\Filesystem; + +use Symfony\Component\Process\Process; + +/** + * @author Nils Adermann + */ +class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase +{ + protected $sources; + protected $finder; + + protected function setup() + { + $fs = new Filesystem; + + $this->sources = sys_get_temp_dir(). + '/composer_archiver_test'.uniqid(mt_rand(), true); + + $fileTree = array( + 'A/prefixA.foo', + 'A/prefixB.foo', + 'A/prefixC.foo', + 'A/prefixD.foo', + 'A/prefixE.foo', + 'A/prefixF.foo', + 'B/sub/prefixA.foo', + 'B/sub/prefixB.foo', + 'B/sub/prefixC.foo', + 'B/sub/prefixD.foo', + 'B/sub/prefixE.foo', + 'B/sub/prefixF.foo', + 'toplevelA.foo', + 'toplevelB.foo', + 'prefixA.foo', + 'prefixB.foo', + 'prefixC.foo', + 'prefixD.foo', + 'prefixE.foo', + 'prefixF.foo', + ); + + foreach ($fileTree as $relativePath) { + $path = $this->sources.'/'.$relativePath; + $fs->ensureDirectoryExists(dirname($path)); + file_put_contents($path, ''); + } + } + + protected function tearDown() + { + $fs = new Filesystem; + $fs->removeDirectory($this->sources); + } + + public function testManualExcludes() + { + $excludes = array( + 'prefixB.foo', + '!/prefixB.foo', + '/prefixA.foo', + 'prefixC.*', + '!*/*/*/prefixC.foo' + ); + + $this->finder = new ArchivableFilesFinder($this->sources, $excludes); + + $this->assertArchivableFiles(array( + '/A/prefixA.foo', + '/A/prefixD.foo', + '/A/prefixE.foo', + '/A/prefixF.foo', + '/B/sub/prefixA.foo', + '/B/sub/prefixC.foo', + '/B/sub/prefixD.foo', + '/B/sub/prefixE.foo', + '/B/sub/prefixF.foo', + '/prefixB.foo', + '/prefixD.foo', + '/prefixE.foo', + '/prefixF.foo', + '/toplevelA.foo', + '/toplevelB.foo', + )); + } + + public function testGitExcludes() + { + file_put_contents($this->sources.'/.gitignore', implode("\n", array( + '# gitignore rules with comments and blank lines', + '', + 'prefixE.foo', + '# and more', + '# comments', + '', + '!/prefixE.foo', + '/prefixD.foo', + 'prefixF.*', + '!/*/*/prefixF.foo', + '', + ))); + + // git does not currently support negative git attributes + file_put_contents($this->sources.'/.gitattributes', implode("\n", array( + '', + '# gitattributes rules with comments and blank lines', + 'prefixB.foo export-ignore', + //'!/prefixB.foo export-ignore', + '/prefixA.foo export-ignore', + 'prefixC.* export-ignore', + //'!/*/*/prefixC.foo export-ignore' + ))); + + $this->finder = new ArchivableFilesFinder($this->sources, array()); + + $this->assertArchivableFiles($this->getArchivedFiles('git init && '. + 'git add .git* && '. + 'git commit -m "ignore rules" && '. + 'git add . && '. + 'git commit -m "init" && '. + 'git archive --format=zip --prefix=archive/ -o archive.zip HEAD' + )); + } + + public function testHgExcludes() + { + file_put_contents($this->sources.'/.hgignore', implode("\n", array( + '# hgignore rules with comments, blank lines and syntax changes', + '', + 'pre*A.foo', + 'prefixE.foo', + '# and more', + '# comments', + '', + '^prefixD.foo', + 'syntax: glob', + 'prefixF.*', + 'B/*', + ))); + + $this->finder = new ArchivableFilesFinder($this->sources, array()); + + $expectedFiles = $this->getArchivedFiles('hg init && '. + 'hg add && '. + 'hg commit -m "init" && '. + 'hg archive archive.zip' + ); + + array_shift($expectedFiles); // remove .hg_archival.txt + + $this->assertArchivableFiles($expectedFiles); + } + + protected function getArchivableFiles() + { + $files = array(); + foreach ($this->finder->getIterator() as $file) { + if (!$file->isDir()) { + $files[] = preg_replace('#^'.preg_quote($this->sources, '#').'#', '', $file->getRealPath()); + } + } + + sort($files); + + return $files; + } + + protected function getArchivedFiles($command) + { + $process = new Process($command, $this->sources); + $process->run(); + + $archive = new \PharData($this->sources.'/archive.zip'); + $iterator = new \RecursiveIteratorIterator($archive); + + $files = array(); + foreach ($iterator as $file) { + $files[] = preg_replace('#^phar://'.preg_quote($this->sources, '#').'/archive\.zip/archive#', '', $file); + } + + unlink($this->sources.'/archive.zip'); + return $files; + } + + protected function assertArchivableFiles($expectedFiles) + { + $actualFiles = $this->getArchivableFiles(); + + $this->assertEquals($expectedFiles, $actualFiles); + } +} diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index 54caa0186..16b315038 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -51,8 +51,6 @@ class ArchiveManagerTest extends ArchiverTest $package = $this->setupPackage(); - // The package is source from git, - // so it should `git archive --format tar` $this->manager->archive($package, 'tar', $this->targetDir); $target = $this->getTargetName($package, 'tar'); @@ -63,7 +61,7 @@ class ArchiveManagerTest extends ArchiverTest protected function getTargetName(PackageInterface $package, $format) { - $packageName = preg_replace('#[^a-z0-9-_.]#i', '-', $package->getPrettyString()); + $packageName = $this->manager->getPackageFilename($package); $target = $this->targetDir.'/'.$packageName.'.'.$format; return $target; diff --git a/tests/Composer/Test/Package/Archiver/HgExcludeFilterTest.php b/tests/Composer/Test/Package/Archiver/HgExcludeFilterTest.php new file mode 100644 index 000000000..1a9d20089 --- /dev/null +++ b/tests/Composer/Test/Package/Archiver/HgExcludeFilterTest.php @@ -0,0 +1,42 @@ + + * 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\Archiver; + +use Composer\Package\Archiver\HgExcludeFilter; + +/** + * @author Nils Adermann + */ +class HgExcludeFilterTest extends \PHPUnit_Framework_TestCase +{ + /** + * @dataProvider patterns + */ + public function testPatternEscape($ignore, $expected) + { + $filter = new HgExcludeFilter('/'); + + $this->assertEquals($expected, $filter->patternFromRegex($ignore)); + } + + public function patterns() + { + return array( + array('.#', array('#.\\##', false, true)), + array('.\\#', array('#.\\\\\\##', false, true)), + array('\\.#', array('#\\.\\##', false, true)), + array('\\\\.\\\\\\\\#', array('#\\\\.\\\\\\\\\\##', false, true)), + array('.\\\\\\\\\\#', array('#.\\\\\\\\\\\\\\##', false, true)), + ); + } +} diff --git a/tests/Composer/Test/Package/Archiver/PharArchiverTest.php b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php index 97910938d..721d34f92 100644 --- a/tests/Composer/Test/Package/Archiver/PharArchiverTest.php +++ b/tests/Composer/Test/Package/Archiver/PharArchiverTest.php @@ -29,7 +29,7 @@ class PharArchiverTest extends ArchiverTest // Test archive $archiver = new PharArchiver(); - $archiver->archive($package->getSourceUrl(), $target, 'tar', null, array('foo/bar', 'baz', '!/foo/bar/baz')); + $archiver->archive($package->getSourceUrl(), $target, 'tar', array('foo/bar', 'baz', '!/foo/bar/baz')); $this->assertFileExists($target); unlink($target); From 6066359944642e47d03c2dd367c9e43af787a9f7 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 26 Mar 2013 13:02:32 +0100 Subject: [PATCH 32/43] Skip directories in zip generation, empty dirs won't get archived This seems ok as we currently rely on git generating archives which does not archive empty directories either. --- .../Archiver/ArchivableFilesFinder.php | 23 ++++++++++++++----- .../Package/Archiver/PharArchiver.php | 2 +- .../Archiver/ArchivableFilesFinderTest.php | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchivableFilesFinder.php b/src/Composer/Package/Archiver/ArchivableFilesFinder.php index e4f5818b5..ab9604df5 100644 --- a/src/Composer/Package/Archiver/ArchivableFilesFinder.php +++ b/src/Composer/Package/Archiver/ArchivableFilesFinder.php @@ -25,7 +25,7 @@ use Symfony\Component\Finder; * * @author Nils Adermann */ -class ArchivableFilesFinder +class ArchivableFilesFinder extends \IteratorIterator { /** * @var Symfony\Component\Finder\Finder @@ -66,13 +66,24 @@ class ArchivableFilesFinder }) ->ignoreVCS(true) ->ignoreDotFiles(false); + + parent::__construct($this->finder->getIterator()); } - /** - * @return Symfony\Component\Finder\Finder - */ - public function getIterator() + public function next() + { + do { + $this->getInnerIterator()->next(); + } while ($this->getInnerIterator()->valid() && $this->getInnerIterator()->current()->isDir()); + } + + public function current() + { + return $this->getInnerIterator()->current(); + } + + public function valid() { - return $this->finder->getIterator(); + return $this->getInnerIterator()->valid(); } } diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index ef7e52fce..103f5bc23 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -37,7 +37,7 @@ class PharArchiver implements ArchiverInterface try { $phar = new \PharData($target, null, null, static::$formats[$format]); $files = new ArchivableFilesFinder($sources, $excludes); - $phar->buildFromIterator($files->getIterator(), $sources); + $phar->buildFromIterator($files, $sources); return $target; } catch (\UnexpectedValueException $e) { $message = sprintf("Could not create archive '%s' from '%s': %s", diff --git a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php index 49d3dd642..1e780414d 100644 --- a/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchivableFilesFinderTest.php @@ -169,7 +169,7 @@ class ArchivableFilesFinderTest extends \PHPUnit_Framework_TestCase protected function getArchivableFiles() { $files = array(); - foreach ($this->finder->getIterator() as $file) { + foreach ($this->finder as $file) { if (!$file->isDir()) { $files[] = preg_replace('#^'.preg_quote($this->sources, '#').'#', '', $file->getRealPath()); } From 75d1759e77e502fe0482a68bac7ef7ed7a6830ae Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 26 Mar 2013 13:04:07 +0100 Subject: [PATCH 33/43] Replace DIRECTORY_SEPARATOR in paths, not PATH_SEPARATOR --- src/Composer/Package/Archiver/ArchivableFilesFinder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Archiver/ArchivableFilesFinder.php b/src/Composer/Package/Archiver/ArchivableFilesFinder.php index ab9604df5..f2c6aad5f 100644 --- a/src/Composer/Package/Archiver/ArchivableFilesFinder.php +++ b/src/Composer/Package/Archiver/ArchivableFilesFinder.php @@ -55,7 +55,7 @@ class ArchivableFilesFinder extends \IteratorIterator $relativePath = preg_replace( '#^'.preg_quote($sources, '#').'#', '', - str_replace(PATH_SEPARATOR, '/', $file->getRealPath()) + str_replace(DIRECTORY_SEPARATOR, '/', $file->getRealPath()) ); $exclude = false; From ecf4f42885581988a3fda7b74e074f58d230afb2 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Tue, 26 Mar 2013 13:09:37 +0100 Subject: [PATCH 34/43] Use a FilterIterator rather than a modified IteratorIterator, simpler --- .../Package/Archiver/ArchivableFilesFinder.php | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/Composer/Package/Archiver/ArchivableFilesFinder.php b/src/Composer/Package/Archiver/ArchivableFilesFinder.php index f2c6aad5f..616b9540e 100644 --- a/src/Composer/Package/Archiver/ArchivableFilesFinder.php +++ b/src/Composer/Package/Archiver/ArchivableFilesFinder.php @@ -25,7 +25,7 @@ use Symfony\Component\Finder; * * @author Nils Adermann */ -class ArchivableFilesFinder extends \IteratorIterator +class ArchivableFilesFinder extends \FilterIterator { /** * @var Symfony\Component\Finder\Finder @@ -70,20 +70,8 @@ class ArchivableFilesFinder extends \IteratorIterator parent::__construct($this->finder->getIterator()); } - public function next() + public function accept() { - do { - $this->getInnerIterator()->next(); - } while ($this->getInnerIterator()->valid() && $this->getInnerIterator()->current()->isDir()); - } - - public function current() - { - return $this->getInnerIterator()->current(); - } - - public function valid() - { - return $this->getInnerIterator()->valid(); + return !$this->getInnerIterator()->current()->isDir(); } } From 1af2be9d6dbf46cbbc7c9a85ccd528e9264c5f8a Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 11:53:39 +0100 Subject: [PATCH 35/43] Rename ExcludeFilterBase to BaseExcludeFilter --- .../Archiver/{ExcludeFilterBase.php => BaseExcludeFilter.php} | 2 +- src/Composer/Package/Archiver/ComposerExcludeFilter.php | 2 +- src/Composer/Package/Archiver/GitExcludeFilter.php | 2 +- src/Composer/Package/Archiver/HgExcludeFilter.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/Composer/Package/Archiver/{ExcludeFilterBase.php => BaseExcludeFilter.php} (99%) diff --git a/src/Composer/Package/Archiver/ExcludeFilterBase.php b/src/Composer/Package/Archiver/BaseExcludeFilter.php similarity index 99% rename from src/Composer/Package/Archiver/ExcludeFilterBase.php rename to src/Composer/Package/Archiver/BaseExcludeFilter.php index 79a132a39..90ea53bf8 100644 --- a/src/Composer/Package/Archiver/ExcludeFilterBase.php +++ b/src/Composer/Package/Archiver/BaseExcludeFilter.php @@ -17,7 +17,7 @@ use Symfony\Component\Finder; /** * @author Nils Adermann */ -abstract class ExcludeFilterBase +abstract class BaseExcludeFilter { /** * @var string diff --git a/src/Composer/Package/Archiver/ComposerExcludeFilter.php b/src/Composer/Package/Archiver/ComposerExcludeFilter.php index ba4cbe6b8..c98a3ae66 100644 --- a/src/Composer/Package/Archiver/ComposerExcludeFilter.php +++ b/src/Composer/Package/Archiver/ComposerExcludeFilter.php @@ -17,7 +17,7 @@ namespace Composer\Package\Archiver; * * @author Nils Adermann */ -class ComposerExcludeFilter extends ExcludeFilterBase +class ComposerExcludeFilter extends BaseExcludeFilter { /** * @param string $sourcePath Directory containing sources to be filtered diff --git a/src/Composer/Package/Archiver/GitExcludeFilter.php b/src/Composer/Package/Archiver/GitExcludeFilter.php index 1f6123e7b..6727f9af2 100644 --- a/src/Composer/Package/Archiver/GitExcludeFilter.php +++ b/src/Composer/Package/Archiver/GitExcludeFilter.php @@ -19,7 +19,7 @@ namespace Composer\Package\Archiver; * * @author Nils Adermann */ -class GitExcludeFilter extends ExcludeFilterBase +class GitExcludeFilter extends BaseExcludeFilter { /** * Parses .gitignore and .gitattributes files if they exist diff --git a/src/Composer/Package/Archiver/HgExcludeFilter.php b/src/Composer/Package/Archiver/HgExcludeFilter.php index 66c9d6b2e..b2e843f75 100644 --- a/src/Composer/Package/Archiver/HgExcludeFilter.php +++ b/src/Composer/Package/Archiver/HgExcludeFilter.php @@ -19,7 +19,7 @@ use Symfony\Component\Finder; * * @author Nils Adermann */ -class HgExcludeFilter extends ExcludeFilterBase +class HgExcludeFilter extends BaseExcludeFilter { const HG_IGNORE_REGEX = 1; const HG_IGNORE_GLOB = 2; From 43be72acb46a2f132f74e03efbd060b2c689ecc7 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 11:54:29 +0100 Subject: [PATCH 36/43] Follow PSR-2 for method modifier ordering --- src/Composer/Package/Archiver/PharArchiver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 103f5bc23..42705e92f 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -22,7 +22,7 @@ use Composer\Package\PackageInterface; */ class PharArchiver implements ArchiverInterface { - static protected $formats = array( + protected static $formats = array( 'zip' => \Phar::ZIP, 'tar' => \Phar::TAR, ); From 14ee67bed403a5853d1a4c3dd0749a96ba35feb2 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 12:00:10 +0100 Subject: [PATCH 37/43] Output packages in archive command using getPrettyString --- src/Composer/Command/ArchiveCommand.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 9c81832bd..fce178047 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -112,12 +112,12 @@ EOT if (count($packages) > 1) { $package = $packages[0]; - $io->write('Found multiple matches, selected '.$package.'.'); - $io->write('Alternatives were '.implode(', ', $packages).'.'); + $io->write('Found multiple matches, selected '.$package->getPrettyString().'.'); + $io->write('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.'); $io->write('Please use a more specific constraint to pick a different package.'); } elseif ($packages) { $package = $packages[0]; - $io->write('Found an exact match '.$package.'.'); + $io->write('Found an exact match '.$package->getPrettyString().'.'); } else { $io->write('Could not find a package matching '.$packageName.'.'); return false; From 870a87f6d65e7a8af7642008ed596c910df768a5 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 12:05:08 +0100 Subject: [PATCH 38/43] Use null as default values rather than false Also made archive() in the ArchiveCommand protected as it does not need to be used from the outside. The ArchiveManager can be used instead. --- src/Composer/Command/ArchiveCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index fce178047..03f9d4bef 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -64,7 +64,7 @@ EOT ); } - public function archive(IOInterface $io, $packageName = false, $version = false, $format = 'tar', $dest = '.') + protected function archive(IOInterface $io, $packageName = null, $version = null, $format = 'tar', $dest = '.') { $config = Factory::createConfig(); $factory = new Factory; @@ -91,7 +91,7 @@ EOT return 0; } - protected function selectPackage(IOInterface $io, $packageName, $version = false) + protected function selectPackage(IOInterface $io, $packageName, $version = null) { $io->write('Searching for the specified package.'); From cfd7a50f0a0d2e3acaceb439c0e17aaf87555677 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 12:06:43 +0100 Subject: [PATCH 39/43] Do not hardcode vendor dir exclusion on archive. For one thing this wouldn't have worked for any custom installers anyway which can write installed code to other places. This will now allow one to use composer archive on a clean code checkout to build an archive as we are used to. Or on one that had composer install run to build an archive that can be used for deployment which includes the vendors. --- src/Composer/Command/ArchiveCommand.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Composer/Command/ArchiveCommand.php b/src/Composer/Command/ArchiveCommand.php index 03f9d4bef..61a5d8e48 100644 --- a/src/Composer/Command/ArchiveCommand.php +++ b/src/Composer/Command/ArchiveCommand.php @@ -78,11 +78,6 @@ EOT } } else { $package = $this->getComposer()->getPackage(); - - // also ignore the vendor dir - $excludes = $package->getArchiveExcludes(); - $excludes[] = '/'.$this->getComposer()->getConfig()->get('vendor-dir'); - $package->setArchiveExcludes($excludes); } $io->write('Creating the archive.'); From 22044121cecec7b027cc5d33d813de4b65abe0a1 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 12:15:04 +0100 Subject: [PATCH 40/43] Callbacks must be accessible publically on PHP 5.3 --- src/Composer/Package/Archiver/GitExcludeFilter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Composer/Package/Archiver/GitExcludeFilter.php b/src/Composer/Package/Archiver/GitExcludeFilter.php index 6727f9af2..926bb4d69 100644 --- a/src/Composer/Package/Archiver/GitExcludeFilter.php +++ b/src/Composer/Package/Archiver/GitExcludeFilter.php @@ -53,7 +53,7 @@ class GitExcludeFilter extends BaseExcludeFilter * * @return array An exclude pattern for filter() */ - protected function parseGitIgnoreLine($line) + public function parseGitIgnoreLine($line) { return $this->generatePattern($line); } @@ -65,7 +65,7 @@ class GitExcludeFilter extends BaseExcludeFilter * * @return array An exclude pattern for filter() */ - protected function parseGitAttributesLine($line) + public function parseGitAttributesLine($line) { $parts = preg_split('#\s+#', $line); From 6f61d95829502d1558db57b59ca488aa2b5f9b29 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 12:48:19 +0100 Subject: [PATCH 41/43] Make sure git is setup to allow commits on travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index eb1c9a5de..a897f3cee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,5 +9,7 @@ php: before_script: - echo '' > ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini - composer install --dev --prefer-source + - git config --global user.name travis-ci + - git config --global user.email travis@example.com script: ./vendor/bin/phpunit -c tests/complete.phpunit.xml From 4af69c85ca9401cc07eb73a7ba8c7fd26fe4920e Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 13:21:55 +0100 Subject: [PATCH 42/43] Fix tests by passing proper working directory to processes --- tests/Composer/Test/AllFunctionalTest.php | 5 ++--- tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Composer/Test/AllFunctionalTest.php b/tests/Composer/Test/AllFunctionalTest.php index 83f530f75..915804f33 100644 --- a/tests/Composer/Test/AllFunctionalTest.php +++ b/tests/Composer/Test/AllFunctionalTest.php @@ -55,9 +55,8 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase $fs->ensureDirectoryExists(dirname(self::$pharPath)); chdir(dirname(self::$pharPath)); - $proc = new Process('php '.escapeshellarg(__DIR__.'/../../../bin/compile')); + $proc = new Process('php '.escapeshellarg(__DIR__.'/../../../bin/compile'), dirname(self::$pharPath)); $exitcode = $proc->run(); - if ($exitcode !== 0 || trim($proc->getOutput())) { $this->fail($proc->getOutput()); } @@ -76,7 +75,7 @@ class AllFunctionalTest extends \PHPUnit_Framework_TestCase putenv('COMPOSER_HOME='.$this->testDir.'home'); $cmd = 'php '.escapeshellarg(self::$pharPath).' --no-ansi '.$testData['RUN']; - $proc = new Process($cmd); + $proc = new Process($cmd, __DIR__.'/Fixtures/functional'); $exitcode = $proc->run(); if (isset($testData['EXPECT'])) { diff --git a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php index 16b315038..2c51461ed 100644 --- a/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php +++ b/tests/Composer/Test/Package/Archiver/ArchiveManagerTest.php @@ -75,7 +75,8 @@ class ArchiveManagerTest extends ArchiverTest $currentWorkDir = getcwd(); chdir($this->testDir); - $result = $this->process->execute('git init -q'); + $output = null; + $result = $this->process->execute('git init -q', $output, $this->testDir); if ($result > 0) { chdir($currentWorkDir); throw new \RuntimeException('Could not init: '.$this->process->getErrorOutput()); @@ -87,7 +88,7 @@ class ArchiveManagerTest extends ArchiverTest throw new \RuntimeException('Could not save file.'); } - $result = $this->process->execute('git add b && git commit -m "commit b" -q'); + $result = $this->process->execute('git add b && git commit -m "commit b" -q', $output, $this->testDir); if ($result > 0) { chdir($currentWorkDir); throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput()); From 88032816489eabfb67b26a90b9050c990c29f4c6 Mon Sep 17 00:00:00 2001 From: Nils Adermann Date: Thu, 28 Mar 2013 13:24:34 +0100 Subject: [PATCH 43/43] Make sure Phar overwrites files and doesn't load them --- src/Composer/Package/Archiver/PharArchiver.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Composer/Package/Archiver/PharArchiver.php b/src/Composer/Package/Archiver/PharArchiver.php index 42705e92f..bd8f5c292 100644 --- a/src/Composer/Package/Archiver/PharArchiver.php +++ b/src/Composer/Package/Archiver/PharArchiver.php @@ -34,6 +34,11 @@ class PharArchiver implements ArchiverInterface { $sources = realpath($sources); + // Phar would otherwise load the file which we don't want + if (file_exists($target)) { + unlink($target); + } + try { $phar = new \PharData($target, null, null, static::$formats[$format]); $files = new ArchivableFilesFinder($sources, $excludes);