Skip to content

Commit bb22355

Browse files
CopilotswissspidyCopilot
authored
Add --no-interaction flag to prevent Git/SSH prompts in package commands (#206)
Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Pascal Birchler <pascalb@google.com>
1 parent f5dbb0e commit bb22355

4 files changed

Lines changed: 103 additions & 3 deletions

File tree

features/package-install.feature

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,3 +1420,20 @@ Feature: Install WP-CLI packages
14201420
Error: ZipArchive failed to unzip 'package-dir/zero.zip': Not a zip archive (19).
14211421
"""
14221422
And STDOUT should be empty
1423+
1424+
@github-api
1425+
Scenario: Install package with --no-interaction fails fast on Git authentication errors
1426+
Given an empty directory
1427+
1428+
# Try to install from a repository that requires authentication
1429+
# With --no-interaction and GIT_TERMINAL_PROMPT=0, Git will fail immediately
1430+
# instead of prompting for credentials
1431+
When I try `wp package install git@github.com:wp-cli/wp-cli-private-test.git --no-interaction`
1432+
Then the return code should be 1
1433+
# The command should fail fast without hanging
1434+
And STDERR should contain:
1435+
"""
1436+
Package installation failed
1437+
"""
1438+
# Git should report it couldn't authenticate, not prompt
1439+
And STDERR should match /fatal:|Could not read from remote repository|Repository not found/

features/package-update.feature

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,22 @@ Feature: Update WP-CLI packages
194194
Error: Package 'non-existent/package' is not installed.
195195
"""
196196
And the return code should be 1
197+
198+
@github-api
199+
Scenario: Update packages with --no-interaction completes without prompting
200+
Given an empty directory
201+
202+
# Install a real package
203+
When I run `wp package install danielbachhuber/wp-cli-reset-post-date-command`
204+
Then STDOUT should contain:
205+
"""
206+
Success: Package installed.
207+
"""
208+
209+
# Update with --no-interaction should complete without hanging
210+
When I run `wp package update --no-interaction`
211+
Then STDOUT should contain:
212+
"""
213+
Packages updated.
214+
"""
215+
And STDERR should be empty

features/package.feature

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,25 @@ Feature: Manage WP-CLI packages
209209
{NO_SUCH_PACKAGE_COMPOSER_JSON}
210210
"""
211211

212+
@github-api
213+
Scenario: Uninstall a package with --no-interaction prevents Git credential prompts
214+
Given an empty directory
215+
216+
# Install a real package first
217+
When I run `wp package install danielbachhuber/wp-cli-reset-post-date-command`
218+
Then STDOUT should contain:
219+
"""
220+
Success: Package installed.
221+
"""
222+
223+
# Uninstall with --no-interaction should complete without hanging
224+
When I run `wp package uninstall danielbachhuber/wp-cli-reset-post-date-command --no-interaction`
225+
Then STDERR should be empty
226+
And STDOUT should contain:
227+
"""
228+
Success: Uninstalled package.
229+
"""
230+
212231
Scenario: List packages with --skip-update-check flag
213232
Given an empty directory
214233

src/Package_Command.php

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ public function browse( $_, $assoc_args ) {
199199
* [--insecure]
200200
* : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
201201
*
202+
* [--interaction]
203+
* : Control interactive mode. Use `--no-interaction` to disable prompts (interactive by default). Useful for scripting.
204+
*
202205
* ## EXAMPLES
203206
*
204207
* # Install a package hosted at a git URL.
@@ -216,7 +219,12 @@ public function browse( $_, $assoc_args ) {
216219
public function install( $args, $assoc_args ) {
217220
list( $package_name ) = $args;
218221

219-
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
222+
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
223+
$interaction = (bool) Utils\get_flag_value( $assoc_args, 'interaction', true );
224+
225+
if ( ! $interaction ) {
226+
$this->set_non_interactive_mode();
227+
}
220228

221229
$this->set_composer_auth_env_var();
222230
$git_package = false;
@@ -639,6 +647,9 @@ public function get( $args, $assoc_args ) {
639647
* [<package-name>...]
640648
* : One or more package names to update. If not specified, all packages will be updated.
641649
*
650+
* [--interaction]
651+
* : Control interactive mode. Use `--no-interaction` to disable prompts (interactive by default). Useful for scripting.
652+
*
642653
* ## EXAMPLES
643654
*
644655
* # Update all packages.
@@ -666,8 +677,17 @@ public function get( $args, $assoc_args ) {
666677
* Generating autoload files
667678
* ---
668679
* Success: Package updated successfully.
680+
*
681+
* @param array<string> $args Positional arguments. One or more package names to update.
682+
* @param array{interaction?: bool} $assoc_args Associative arguments.
669683
*/
670-
public function update( $args = [] ) {
684+
public function update( $args, $assoc_args = [] ) {
685+
$interaction = (bool) Utils\get_flag_value( $assoc_args, 'interaction', true );
686+
687+
if ( ! $interaction ) {
688+
$this->set_non_interactive_mode();
689+
}
690+
671691
$this->set_composer_auth_env_var();
672692

673693
// Validate package names if provided
@@ -764,6 +784,9 @@ function ( $event ) use ( &$updated_packages ) {
764784
* [--insecure]
765785
* : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
766786
*
787+
* [--interaction]
788+
* : Control interactive prompts. Use `--no-interaction` to disable interactive questions (useful for scripting).
789+
*
767790
* ## EXAMPLES
768791
*
769792
* # Uninstall package.
@@ -776,7 +799,12 @@ function ( $event ) use ( &$updated_packages ) {
776799
public function uninstall( $args, $assoc_args ) {
777800
list( $package_name ) = $args;
778801

779-
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
802+
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
803+
$interaction = (bool) Utils\get_flag_value( $assoc_args, 'interaction', true );
804+
805+
if ( ! $interaction ) {
806+
$this->set_non_interactive_mode();
807+
}
780808

781809
$this->set_composer_auth_env_var();
782810
$package = $this->get_installed_package_by_name( $package_name );
@@ -1722,4 +1750,21 @@ private function get_github_default_branch( $package_name, $insecure = false ) {
17221750

17231751
return $default_branch;
17241752
}
1753+
1754+
/**
1755+
* Sets environment variables to enable non-interactive mode.
1756+
*
1757+
* This prevents Git from prompting for credentials (e.g., SSH passwords),
1758+
* which is useful for scripting and automation.
1759+
*
1760+
* Note: This uses putenv() which affects the entire PHP process, including
1761+
* any Git operations spawned by Composer. This is intentional to ensure
1762+
* non-interactive behavior propagates to all child processes.
1763+
*/
1764+
private function set_non_interactive_mode() {
1765+
// Prevent Git from prompting for credentials
1766+
putenv( 'GIT_TERMINAL_PROMPT=0' );
1767+
// Prevent SSH from prompting for passwords
1768+
putenv( 'GIT_SSH_COMMAND=ssh -o BatchMode=yes' );
1769+
}
17251770
}

0 commit comments

Comments
 (0)