Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ vendor/
phpunit.xml
phpcs.xml
.phpcs.xml
.phpunit.result.cache
.phpunit.cache
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
],
"require": {
"php": ">=7.2",
"wp-cli/wp-cli": "^2",
"wp-cli/wp-cli": "^2.13",
"inmarelibero/gitignore-checker": "^1.0.4"
},
"require-dev": {
Expand Down
47 changes: 47 additions & 0 deletions features/distignore.feature
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,50 @@ Feature: Generate a distribution archive of a project with .distignore
"""
Error: Broken symlink at /symlink. Target missing at
"""

Scenario: Efficiently ignores directories with many files
# Performance test: ensure ignored directories are not scanned
# @see https://github.com/wp-cli/dist-archive-command/issues/115
Given an empty directory
And a foo/.distignore file:
"""
node_modules
.git
"""
And a foo/plugin.php file:
"""
<?php
/**
* Plugin Name: Test Plugin
* Version: 1.0.0
*/
"""
And a foo/readme.txt file:
"""
=== Test Plugin ===
"""

When I run `mkdir -p foo/node_modules/package1 foo/node_modules/package2 foo/node_modules/package3`
Then STDERR should be empty

When I run `sh -c 'i=1; while [ $i -le 50 ]; do touch foo/node_modules/package1/file$i.js; i=$((i+1)); done'`
Then STDERR should be empty

When I run `sh -c 'i=1; while [ $i -le 50 ]; do touch foo/node_modules/package2/file$i.js; i=$((i+1)); done'`
Then STDERR should be empty

When I run `sh -c 'i=1; while [ $i -le 50 ]; do touch foo/node_modules/package3/file$i.js; i=$((i+1)); done'`
Then STDERR should be empty

When I run `wp dist-archive foo`
Then STDOUT should match /^Success: Created foo\.[^ ]+ \(Size: \d+(?:\.\d*)? [a-zA-Z]{1,3}\)$/
And STDERR should be empty

When I run `rm -rf foo`
Then the foo directory should not exist

When I try `unzip foo.*.zip`
Then the foo directory should exist
And the foo/plugin.php file should exist
And the foo/readme.txt file should exist
And the foo/node_modules directory should not exist
26 changes: 26 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/4.8/phpunit.xsd"
bootstrap="vendor/autoload.php"
backupGlobals="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTestsThatDoNotTestAnything="true"
beStrictAboutTodoAnnotatedTests="true"
convertErrorsToExceptions="true"
convertWarningsToExceptions="true"
convertNoticesToExceptions="true"
convertDeprecationsToExceptions="true"
colors="true"
verbose="true">
<testsuites>
<testsuite name="wp-cli/dist-archive-command tests">
<directory suffix="Test.php">tests</directory>
</testsuite>
</testsuites>

<coverage processUncoveredFiles="false">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
</phpunit>
63 changes: 42 additions & 21 deletions src/Dist_Archive_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -493,46 +493,61 @@ protected function is_path_contains_symlink( $source_dir_path ) {
private function get_file_list( $source_dir_path, $excluded = false ) {

$included_files = [];
$excluded_files = [];

$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator( $source_dir_path, RecursiveDirectoryIterator::SKIP_DOTS ),
$directory_iterator = new RecursiveDirectoryIterator( $source_dir_path, RecursiveDirectoryIterator::SKIP_DOTS );
$filter_iterator = new Distignore_Filter_Iterator( $directory_iterator, $this->checker, $source_dir_path );
$iterator = new RecursiveIteratorIterator(
$filter_iterator,
RecursiveIteratorIterator::SELF_FIRST
);

/**
* @var SplFileInfo $item
*/
foreach ( $iterator as $item ) {
$relative_filepath = str_replace( $source_dir_path, '', $item->getPathname() );
try {
if ( $this->checker->isPathIgnored( $relative_filepath ) ) {
$excluded_files[] = $relative_filepath;
} else {
$included_files[] = $relative_filepath;
}
} catch ( \Inmarelibero\GitIgnoreChecker\Exception\InvalidArgumentException $exception ) {
$pathname = $item->getPathname();
if ( 0 === strpos( $pathname, $source_dir_path ) ) {
$relative_filepath = substr( $pathname, strlen( $source_dir_path ) );
} else {
$relative_filepath = $pathname;
}

// Check if this item had an error during filtering.
$error = $filter_iterator->getErrorForItem( $relative_filepath );
if ( $error ) {
if ( $item->isLink() && ! file_exists( (string) readlink( $item->getPathname() ) ) ) {
WP_CLI::error( "Broken symlink at {$relative_filepath}. Target missing at {$item->getLinkTarget()}." );
} else {
WP_CLI::error( $exception->getMessage() );
WP_CLI::error( $error->getMessage() );
}
}
}

// Check all excluded directories and remove them from the excluded list if they contain included files.
foreach ( $excluded_files as $excluded_file_index => $excluded_relative_path ) {
if ( ! is_dir( $source_dir_path . $excluded_relative_path ) ) {
continue;
// Check if this item is ignored (directories may still be yielded even if ignored).
if ( ! $filter_iterator->isPathIgnoredCached( $relative_filepath ) ) {
$included_files[] = $relative_filepath;
}
foreach ( $included_files as $included_relative_path ) {
if ( 0 === strpos( $included_relative_path, $excluded_relative_path ) ) {
unset( $excluded_files[ $excluded_file_index ] );
}

if ( $excluded ) {
// Get excluded files from the filter iterator.
$excluded_files = $filter_iterator->getExcludedFiles();

// Check all excluded directories and remove them from the excluded list if they contain included files.
foreach ( $excluded_files as $excluded_file_index => $excluded_relative_path ) {
if ( ! is_dir( $source_dir_path . $excluded_relative_path ) ) {
continue;
}
foreach ( $included_files as $included_relative_path ) {
if ( 0 === strpos( $included_relative_path, $excluded_relative_path ) ) {
unset( $excluded_files[ $excluded_file_index ] );
}
}
}

return $excluded_files;
}

return $excluded ? $excluded_files : $included_files;
return $included_files;
}

/**
Expand Down Expand Up @@ -561,6 +576,12 @@ private function get_size_format( $bytes, $decimals = 0 ) {
$size_key = floor( log( $bytes ) / log( 1000 ) );
$sizes = [ 'B', 'KB', 'MB', 'GB', 'TB' ];

if ( is_infinite( $size_key ) ) {
$size_key = 0;
}

$size_key = (int) $size_key;

$size_format = isset( $sizes[ $size_key ] ) ? $sizes[ $size_key ] : $sizes[0];

// Display the size as a number.
Expand Down
Loading