Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
59 changes: 49 additions & 10 deletions features/bootstrap/FeatureContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ class FeatureContext extends BehatContext implements ClosuredContextInterface {
private $running_procs = array();

/**
* Array of variables available as {VARIABLE_NAME}. Some are always set: CORE_CONFIG_SETTINGS, SRC_DIR, CACHE_DIR, WP_VERSION-version-latest. Some are step-dependent:
* RUN_DIR, SUITE_CACHE_DIR, COMPOSER_LOCAL_REPOSITORY, PHAR_PATH. Scenarios can define their own variables using "Given save" steps. Variables are reset for each scenario.
* Array of variables available as {VARIABLE_NAME}. Some are always set: CORE_CONFIG_SETTINGS, SRC_DIR, CACHE_DIR, WP_VERSION-version-latest.
* Some are step-dependent: RUN_DIR, SUITE_CACHE_DIR, COMPOSER_LOCAL_REPOSITORY, PHAR_PATH. One is set on use: INVOKE_WP_CLI_WITH_PHP_ARGS-args.
* Scenarios can define their own variables using "Given save" steps. Variables are reset for each scenario.
*/
public $variables = array();

Expand Down Expand Up @@ -117,8 +118,9 @@ private static function get_process_env_variables() {
// Ensure we're using the expected `wp` binary
$bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ?: realpath( __DIR__ . '/../../bin' );
$vendor_dir = realpath( __DIR__ . '/../../vendor/bin' );
$path_separator = Utils\is_windows() ? ';' : ':';
$env = array(
'PATH' => $bin_dir . ':' . $vendor_dir . ':' . getenv( 'PATH' ),
'PATH' => $bin_dir . $path_separator . $vendor_dir . $path_separator . getenv( 'PATH' ),
'BEHAT_RUN' => 1,
'HOME' => sys_get_temp_dir() . '/wp-cli-home',
);
Expand Down Expand Up @@ -328,20 +330,57 @@ public function getHookDefinitionResources() {
}

/**
* Replace {VARIABLE_NAME}. Note that variable names can only contain uppercase letters and underscores (no numbers).
* Replace standard {VARIABLE_NAME} variables and the special {INVOKE_WP_CLI_WITH_PHP_ARGS-args} and {WP_VERSION-version-latest} variables.
* Note that standard variable names can only contain uppercase letters, digits and underscores and cannot begin with a digit.
*/
public function replace_variables( $str ) {
$ret = preg_replace_callback( '/\{([A-Z_]+)\}/', array( $this, '_replace_var' ), $str );
if ( false !== strpos( $str, '{INVOKE_WP_CLI_WITH_PHP_ARGS-' ) ) {
$str = $this->replace_invoke_wp_cli_with_php_args( $str );
}
$str = preg_replace_callback( '/\{([A-Z_][A-Z_0-9]*)\}/', array( $this, 'replace_var' ), $str );
if ( false !== strpos( $str, '{WP_VERSION-' ) ) {
$ret = $this->_replace_wp_versions( $ret );
$str = $this->replace_wp_versions( $str );
}
return $str;
}

/**
* Substitute {INVOKE_WP_CLI_WITH_PHP_ARGS-args} variables.
*/
private function replace_invoke_wp_cli_with_php_args( $str ) {
static $phar_path = null, $shell_path = null;

if ( null === $phar_path ) {
$phar_path = false;
$phar_begin = '#!/usr/bin/env php';
$phar_begin_len = strlen( $phar_begin );
if ( ( $bin_dir = getenv( 'WP_CLI_BIN_DIR' ) ) && file_exists( $bin_dir . '/wp' ) && $phar_begin === file_get_contents( $bin_dir . '/wp', false, null, 0, $phar_begin_len ) ) {
$phar_path = $bin_dir . '/wp';
} else {
$src_dir = dirname( dirname( __DIR__ ) );
$bin_path = $src_dir . '/bin/wp';
$vendor_bin_path = $src_dir . '/vendor/bin/wp';
if ( file_exists( $bin_path ) && is_executable( $bin_path ) ) {
$shell_path = $bin_path;
} elseif ( file_exists( $vendor_bin_path ) && is_executable( $vendor_bin_path ) ) {
$shell_path = $vendor_bin_path;
} else {
$shell_path = 'wp';
}
}
}
return $ret;

$str = preg_replace_callback( '/{INVOKE_WP_CLI_WITH_PHP_ARGS-([^}]*)}/', function ( $matches ) use ( $phar_path, $shell_path ) {
return $phar_path ? "php {$matches[1]} {$phar_path}" : ( 'WP_CLI_PHP_ARGS=' . escapeshellarg( $matches[1] ) . ' ' . $shell_path );
}, $str );

return $str;
}

/**
* Replace variables callback.
*/
private function _replace_var( $matches ) {
private function replace_var( $matches ) {
$cmd = $matches[0];

foreach ( array_slice( $matches, 1 ) as $key ) {
Expand All @@ -352,9 +391,9 @@ private function _replace_var( $matches ) {
}

/**
* Substitute "{WP_VERSION-version-latest}" variables.
* Substitute {WP_VERSION-version-latest} variables.
*/
private function _replace_wp_versions( $str ) {
private function replace_wp_versions( $str ) {
static $wp_versions = null;
if ( null === $wp_versions ) {
$wp_versions = array();
Expand Down
4 changes: 3 additions & 1 deletion features/bootstrap/Process.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace WP_CLI;

use WP_CLI\Utils;

/**
* Run a system process, and learn what happened.
*/
Expand Down Expand Up @@ -67,7 +69,7 @@ private function __construct() {}
public function run() {
$start_time = microtime( true );

$proc = proc_open( $this->command, self::$descriptors, $pipes, $this->cwd, $this->env );
$proc = Utils\proc_open_compat( $this->command, self::$descriptors, $pipes, $this->cwd, $this->env );

$stdout = stream_get_contents( $pipes[1] );
fclose( $pipes[1] );
Expand Down
69 changes: 53 additions & 16 deletions features/bootstrap/utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -383,15 +383,11 @@ function launch_editor_for_input( $input, $filename = 'WP-CLI' ) {

$editor = getenv( 'EDITOR' );
if ( ! $editor ) {
$editor = 'vi';

if ( isset( $_SERVER['OS'] ) && false !== strpos( $_SERVER['OS'], 'indows' ) ) {
$editor = 'notepad';
}
$editor = is_windows() ? 'notepad' : 'vi';
}

$descriptorspec = array( STDIN, STDOUT, STDERR );
$process = proc_open( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes );
$process = proc_open_compat( "$editor " . escapeshellarg( $tmpfile ), $descriptorspec, $pipes );
$r = proc_close( $process );
if ( $r ) {
exit( $r );
Expand Down Expand Up @@ -453,7 +449,7 @@ function run_mysql_command( $cmd, $assoc_args, $descriptors = null ) {

$final_cmd = force_env_on_nix_systems( $cmd ) . assoc_args_to_str( $assoc_args );

$proc = proc_open( $final_cmd, $descriptors, $pipes );
$proc = proc_open_compat( $final_cmd, $descriptors, $pipes );
if ( ! $proc ) {
exit( 1 );
}
Expand Down Expand Up @@ -790,14 +786,8 @@ function get_temp_dir() {
return $temp;
}

$temp = '/tmp/';

// `sys_get_temp_dir()` introduced PHP 5.2.1.
if ( $try = sys_get_temp_dir() ) {
$temp = trailingslashit( $try );
} elseif ( $try = ini_get( 'upload_tmp_dir' ) ) {
$temp = trailingslashit( $try );
}
// `sys_get_temp_dir()` introduced PHP 5.2.1. Will always return something.
$temp = trailingslashit( sys_get_temp_dir() );

if ( ! is_writable( $temp ) ) {
\WP_CLI::warning( "Temp directory isn't writable: {$temp}" );
Expand Down Expand Up @@ -1129,7 +1119,7 @@ function get_suggestion( $target, array $options, $threshold = 2 ) {
'v' => 'version',
);

if ( array_key_exists( $target, $suggestion_map ) ) {
if ( array_key_exists( $target, $suggestion_map ) && in_array( $suggestion_map[ $target ], $options, true ) ) {
return $suggestion_map[ $target ];
}

Expand Down Expand Up @@ -1318,3 +1308,50 @@ function get_php_binary() {

return 'php';
}

/**
* Windows compatible `proc_open()`.
* Works around bug in PHP, and also deals with *nix-like `ENV_VAR=blah cmd` environment variable prefixes.
*
* @access public
*
* @param string $command Command to execute.
* @param array $descriptorspec Indexed array of descriptor numbers and their values.
* @param array &$pipes Indexed array of file pointers that correspond to PHP's end of any pipes that are created.
* @param string $cwd Initial working directory for the command.
* @param array $env Array of environment variables.
* @param array $other_options Array of additional options (Windows only).
*
* @return string Command stripped of any environment variable settings.
*/
function proc_open_compat( $cmd, $descriptorspec, &$pipes, $cwd = null, $env = null, $other_options = null ) {
if ( is_windows() ) {
// Need to encompass the whole command in double quotes - PHP bug https://bugs.php.net/bug.php?id=49139
$cmd = '"' . _proc_open_compat_win_env( $cmd, $env ) . '"';
}
return proc_open( $cmd, $descriptorspec, $pipes, $cwd, $env, $other_options );
}

/**
* For use by `proc_open_compat()` only. Separated out for ease of testing. Windows only.
* Turns *nix-like `ENV_VAR=blah command` environment variable prefixes into stripped `cmd` with prefixed environment variables added to passed in environment array.
*
* @access private
*
* @param string $command Command to execute.
* @param array &$env Array of existing environment variables. Will be modified if any settings in command.
*
* @return string Command stripped of any environment variable settings.
*/
function _proc_open_compat_win_env( $cmd, &$env ) {
if ( false !== strpos( $cmd, '=' ) ) {
while ( preg_match( '/^([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|[^ ]*) /', $cmd, $matches ) ) {
$cmd = substr( $cmd, strlen( $matches[0] ) );
if ( null === $env ) {
$env = array();
}
$env[ $matches[1] ] = isset( $matches[2][0] ) && '"' === $matches[2][0] ? substr( $matches[2], 1, -1 ) : $matches[2];
}
}
return $cmd;
}
2 changes: 1 addition & 1 deletion features/package-install.feature
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ Feature: Install WP-CLI packages
rocket
"""

Scenario: Install a package in a local zip
Scenario: Install a package from a local zip
Given an empty directory
And I run `wget -q -O google-sitemap-generator-cli.zip https://github.com/wp-cli/google-sitemap-generator-cli/archive/master.zip`

Expand Down
147 changes: 147 additions & 0 deletions features/package.feature
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,150 @@ Feature: Manage WP-CLI packages

When I run `wp --require=bad-command.php package list`
Then STDERR should be empty

Scenario: Revert the WP-CLI packages composer.json when fail to install/uninstall a package due to memory limit
Given an empty directory
When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--dmemory_limit=32M -ddisable_functions=ini_set} package install danielbachhuber/wp-cli-reset-post-date-command`
Then the return code should not be 0
And STDERR should contain:
"""
Reverted composer.json.
"""

When I run `wp package install danielbachhuber/wp-cli-reset-post-date-command`
Then STDOUT should contain:
"""
Success: Package installed.
"""

When I try `{INVOKE_WP_CLI_WITH_PHP_ARGS--dmemory_limit=32M -ddisable_functions=ini_set} package uninstall danielbachhuber/wp-cli-reset-post-date-command`
Then the return code should not be 0
And STDERR should contain:
"""
Reverted composer.json.
"""

# Create a default composer.json first to compare.
When I run `WP_CLI_PACKAGES_DIR={RUN_DIR}/mypackages wp package list`
Then the {RUN_DIR}/mypackages/composer.json file should exist
And save the {RUN_DIR}/mypackages/composer.json file as {MYPACKAGES_COMPOSER_JSON}

When I try `WP_CLI_PACKAGES_DIR={RUN_DIR}/mypackages {INVOKE_WP_CLI_WITH_PHP_ARGS--dmemory_limit=32M -ddisable_functions=ini_set} package install danielbachhuber/wp-cli-reset-post-date-command`
Then the return code should not be 0
And STDERR should contain:
"""
Reverted composer.json.
"""
And the mypackages/composer.json file should be:
"""
{MYPACKAGES_COMPOSER_JSON}
"""

@github-api
Scenario: Try to run with a bad WP_CLI_PACKAGES_DIR/composer.json
Given an empty directory
And a packages-bad-json/composer.json file:
"""
{
"name": "wp-cli/wp-cli",
}
"""

When I try `WP_CLI_PACKAGES_DIR={RUN_DIR}/packages-bad-json wp package list`
Then the return code should be 1
And STDERR should contain:
"""
Error: Failed to get composer instance
"""
And STDERR should contain:
"""
Parse error
"""
And STDOUT should be empty

When I try `WP_CLI_PACKAGES_DIR={RUN_DIR}/packages-bad-json wp package install danielbachhuber/wp-cli-reset-post-date-command`
Then the return code should be 1
And STDERR should contain:
"""
Error: Failed to parse
"""
And STDERR should contain:
"""
Parse error
"""
And STDOUT should contain:
"""
Installing
"""

When I try `WP_CLI_PACKAGES_DIR={RUN_DIR}/packages-bad-json wp package update`
Then the return code should be 1
And STDERR should contain:
"""
Error: Failed to get composer instance
"""
And STDERR should contain:
"""
Parse error
"""
And STDOUT should be empty

Given a packages-no-such-package/composer.json file:
"""
{
"name": "wp-cli/wp-cli",
"repositories": {
"no-such-gituser/no-such-package": {
"type": "vcs",
"url": "https://github.com/no-such-gituser/no-such-package.git"
}
},
"require": {
"no-such-gituser/no-such-package": "dev-master"
}
}
"""
And save the {RUN_DIR}/packages-no-such-package/composer.json file as {NO_SUCH_PACKAGE_COMPOSER_JSON}

When I try `WP_CLI_PACKAGES_DIR={RUN_DIR}/packages-no-such-package wp package install danielbachhuber/wp-cli-reset-post-date-command`
Then the return code should be 1
And STDERR should contain:
"""
Error: Package installation failed.
"""
And STDERR should contain:
"""
Repository not found
"""
And STDERR should contain:
"""
Reverted composer.json.
"""
And STDOUT should contain:
"""
Installing
"""
And the packages-no-such-package/composer.json file should be:
"""
{NO_SUCH_PACKAGE_COMPOSER_JSON}
"""

When I try `WP_CLI_PACKAGES_DIR={RUN_DIR}/packages-no-such-package wp package update`
Then the return code should be 1
And STDERR should contain:
"""
Error: Failed to update packages.
"""
And STDERR should contain:
"""
Repository not found
"""
And STDERR should not contain:
"""
Reverted composer.json.
"""
And STDOUT should not be empty
And the packages-no-such-package/composer.json file should be:
"""
{NO_SUCH_PACKAGE_COMPOSER_JSON}
"""
Loading