Skip to content
Merged
21 changes: 11 additions & 10 deletions features/package-install.feature
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ Feature: Install WP-CLI packages
"""
And the {PACKAGE_PATH}composer.json file should not contain:
"""
"wp-cli/google-sitemap-generator-cli": "dev-master"
"wp-cli/google-sitemap-generator-cli":
"""
And the {PACKAGE_PATH}composer.json file should not contain:
"""
Expand Down Expand Up @@ -474,8 +474,9 @@ Feature: Install WP-CLI packages
Scenario: Install a package from Git using a shortened package identifier
Given an empty directory

When I run `wp package install wp-cli-test/github-test-command`
Then STDOUT should contain:
When I try `wp package install wp-cli-test/github-test-command`
Then the return code should be 0
And STDOUT should contain:
"""
Installing package wp-cli-test/github-test-command (dev-master)
"""
Expand Down Expand Up @@ -792,7 +793,7 @@ Feature: Install WP-CLI packages
"""
Warning: Package name mismatch...Updating from git name 'GeekPress/wp-rocket-cli' to composer.json name 'wp-media/wp-rocket-cli'.
"""
And STDOUT should match /Installing package wp-media\/wp-rocket-cli \(dev-/
And STDOUT should match /Installing package wp-media\/wp-rocket-cli \([^)]+\)/
# This path is sometimes changed on Macs to prefix with /private
And STDOUT should contain:
"""
Expand Down Expand Up @@ -957,7 +958,7 @@ Feature: Install WP-CLI packages

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`
And I run `curl -fSsL -o google-sitemap-generator-cli.zip https://github.com/wp-cli/google-sitemap-generator-cli/archive/main.zip`

When I run `wp package install google-sitemap-generator-cli.zip`
Then STDOUT should contain:
Expand Down Expand Up @@ -1093,7 +1094,7 @@ Feature: Install WP-CLI packages
Error: Couldn't download package from 'https://github.com/wp-cli/google-sitemap-generator.zip' (HTTP code 404).
"""

When I run `wp package install https://github.com/wp-cli/google-sitemap-generator-cli/archive/master.zip`
When I run `wp package install https://github.com/wp-cli/google-sitemap-generator-cli/archive/main.zip`
Then STDOUT should contain:
"""
Installing package wp-cli/google-sitemap-generator-cli (dev-
Expand Down Expand Up @@ -1201,9 +1202,9 @@ Feature: Install WP-CLI packages
"""
{PACKAGE_PATH}composer.json to require the package...
"""
And STDOUT should match /Registering .*?path-command as a path repository\.\.\./
And STDOUT should contain:
"""
Registering {CURRENT_PATH}/path-command as a path repository...
Using Composer to install the package...
"""
And STDOUT should contain:
Expand Down Expand Up @@ -1290,9 +1291,9 @@ Feature: Install WP-CLI packages
"""
{PACKAGE_PATH}composer.json to require the package...
"""
And STDOUT should match /Registering .*?path-command as a path repository\.\.\./
And STDOUT should contain:
"""
Registering {CURRENT_PATH}/path-command as a path repository...
Using Composer to install the package...
"""
And STDOUT should contain:
Expand Down Expand Up @@ -1374,11 +1375,11 @@ Feature: Install WP-CLI packages
"""
And STDOUT should be empty

When I try `wp package install https://example.com/non-existent-zip-asdfasdf.zip`
When I try `wp package install http://example.com/non-existent-zip-asdfasdf.zip`
Then the return code should be 1
And STDERR should contain:
"""
Error: Couldn't download package from 'https://example.com/non-existent-zip-asdfasdf.zip'
Error: Couldn't download package from 'http://example.com/non-existent-zip-asdfasdf.zip'
"""
And STDOUT should be empty

Expand Down
6 changes: 3 additions & 3 deletions features/package-update.feature
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Feature: Update WP-CLI packages
| name | update |
| wp-cli-test/updateable-package | available |

When I run `sed -i.bak s/v1.0.0/\>=1.0.0/g {PACKAGE_PATH}/composer.json`
When I run `wp eval "file_put_contents( '{PACKAGE_PATH}composer.json', str_replace( 'v1.0.0', '>=1.0.0', file_get_contents( '{PACKAGE_PATH}composer.json' ) ) );" --skip-wordpress`
Then the return code should be 0

When I run `cat {PACKAGE_PATH}/composer.json`
Expand Down Expand Up @@ -114,7 +114,7 @@ Feature: Update WP-CLI packages
Success: Package installed.
"""

When I run `sed -i.bak s/v1.0.0/\>=1.0.0/g {PACKAGE_PATH}/composer.json`
When I run `wp eval "file_put_contents( '{PACKAGE_PATH}composer.json', str_replace( 'v1.0.0', '>=1.0.0', file_get_contents( '{PACKAGE_PATH}composer.json' ) ) );" --skip-wordpress`
Then the return code should be 0

When I run `wp package update wp-cli-test/updateable-package`
Expand Down Expand Up @@ -147,7 +147,7 @@ Feature: Update WP-CLI packages
Success: Package installed.
"""

When I run `sed -i.bak s/v1.0.0/\>=1.0.0/g {PACKAGE_PATH}/composer.json`
When I run `wp eval "file_put_contents( '{PACKAGE_PATH}composer.json', str_replace( 'v1.0.0', '>=1.0.0', file_get_contents( '{PACKAGE_PATH}composer.json' ) ) );" --skip-wordpress`
Then the return code should be 0

When I run `wp package update wp-cli-test/updateable-package danielbachhuber/wp-cli-reset-post-date-command`
Expand Down
35 changes: 13 additions & 22 deletions src/Package_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ public function install( $args, $assoc_args ) {
$this->register_revert_shutdown_function( $json_path, $composer_backup, $revert );

// Add the 'require' to composer.json
WP_CLI::log( sprintf( 'Updating %s to require the package...', $json_path ) );
WP_CLI::log( sprintf( 'Updating %s to require the package...', Path::normalize( $json_path ) ) );
$json_manipulator = new JsonManipulator( $composer_backup );
$json_manipulator->addMainKey( 'name', 'wp-cli/wp-cli' );
$json_manipulator->addMainKey( 'version', self::get_wp_cli_version_composer() );
Expand Down Expand Up @@ -526,12 +526,12 @@ public function list_( $args, $assoc_args ) {
public function path( $args ) {
$packages_dir = WP_CLI::get_runner()->get_packages_dir_path();
if ( ! empty( $args ) ) {
$packages_dir .= 'vendor/' . $args[0];
$packages_dir .= 'vendor' . DIRECTORY_SEPARATOR . $args[0];
if ( ! is_dir( $packages_dir ) ) {
WP_CLI::error( 'Invalid package name.' );
}
}
WP_CLI::line( $packages_dir );
WP_CLI::line( Path::normalize( $packages_dir ) );
}

/**
Expand Down Expand Up @@ -835,12 +835,12 @@ public function uninstall( $args, $assoc_args ) {
$this->register_revert_shutdown_function( $json_path, $composer_backup, $revert );

// Remove the 'require' from composer.json.
WP_CLI::log( sprintf( 'Removing require statement for package \'%s\' from %s', $package_name, $json_path ) );
WP_CLI::log( sprintf( 'Removing require statement for package \'%s\' from %s', $package_name, Path::normalize( $json_path ) ) );
$manipulator = new JsonManipulator( $composer_backup );
$manipulator->removeSubNode( 'require', $package_name, true /*caseInsensitive*/ );

// Remove the 'repository' details from composer.json.
WP_CLI::log( sprintf( 'Removing repository details from %s', $json_path ) );
WP_CLI::log( sprintf( 'Removing repository details from %s', Path::normalize( $json_path ) ) );
$manipulator->removeSubNode( 'repositories', $package_name, true /*caseInsensitive*/ );

file_put_contents( $json_path, $manipulator->getContents() );
Expand Down Expand Up @@ -1158,7 +1158,7 @@ private function get_installed_package_by_name( $package_name ) {
* @return array Two-element array containing package name and version.
*/
private static function get_package_name_and_version_from_dir_package( $dir_package ) {
$composer_file = $dir_package . '/composer.json';
$composer_file = Path::normalize( $dir_package . '/composer.json' );
if ( ! file_exists( $composer_file ) ) {
WP_CLI::error( sprintf( "Invalid package: composer.json file '%s' not found.", $composer_file ) );
}
Expand Down Expand Up @@ -1192,21 +1192,12 @@ private function get_composer_json_path() {

if ( null === $composer_path || getenv( 'WP_CLI_TEST_PACKAGE_GET_COMPOSER_JSON_PATH' ) ) {

if ( getenv( 'WP_CLI_PACKAGES_DIR' ) ) {
$composer_path = Path::trailingslashit( getenv( 'WP_CLI_PACKAGES_DIR' ) ) . 'composer.json';
} else {
$composer_path = Path::trailingslashit( Path::get_home_dir() ) . '.wp-cli/packages/composer.json';
}
$packages_dir = WP_CLI::get_runner()->get_packages_dir_path();
$composer_path = rtrim( $packages_dir, '/\\' ) . DIRECTORY_SEPARATOR . 'composer.json';

// `composer.json` and its directory might need to be created
if ( ! file_exists( $composer_path ) ) {
$composer_path = $this->create_default_composer_json( $composer_path );
} else {
$composer_path = realpath( $composer_path );
if ( false === $composer_path ) {
$error = error_get_last();
WP_CLI::error( sprintf( "Composer path '%s' for packages/composer.json not found: %s", $composer_path, $error['message'] ) );
}
}
}

Expand Down Expand Up @@ -1238,11 +1229,7 @@ private function create_default_composer_json( $composer_path ) {
}
}

$composer_dir = realpath( $composer_dir );
if ( false === $composer_dir ) {
$error = error_get_last();
WP_CLI::error( sprintf( "Composer directory '%s' for packages not found: %s", $composer_dir, $error['message'] ) );
}
// No realpath() here to preserve short names on Windows if applicable.

$composer_path = Path::trailingslashit( $composer_dir ) . Path::basename( $composer_path );

Expand Down Expand Up @@ -1346,6 +1333,10 @@ private function maybe_add_git_suffix( $package_name ) {
if ( preg_match( '#\.git(?::[^:]+)?$#i', $package_name ) ) {
return $package_name;
}
// Skip if it looks like a zip file.
if ( preg_match( '#\.zip$#i', $package_name ) ) {
return $package_name;
}
// Append .git for GitHub/GitLab HTTPS or SSH URLs, preserving any :version suffix.
// Pattern: (https?://github.com/<user>/<repo>[...subgroups...] or git@github.com:<user>/<repo>[...subgroups...])(:version)?
if ( preg_match( '#^((?:https?://(?:github|gitlab)\.com/|git@(?:github|gitlab)\.com:)[^/:]+(?:/[^/:]+)*)(:.*)?$#i', $package_name, $matches ) ) {
Expand Down
23 changes: 15 additions & 8 deletions tests/phpunit/ComposerJsonTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,12 @@ public function set_up() {
}
$class_wp_cli_capture_exit->setValue( null, true );

$this->temp_dir = Utils\get_temp_dir() . uniqid( 'wp-cli-test-package-composer-json-', true ) . '/';
$temp_dir = Utils\get_temp_dir();
if ( Utils\is_windows() ) {
$temp_dir = realpath( $temp_dir ) ?: $temp_dir;
$temp_dir = str_replace( '\\', '/', $temp_dir );
}
$this->temp_dir = rtrim( $temp_dir, '/' ) . '/' . uniqid( 'wp-cli-test-package-composer-json-', true ) . '/';
mkdir( $this->temp_dir );
}

Expand Down Expand Up @@ -80,7 +85,7 @@ public function test_create_default_composer_json() {
// Succeed.
$expected = $this->temp_dir . 'packages/composer.json';
$actual = $create_default_composer_json->invoke( $package, $expected );
$this->assertSame( $expected, $this->mac_safe_path( $actual ) );
$this->assertSame( $this->mac_safe_path( realpath( $expected ) ?: $expected ), $this->mac_safe_path( realpath( $actual ) ?: $actual ) );
$this->assertTrue( false !== strpos( file_get_contents( $actual ), 'wp-cli/wp-cli' ) );
unlink( $actual );
rmdir( dirname( $actual ) );
Expand All @@ -105,7 +110,7 @@ public function test_get_composer_json_path() {
putenv( 'WP_CLI_PACKAGES_DIR' );
$expected = $this->temp_dir . '.wp-cli/packages/composer.json';
$actual = $get_composer_json_path->invoke( $package );
$this->assertSame( $expected, $this->mac_safe_path( $actual ) );
$this->assertSame( $this->mac_safe_path( realpath( $expected ) ?: $expected ), $this->mac_safe_path( realpath( $actual ) ?: $actual ) );
$this->assertTrue( false !== strpos( file_get_contents( $actual ), 'wp-cli/wp-cli' ) );
unlink( $actual );
rmdir( dirname( $actual ) );
Expand All @@ -115,7 +120,7 @@ public function test_get_composer_json_path() {
putenv( 'WP_CLI_PACKAGES_DIR=' . $this->temp_dir . 'packages' );
$expected = $this->temp_dir . 'packages/composer.json';
$actual = $get_composer_json_path->invoke( $package );
$this->assertSame( $expected, $this->mac_safe_path( $actual ) );
$this->assertSame( $this->mac_safe_path( realpath( $expected ) ?: $expected ), $this->mac_safe_path( realpath( $actual ) ?: $actual ) );
$this->assertTrue( false !== strpos( file_get_contents( $actual ), 'wp-cli/wp-cli' ) );
unlink( $actual );
rmdir( dirname( $actual ) );
Expand All @@ -126,7 +131,7 @@ public function test_get_composer_json_path() {
mkdir( $this->temp_dir . 'packages' );
touch( $expected );
$actual = $get_composer_json_path->invoke( $package );
$this->assertSame( $expected, $this->mac_safe_path( $actual ) );
$this->assertSame( $this->mac_safe_path( realpath( $expected ) ?: $expected ), $this->mac_safe_path( realpath( $actual ) ?: $actual ) );
$this->assertSame( 0, filesize( $actual ) );
unlink( $actual );
rmdir( dirname( $actual ) );
Expand Down Expand Up @@ -170,7 +175,7 @@ public function test_get_composer_json_path_backup_decoded() {
// Succeed with newly created.
$expected = $this->temp_dir . 'packages/composer.json';
list( $actual, $content, $decoded ) = $get_composer_json_path_backup_decoded->invoke( $package );
$this->assertSame( $expected, $this->mac_safe_path( $actual ) );
$this->assertSame( $this->mac_safe_path( realpath( $expected ) ?: $expected ), $this->mac_safe_path( realpath( $actual ) ?: $actual ) );
$this->assertTrue( false !== strpos( file_get_contents( $actual ), 'wp-cli/wp-cli' ) );
$this->assertSame( file_get_contents( $actual ), $content );
$this->assertFalse( empty( $decoded ) );
Expand All @@ -182,7 +187,7 @@ public function test_get_composer_json_path_backup_decoded() {
mkdir( $this->temp_dir . 'packages' );
file_put_contents( $expected, '{}' );
list( $actual, $content, $decoded ) = $get_composer_json_path_backup_decoded->invoke( $package );
$this->assertSame( $expected, $this->mac_safe_path( $actual ) );
$this->assertSame( $this->mac_safe_path( realpath( $expected ) ?: $expected ), $this->mac_safe_path( realpath( $actual ) ?: $actual ) );
$this->assertSame( '{}', $content );
$this->assertTrue( empty( $decoded ) );
unlink( $expected );
Expand All @@ -193,6 +198,8 @@ public function test_get_composer_json_path_backup_decoded() {
}

private function mac_safe_path( $path ) {
return preg_replace( '#^/private/(var|tmp)/#i', '/$1/', $path );
$path = \WP_CLI\Path::normalize( $path );
$path = preg_replace( '#^/private/(var|tmp)/#i', '/$1/', $path );
return $path;
}
}
19 changes: 12 additions & 7 deletions tests/phpunit/JsonManipulatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public function testAddLink($json, $type, $package, $constraint, $expected)
{
$manipulator = new JsonManipulator($json);
$this->assertTrue($manipulator->addLink($type, $package, $constraint));
$this->assertEquals($expected, $manipulator->getContents());
$this->assertJsonEquals($expected, $manipulator->getContents());
}

public static function linkProvider()
Expand Down Expand Up @@ -1297,7 +1297,7 @@ public function testAddLinkAndSortPackages($json, $type, $package, $constraint,
{
$manipulator = new JsonManipulator($json);
$this->assertTrue($manipulator->addLink($type, $package, $constraint, $sortPackages));
$this->assertEquals($expected, $manipulator->getContents());
$this->assertJsonEquals($expected, $manipulator->getContents());
}

public static function providerAddLinkAndSortPackages()
Expand Down Expand Up @@ -1380,7 +1380,7 @@ public function testRemoveSubNode($json, $name, $expected, $expectedContent = nu

$this->assertEquals($expected, $manipulator->removeSubNode('repositories', $name));
if (null !== $expectedContent) {
$this->assertEquals($expectedContent, $manipulator->getContents());
$this->assertJsonEquals($expectedContent, $manipulator->getContents());
}
}

Expand Down Expand Up @@ -2167,10 +2167,11 @@ public function testAddMainKeyWithContentHavingDollarSignFollowedByDigit2()
$manipulator = new JsonManipulator('{}');

$this->assertTrue($manipulator->addMainKey('foo', '$1bar'));
$this->assertEquals('{
$expected = '{
"foo": "$1bar"
}
', $manipulator->getContents());
';
$this->assertEquals( preg_replace( '/\R/', "\n", $expected ), preg_replace( '/\R/', "\n", $manipulator->getContents() ) );
}

public function testUpdateMainKey()
Expand Down Expand Up @@ -2301,9 +2302,10 @@ public function testRemoveMainKey()

$this->assertTrue($manipulator->removeMainKey('require'));
$this->assertTrue($manipulator->removeMainKey('require-dev'));
$this->assertEquals('{
$expected = '{
}
', $manipulator->getContents());
';
$this->assertEquals( preg_replace( '/\R/', "\n", $expected ), preg_replace( '/\R/', "\n", $manipulator->getContents() ) );
}

public function testIndentDetection()
Expand Down Expand Up @@ -2576,4 +2578,7 @@ public static function providerRemoveSubNodeCaseInsensitive()
}
// WP_CLI: end caseInsensitive.

private function assertJsonEquals( $expected, $actual ) {
$this->assertEquals( preg_replace( '/\R/', "\n", $expected ), preg_replace( '/\R/', "\n", $actual ) );
}
}