mirror of
https://github.com/composer/composer
synced 2025-05-09 00:22:53 +00:00
Define an option to exclude files in the archive command
This commit is contained in:
parent
3e26502561
commit
afcdad4b23
14 changed files with 168 additions and 8 deletions
|
@ -656,4 +656,29 @@ See [Vendor Binaries](articles/vendor-binaries.md) for more details.
|
||||||
|
|
||||||
Optional.
|
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) →
|
← [Command-line interface](03-cli.md) | [Repositories](05-repositories.md) →
|
||||||
|
|
|
@ -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": {
|
"repositories": {
|
||||||
"type": ["object", "array"],
|
"type": ["object", "array"],
|
||||||
"description": "A set of additional repositories where packages can be found.",
|
"description": "A set of additional repositories where packages can be found.",
|
||||||
|
|
|
@ -311,6 +311,10 @@ class AliasPackage extends BasePackage implements CompletePackageInterface
|
||||||
{
|
{
|
||||||
return $this->aliasOf->getNotificationUrl();
|
return $this->aliasOf->getNotificationUrl();
|
||||||
}
|
}
|
||||||
|
public function getArchiveExcludes()
|
||||||
|
{
|
||||||
|
return $this->aliasOf->getArchiveExcludes();
|
||||||
|
}
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')';
|
return parent::__toString().' (alias of '.$this->aliasOf->getVersion().')';
|
||||||
|
|
|
@ -89,6 +89,6 @@ class ArchiveManager
|
||||||
|
|
||||||
// Create the archive
|
// Create the archive
|
||||||
$sourceRef = $package->getSourceReference();
|
$sourceRef = $package->getSourceReference();
|
||||||
$usableArchiver->archive($sourcePath, $target, $format, $sourceRef);
|
$usableArchiver->archive($sourcePath, $target, $format, $sourceRef, $package->getArchiveExcludes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,9 @@ interface ArchiverInterface
|
||||||
* @param string $format The format used for archive
|
* @param string $format The format used for archive
|
||||||
* @param string $sourceRef The reference of the source to archive or null
|
* @param string $sourceRef The reference of the source to archive or null
|
||||||
* for the current reference
|
* 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.
|
* Format supported by the archiver.
|
||||||
|
|
|
@ -15,8 +15,11 @@ namespace Composer\Package\Archiver;
|
||||||
use Composer\Package\BasePackage;
|
use Composer\Package\BasePackage;
|
||||||
use Composer\Package\PackageInterface;
|
use Composer\Package\PackageInterface;
|
||||||
|
|
||||||
|
use Symfony\Component\Finder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Till Klampaeckel <till@php.net>
|
* @author Till Klampaeckel <till@php.net>
|
||||||
|
* @author Nils Adermann <naderman@naderman.de>
|
||||||
* @author Matthieu Moquet <matthieu@moquet.net>
|
* @author Matthieu Moquet <matthieu@moquet.net>
|
||||||
*/
|
*/
|
||||||
class PharArchiver implements ArchiverInterface
|
class PharArchiver implements ArchiverInterface
|
||||||
|
@ -29,11 +32,34 @@ class PharArchiver implements ArchiverInterface
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@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 {
|
try {
|
||||||
|
if (file_exists($target)) {
|
||||||
|
unlink($target);
|
||||||
|
}
|
||||||
$phar = new \PharData($target, null, null, static::$formats[$format]);
|
$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) {
|
} catch (\UnexpectedValueException $e) {
|
||||||
$message = sprintf("Could not create archive '%s' from '%s': %s",
|
$message = sprintf("Could not create archive '%s' from '%s': %s",
|
||||||
$target,
|
$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}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -58,6 +58,10 @@ class ArrayDumper
|
||||||
$data['dist']['shasum'] = $package->getDistSha1Checksum();
|
$data['dist']['shasum'] = $package->getDistSha1Checksum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($package->getArchiveExcludes()) {
|
||||||
|
$data['archive']['exclude'] = $package->getArchiveExcludes();
|
||||||
|
}
|
||||||
|
|
||||||
foreach (BasePackage::$supportedLinkTypes as $type => $opts) {
|
foreach (BasePackage::$supportedLinkTypes as $type => $opts) {
|
||||||
if ($links = $package->{'get'.ucfirst($opts['method'])}()) {
|
if ($links = $package->{'get'.ucfirst($opts['method'])}()) {
|
||||||
foreach ($links as $link) {
|
foreach ($links as $link) {
|
||||||
|
|
|
@ -150,6 +150,10 @@ class ArrayLoader implements LoaderInterface
|
||||||
$package->setNotificationUrl($config['notification-url']);
|
$package->setNotificationUrl($config['notification-url']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($config['archive']['exclude'])) {
|
||||||
|
$package->setArchiveExcludes($config['archive']['exclude']);
|
||||||
|
}
|
||||||
|
|
||||||
if ($package instanceof Package\CompletePackageInterface) {
|
if ($package instanceof Package\CompletePackageInterface) {
|
||||||
if (isset($config['scripts']) && is_array($config['scripts'])) {
|
if (isset($config['scripts']) && is_array($config['scripts'])) {
|
||||||
foreach ($config['scripts'] as $event => $listeners) {
|
foreach ($config['scripts'] as $event => $listeners) {
|
||||||
|
|
|
@ -51,6 +51,7 @@ class Package extends BasePackage
|
||||||
protected $suggests = array();
|
protected $suggests = array();
|
||||||
protected $autoload = array();
|
protected $autoload = array();
|
||||||
protected $includePaths = array();
|
protected $includePaths = array();
|
||||||
|
protected $archiveExcludes = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new in memory package.
|
* Creates a new in memory package.
|
||||||
|
@ -525,4 +526,22 @@ class Package extends BasePackage
|
||||||
{
|
{
|
||||||
return $this->notificationUrl;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,4 +308,11 @@ interface PackageInterface
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getPrettyString();
|
public function getPrettyString();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of patterns to exclude from package archives
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getArchiveExcludes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ class PharArchiverTest extends ArchiverTest
|
||||||
|
|
||||||
// Test archive
|
// Test archive
|
||||||
$archiver = new PharArchiver();
|
$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);
|
$this->assertFileExists($target);
|
||||||
|
|
||||||
unlink($target);
|
unlink($target);
|
||||||
|
@ -58,12 +58,25 @@ class PharArchiverTest extends ArchiverTest
|
||||||
$currentWorkDir = getcwd();
|
$currentWorkDir = getcwd();
|
||||||
chdir($this->testDir);
|
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) {
|
if (false === $result) {
|
||||||
chdir($currentWorkDir);
|
chdir($currentWorkDir);
|
||||||
throw new \RuntimeException('Could not save file.');
|
throw new \RuntimeException('Could not save file.');
|
||||||
}
|
}
|
||||||
|
|
||||||
chdir($currentWorkDir);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,6 +130,14 @@ class ArrayDumperTest extends \PHPUnit_Framework_TestCase
|
||||||
'extra',
|
'extra',
|
||||||
array('class' => 'MyVendor\\Installer')
|
array('class' => 'MyVendor\\Installer')
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'archive',
|
||||||
|
array('/foo/bar', 'baz', '!/foo/bar/baz'),
|
||||||
|
'archiveExcludes',
|
||||||
|
array(
|
||||||
|
'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'),
|
||||||
|
),
|
||||||
|
),
|
||||||
array(
|
array(
|
||||||
'require',
|
'require',
|
||||||
array(new Link('foo', 'foo/bar', new VersionConstraint('=', '1.0.0.0'), 'requires', '1.0.0')),
|
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',
|
'target-dir' => 'some/prefix',
|
||||||
'extra' => array('random' => array('things' => 'of', 'any' => 'shape')),
|
'extra' => array('random' => array('things' => 'of', 'any' => 'shape')),
|
||||||
'bin' => array('bin1', 'bin/foo'),
|
'bin' => array('bin1', 'bin/foo'),
|
||||||
|
'archive' => array(
|
||||||
|
'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
$package = $this->loader->load($config);
|
$package = $this->loader->load($config);
|
||||||
|
|
|
@ -123,6 +123,9 @@ class ValidatingArrayLoaderTest extends \PHPUnit_Framework_TestCase
|
||||||
'vendor-dir' => 'vendor',
|
'vendor-dir' => 'vendor',
|
||||||
'process-timeout' => 10000,
|
'process-timeout' => 10000,
|
||||||
),
|
),
|
||||||
|
'archive' => array(
|
||||||
|
'exclude' => array('/foo/bar', 'baz', '!/foo/bar/baz'),
|
||||||
|
),
|
||||||
'scripts' => array(
|
'scripts' => array(
|
||||||
'post-update-cmd' => 'Foo\\Bar\\Baz::doSomething',
|
'post-update-cmd' => 'Foo\\Bar\\Baz::doSomething',
|
||||||
'post-install-cmd' => array(
|
'post-install-cmd' => array(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue