commit
78c250da19
|
@ -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
|
||||
|
|
|
@ -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) →
|
||||
|
|
|
@ -202,6 +202,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"archive": {
|
||||
"type": ["object"],
|
||||
"description": "Options for creating package archives for distribution.",
|
||||
"properties": {
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"description": "A list of patterns for paths to exclude or include if prefixed with an exclamation mark."
|
||||
}
|
||||
}
|
||||
},
|
||||
"repositories": {
|
||||
"type": ["object", "array"],
|
||||
"description": "A set of additional repositories where packages can be found.",
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\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;
|
||||
|
||||
/**
|
||||
* Creates an archive of a package for distribution.
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class ArchiveCommand extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('archive')
|
||||
->setDescription('Create an archive of this composer package')
|
||||
->setDefinition(array(
|
||||
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', '.'),
|
||||
))
|
||||
->setHelp(<<<EOT
|
||||
The <info>archive</info> 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.
|
||||
|
||||
<info>php composer.phar archive [--format=zip] [--dir=/foo] [package [version]]</info>
|
||||
|
||||
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')
|
||||
);
|
||||
}
|
||||
|
||||
protected function archive(IOInterface $io, $packageName = null, $version = null, $format = 'tar', $dest = '.')
|
||||
{
|
||||
$config = Factory::createConfig();
|
||||
$factory = new Factory;
|
||||
$archiveManager = $factory->createArchiveManager($config);
|
||||
|
||||
if ($packageName) {
|
||||
$package = $this->selectPackage($io, $packageName, $version);
|
||||
|
||||
if (!$package) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
$package = $this->getComposer()->getPackage();
|
||||
}
|
||||
|
||||
$io->write('<info>Creating the archive.</info>');
|
||||
$archiveManager->archive($package, $format, $dest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function selectPackage(IOInterface $io, $packageName, $version = null)
|
||||
{
|
||||
$io->write('<info>Searching for the specified package.</info>');
|
||||
|
||||
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('<info>Found multiple matches, selected '.$package->getPrettyString().'.</info>');
|
||||
$io->write('Alternatives were '.implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)).'.');
|
||||
$io->write('<comment>Please use a more specific constraint to pick a different package.</comment>');
|
||||
} elseif ($packages) {
|
||||
$package = $packages[0];
|
||||
$io->write('<info>Found an exact match '.$package->getPrettyString().'.</info>');
|
||||
} else {
|
||||
$io->write('<error>Could not find a package matching '.$packageName.'.</error>');
|
||||
return false;
|
||||
}
|
||||
|
||||
return $package;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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,24 @@ class Factory
|
|||
return $dm;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Config $config The configuration
|
||||
* @param Downloader\DownloadManager $dm Manager use to download sources
|
||||
*
|
||||
* @return Archiver\ArchiveManager
|
||||
*/
|
||||
public function createArchiveManager(Config $config, Downloader\DownloadManager $dm = null)
|
||||
{
|
||||
if (null === $dm) {
|
||||
$dm = $this->createDownloadManager(new IO\NullIO(), $config);
|
||||
}
|
||||
|
||||
$am = new Archiver\ArchiveManager($dm);
|
||||
$am->addArchiver(new Archiver\PharArchiver);
|
||||
|
||||
return $am;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Installer\InstallationManager
|
||||
*/
|
||||
|
|
|
@ -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().')';
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\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 <naderman@naderman.de>
|
||||
*/
|
||||
class ArchivableFilesFinder extends \FilterIterator
|
||||
{
|
||||
/**
|
||||
* @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(DIRECTORY_SEPARATOR, '/', $file->getRealPath())
|
||||
);
|
||||
|
||||
$exclude = false;
|
||||
foreach ($filters as $filter) {
|
||||
$exclude = $filter->filter($relativePath, $exclude);
|
||||
}
|
||||
return !$exclude;
|
||||
})
|
||||
->ignoreVCS(true)
|
||||
->ignoreDotFiles(false);
|
||||
|
||||
parent::__construct($this->finder->getIterator());
|
||||
}
|
||||
|
||||
public function accept()
|
||||
{
|
||||
return !$this->getInnerIterator()->current()->isDir();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Package\Archiver;
|
||||
|
||||
use Composer\Downloader\DownloadManager;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Package\PackageInterface;
|
||||
use Composer\Package\RootPackage;
|
||||
use Composer\Util\Filesystem;
|
||||
|
||||
/**
|
||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||
* @author Till Klampaeckel <till@php.net>
|
||||
*/
|
||||
class ArchiveManager
|
||||
{
|
||||
protected $downloadManager;
|
||||
|
||||
protected $archivers = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $overwriteFiles = true;
|
||||
|
||||
/**
|
||||
* @param DownloadManager $downloadManager A manager used to download package sources
|
||||
*/
|
||||
public function __construct(DownloadManager $downloadManager)
|
||||
{
|
||||
$this->downloadManager = $downloadManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArchiverInterface $archiver
|
||||
*/
|
||||
public function addArchiver(ArchiverInterface $archiver)
|
||||
{
|
||||
$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.
|
||||
*
|
||||
* @param PackageInterface $package The package to get a name for
|
||||
*
|
||||
* @return string A filename without an extension
|
||||
*/
|
||||
public 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()));
|
||||
}
|
||||
|
||||
if ($package->getSourceReference()) {
|
||||
$nameParts[] = substr(sha1($package->getSourceReference()), 0, 6);
|
||||
}
|
||||
|
||||
return implode('-', array_filter($nameParts, function ($p) {
|
||||
return !empty($p);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string $targetDir The diretory where to build the archive
|
||||
*
|
||||
* @return string The path of the created archive
|
||||
*/
|
||||
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())) {
|
||||
$usableArchiver = $archiver;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = $this->getPackageFilename($package);
|
||||
|
||||
// Archive filename
|
||||
$filesystem->ensureDirectoryExists($targetDir);
|
||||
$target = realpath($targetDir).'/'.$packageName.'.'.$format;
|
||||
$filesystem->ensureDirectoryExists(dirname($target));
|
||||
|
||||
if (!$this->overwriteFiles && file_exists($target)) {
|
||||
return $target;
|
||||
}
|
||||
|
||||
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
|
||||
return $usableArchiver->archive($sourcePath, $target, $format, $package->getArchiveExcludes());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Package\Archiver;
|
||||
|
||||
use Composer\Package\PackageInterface;
|
||||
|
||||
/**
|
||||
* @author Till Klampaeckel <till@php.net>
|
||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
interface ArchiverInterface
|
||||
{
|
||||
/**
|
||||
* Create an archive from the sources.
|
||||
*
|
||||
* @param string $sources The sources directory
|
||||
* @param string $target The target file
|
||||
* @param string $format The format used for archive
|
||||
* @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, array $excludes = array());
|
||||
|
||||
/**
|
||||
* Format supported by the archiver.
|
||||
*
|
||||
* @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, $sourceType);
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Package\Archiver;
|
||||
|
||||
use Symfony\Component\Finder;
|
||||
|
||||
/**
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
abstract class BaseExcludeFilter
|
||||
{
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Package\Archiver;
|
||||
|
||||
/**
|
||||
* An exclude filter which processes composer's own exclude rules
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class ComposerExcludeFilter extends BaseExcludeFilter
|
||||
{
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Package\Archiver;
|
||||
|
||||
/**
|
||||
* An exclude filter that processes gitignore and gitattributes
|
||||
*
|
||||
* It respects export-ignore git attributes
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class GitExcludeFilter extends BaseExcludeFilter
|
||||
{
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
public 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()
|
||||
*/
|
||||
public function parseGitAttributesLine($line)
|
||||
{
|
||||
$parts = preg_split('#\s+#', $line);
|
||||
|
||||
if (count($parts) != 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($parts[1] === 'export-ignore') {
|
||||
return $this->generatePattern($parts[0]);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Package\Archiver;
|
||||
|
||||
use Symfony\Component\Finder;
|
||||
|
||||
/**
|
||||
* An exclude filter that processes hgignore files
|
||||
*
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
class HgExcludeFilter extends BaseExcludeFilter
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Package\Archiver;
|
||||
|
||||
use Composer\Package\BasePackage;
|
||||
use Composer\Package\PackageInterface;
|
||||
|
||||
/**
|
||||
* @author Till Klampaeckel <till@php.net>
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||
*/
|
||||
class PharArchiver implements ArchiverInterface
|
||||
{
|
||||
protected static $formats = array(
|
||||
'zip' => \Phar::ZIP,
|
||||
'tar' => \Phar::TAR,
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function archive($sources, $target, $format, array $excludes = array())
|
||||
{
|
||||
$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);
|
||||
$phar->buildFromIterator($files, $sources);
|
||||
return $target;
|
||||
} catch (\UnexpectedValueException $e) {
|
||||
$message = sprintf("Could not create archive '%s' from '%s': %s",
|
||||
$target,
|
||||
$sources,
|
||||
$e->getMessage()
|
||||
);
|
||||
|
||||
throw new \RuntimeException($message, $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supports($format, $sourceType)
|
||||
{
|
||||
return isset(static::$formats[$format]);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(array $excludes)
|
||||
{
|
||||
$this->archiveExcludes = $excludes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getArchiveExcludes()
|
||||
{
|
||||
return $this->archiveExcludes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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'])) {
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Package\Archiver;
|
||||
|
||||
use Composer\Package\Archiver\ArchivableFilesFinder;
|
||||
use Composer\Util\Filesystem;
|
||||
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
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 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Package\Archiver;
|
||||
|
||||
use Composer\Factory;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Package\Archiver;
|
||||
use Composer\Package\Archiver\ArchiveManager;
|
||||
use Composer\Package\PackageInterface;
|
||||
|
||||
/**
|
||||
* @author Till Klampaeckel <till@php.net>
|
||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||
*/
|
||||
class ArchiveManagerTest extends ArchiverTest
|
||||
{
|
||||
protected $manager;
|
||||
protected $targetDir;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$factory = new Factory();
|
||||
$this->manager = $factory->createArchiveManager($factory->createConfig());
|
||||
$this->targetDir = $this->testDir.'/composer_archiver_tests';
|
||||
}
|
||||
|
||||
public function testUnknownFormat()
|
||||
{
|
||||
$this->setExpectedException('RuntimeException');
|
||||
|
||||
$package = $this->setupPackage();
|
||||
|
||||
$this->manager->archive($package, '__unknown_format__', $this->targetDir);
|
||||
}
|
||||
|
||||
public function testArchiveTar()
|
||||
{
|
||||
$this->setupGitRepo();
|
||||
|
||||
$package = $this->setupPackage();
|
||||
|
||||
$this->manager->archive($package, 'tar', $this->targetDir);
|
||||
|
||||
$target = $this->getTargetName($package, 'tar');
|
||||
$this->assertFileExists($target);
|
||||
|
||||
unlink($target);
|
||||
}
|
||||
|
||||
protected function getTargetName(PackageInterface $package, $format)
|
||||
{
|
||||
$packageName = $this->manager->getPackageFilename($package);
|
||||
$target = $this->targetDir.'/'.$packageName.'.'.$format;
|
||||
|
||||
return $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create local git repository to run tests against!
|
||||
*/
|
||||
protected function setupGitRepo()
|
||||
{
|
||||
$currentWorkDir = getcwd();
|
||||
chdir($this->testDir);
|
||||
|
||||
$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());
|
||||
}
|
||||
|
||||
$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', $output, $this->testDir);
|
||||
if ($result > 0) {
|
||||
chdir($currentWorkDir);
|
||||
throw new \RuntimeException('Could not commit: '.$this->process->getErrorOutput());
|
||||
}
|
||||
|
||||
chdir($currentWorkDir);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Package\Archiver;
|
||||
|
||||
use Composer\Util\Filesystem;
|
||||
use Composer\Util\ProcessExecutor;
|
||||
use Composer\Package\Package;
|
||||
|
||||
/**
|
||||
* @author Till Klampaeckel <till@php.net>
|
||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||
*/
|
||||
abstract class ArchiverTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \Composer\Util\Filesystem
|
||||
*/
|
||||
protected $filesystem;
|
||||
|
||||
/**
|
||||
* @var \Composer\Util\ProcessExecutor
|
||||
*/
|
||||
protected $process;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $testDir;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->filesystem = new Filesystem();
|
||||
$this->process = new ProcessExecutor();
|
||||
$this->testDir = sys_get_temp_dir().'/composer_archiver_test_'.mt_rand();
|
||||
$this->filesystem->ensureDirectoryExists($this->testDir);
|
||||
}
|
||||
|
||||
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');
|
||||
$package->setSourceUrl(realpath($this->testDir));
|
||||
$package->setSourceReference('master');
|
||||
$package->setSourceType('git');
|
||||
|
||||
return $package;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Package\Archiver;
|
||||
|
||||
use Composer\Package\Archiver\HgExcludeFilter;
|
||||
|
||||
/**
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
*/
|
||||
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)),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Test\Package\Archiver;
|
||||
|
||||
use Composer\Package\Archiver\PharArchiver;
|
||||
|
||||
/**
|
||||
* @author Till Klampaeckel <till@php.net>
|
||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||
*/
|
||||
class PharArchiverTest extends ArchiverTest
|
||||
{
|
||||
public function testTarArchive()
|
||||
{
|
||||
// Set up repository
|
||||
$this->setupDummyRepo();
|
||||
$package = $this->setupPackage();
|
||||
$target = sys_get_temp_dir().'/composer_archiver_test.tar';
|
||||
|
||||
// Test archive
|
||||
$archiver = new PharArchiver();
|
||||
$archiver->archive($package->getSourceUrl(), $target, 'tar', array('foo/bar', 'baz', '!/foo/bar/baz'));
|
||||
$this->assertFileExists($target);
|
||||
|
||||
unlink($target);
|
||||
}
|
||||
|
||||
public function testZipArchive()
|
||||
{
|
||||
// Set up repository
|
||||
$this->setupDummyRepo();
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a local dummy repository to run tests against!
|
||||
*/
|
||||
protected function setupDummyRepo()
|
||||
{
|
||||
$currentWorkDir = getcwd();
|
||||
chdir($this->testDir);
|
||||
|
||||
$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.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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')),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue