From 471b95ff0372b1091b1fb4f3d47b37441a8950b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 06:38:56 +0000 Subject: [PATCH] Detect already installed plugins without update information `install_plugin_install_status()` returns an `install` status for a plugin that is already installed whenever the WordPress.org API does not offer any update information for the installed version. The installation then fails with a "Destination folder already exists" error instead of the expected "Plugin already installed" warning. This is what currently happens for the Akismet plugin bundled with WordPress, which broke the `Install dependencies with activation` scenario. Check the file system in addition to the reported status, similar to what `Theme_Command` already does via `wp_get_theme()`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NeGydmXhxbx34V4UV6M3L6 --- features/plugin-install.feature | 47 +++++++++++++++++++++++++++++++++ src/Plugin_Command.php | 36 ++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/features/plugin-install.feature b/features/plugin-install.feature index 8943e738..b1851041 100644 --- a/features/plugin-install.feature +++ b/features/plugin-install.feature @@ -165,6 +165,53 @@ Feature: Install WordPress plugins """ And the return code should be 1 + Scenario: Installed plugin is detected even without available update information + Given a WP install + # `install_plugin_install_status()` falls back to an "install" status if the + # WordPress.org API does not offer an update for an outdated plugin. + And a wp-content/mu-plugins/no-plugin-updates.php file: + """ + response = array(); + } + return $updates; + }, + PHP_INT_MAX + ); + """ + And a wp-content/plugins/debug-bar/debug-bar.php file: + """ + is_plugin_installed( $slug ) + || $this->is_plugin_installed( $api->slug ); + + if ( $is_installed ) { + // We know this will fail, so avoid a needless download of the package. + return new WP_Error( 'already_installed', 'Plugin already installed.' ); + } } WP_CLI::log( sprintf( 'Installing %s (%s)', html_entity_decode( $api->name, ENT_QUOTES ), $api->version ) ); @@ -798,6 +804,30 @@ protected function install_from_repo( $slug, $assoc_args ) { return $result; } + /** + * Checks whether a plugin with the given slug is already installed. + * + * `install_plugin_install_status()` reports an 'install' status for a plugin + * that is already installed when the WordPress.org API does not provide any + * update information for the installed version. This happens for example for + * the Akismet plugin that is bundled with WordPress, whenever the bundled + * version is outdated but no update is being offered for it. + * + * Checking the file system as well makes sure such a plugin is reported as + * being already installed instead of the installation failing with a + * "Destination folder already exists" error. + * + * @param string $slug Plugin slug. + * @return bool Whether the plugin folder holds an installed plugin. + */ + private function is_plugin_installed( $slug ) { + if ( ! is_dir( WP_PLUGIN_DIR . '/' . $slug ) ) { + return false; + } + + return count( get_plugins( '/' . $slug ) ) > 0; + } + /** * Updates one or more plugins. *