From 5c3c0074a9e44070cac85e2d78e8f5c71513d324 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:06:45 +0000 Subject: [PATCH 1/2] Initial plan From 2c58fb54154d2e6bcf02f43ca89a29d9c4ccd0fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 10:18:24 +0000 Subject: [PATCH 2/2] Add --slug option to plugin/theme install for directory renaming Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> --- features/plugin-install.feature | 26 ++++++++++++++++++++++ features/theme-install.feature | 26 ++++++++++++++++++++++ src/Plugin_Command.php | 3 +++ src/Theme_Command.php | 3 +++ src/WP_CLI/CommandWithUpgrade.php | 36 +++++++++++++++++++++++++++---- 5 files changed, 90 insertions(+), 4 deletions(-) diff --git a/features/plugin-install.feature b/features/plugin-install.feature index 8f8283292..6275c93b5 100644 --- a/features/plugin-install.feature +++ b/features/plugin-install.feature @@ -472,3 +472,29 @@ Feature: Install WordPress plugins """ active-network """ + + Scenario: Install plugin from a zip file with a custom --slug + Given a WP install + + When I run `wp plugin install https://github.com/wp-cli-test/generic-example-plugin/archive/refs/heads/master.zip --slug=my-custom-plugin` + Then STDOUT should contain: + """ + Renamed 'generic-example-plugin-master' to 'my-custom-plugin'. + """ + And STDOUT should contain: + """ + Plugin installed successfully. + """ + And the wp-content/plugins/my-custom-plugin directory should exist + And the wp-content/plugins/generic-example-plugin-master directory should not exist + And the return code should be 0 + + Scenario: Error when --slug is used with multiple plugins + Given a WP install + + When I try `wp plugin install hello-dolly akismet --slug=my-plugin` + Then STDERR should contain: + """ + Error: The --slug option can only be used when installing a single item. + """ + And the return code should be 1 diff --git a/features/theme-install.feature b/features/theme-install.feature index 52373006a..a5406cc6b 100644 --- a/features/theme-install.feature +++ b/features/theme-install.feature @@ -186,3 +186,29 @@ Feature: Install WordPress themes """ active """ + + Scenario: Install theme from a zip file with a custom --slug + Given a WP install + + When I run `wp theme install https://github.com/wp-cli-test/generic-example-theme/archive/refs/heads/master.zip --slug=my-custom-theme` + Then STDOUT should contain: + """ + Renamed 'generic-example-theme-master' to 'my-custom-theme'. + """ + And STDOUT should contain: + """ + Theme installed successfully. + """ + And the wp-content/themes/my-custom-theme directory should exist + And the wp-content/themes/generic-example-theme-master directory should not exist + And the return code should be 0 + + Scenario: Error when --slug is used with multiple themes + Given a WP install + + When I try `wp theme install twentytwelve twentyeleven --slug=my-theme` + Then STDERR should contain: + """ + Error: The --slug option can only be used when installing a single item. + """ + And the return code should be 1 diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index 1c3ac3a2e..0fb12a8c3 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1165,6 +1165,9 @@ protected function filter_item_list( $items, $args ) { * [--with-dependencies] * : If set, the command will also install all required dependencies of the plugin as specified in the 'Requires Plugins' header. * + * [--slug=] + * : Use this as the target directory name when installing from a zip file. Cannot be used when installing multiple plugins. + * * ## EXAMPLES * * # Install the latest version from wordpress.org and activate diff --git a/src/Theme_Command.php b/src/Theme_Command.php index 00dfe3da2..7ea769c1c 100644 --- a/src/Theme_Command.php +++ b/src/Theme_Command.php @@ -612,6 +612,9 @@ protected function filter_item_list( $items, $args ) { * [--insecure] * : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack. * + * [--slug=] + * : Use this as the target directory name when installing from a zip file. Cannot be used when installing multiple themes. + * * ## EXAMPLES * * # Install the latest version from wordpress.org and activate diff --git a/src/WP_CLI/CommandWithUpgrade.php b/src/WP_CLI/CommandWithUpgrade.php index d1623c979..4ee663dbd 100755 --- a/src/WP_CLI/CommandWithUpgrade.php +++ b/src/WP_CLI/CommandWithUpgrade.php @@ -185,8 +185,14 @@ private function show_legend( $items ) { } public function install( $args, $assoc_args ) { - $successes = 0; - $errors = 0; + $successes = 0; + $errors = 0; + $custom_slug = Utils\get_flag_value( $assoc_args, 'slug', false ); + + if ( $custom_slug && count( $args ) > 1 ) { + WP_CLI::error( 'The --slug option can only be used when installing a single item.' ); + } + foreach ( $args as $slug ) { if ( empty( $slug ) ) { @@ -263,10 +269,32 @@ public function install( $args, $assoc_args ) { $file_upgrader = $this->get_upgrader( $assoc_args ); $filter = false; - // If a GitHub URL, do some guessing as to the correct plugin/theme directory. - if ( $is_remote && 'github.com' === Utils\parse_url( $slug, PHP_URL_HOST ) + if ( $custom_slug ) { + // If --slug is specified, rename the extracted directory to the provided slug. + $filter = function ( $source ) use ( $custom_slug ) { + /** + * @var \WP_Filesystem_Base $wp_filesystem + */ + global $wp_filesystem; + + $source_dir = Utils\basename( $source ); // `$source` is trailing-slashed path to the unzipped archive directory. + if ( $source_dir === $custom_slug ) { + return $source; + } + $new_path = substr_replace( $source, $custom_slug, (int) strrpos( $source, $source_dir ), strlen( $source_dir ) ); + + if ( $wp_filesystem->move( $source, $new_path ) ) { + WP_CLI::log( sprintf( "Renamed '%s' to '%s'.", $source_dir, $custom_slug ) ); + return $new_path; + } + + return new WP_Error( 'wpcli_install_slug', "Couldn't rename to '{$custom_slug}'." ); + }; + add_filter( 'upgrader_source_selection', $filter, 10 ); + } elseif ( $is_remote && 'github.com' === Utils\parse_url( $slug, PHP_URL_HOST ) // Don't attempt to rename ZIPs uploaded to the releases page or coming from a raw source. && ! preg_match( '#github\.com/[^/]+/[^/]+/(?:releases/download|raw)/#', $slug ) ) { + // If a GitHub URL, do some guessing as to the correct plugin/theme directory. $filter = function ( $source ) use ( $slug ) { /**