Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3edda0e
Initial plan
Copilot Nov 2, 2025
415fb60
Add --no-interaction option to package commands
Copilot Nov 2, 2025
f8ca62d
Add documentation for update() parameters and putenv() usage
Copilot Nov 2, 2025
e68d8d6
Change flag to 'interaction' with default true, keep --no-interaction…
Copilot Nov 2, 2025
60df40b
Fix synopsis
swissspidy Nov 2, 2025
249e76e
Fix test to use real package name from package index
Copilot Nov 2, 2025
0a6dc7f
Merge branch 'main' into copilot/add-non-interactive-option
swissspidy Dec 19, 2025
a6df039
Update src/Package_Command.php
swissspidy Dec 19, 2025
27ab934
Update src/Package_Command.php
swissspidy Dec 19, 2025
1b84407
Update src/Package_Command.php
swissspidy Dec 19, 2025
3e26c9a
Update src/Package_Command.php
swissspidy Jan 20, 2026
10cb4e0
Merge branch 'main' into copilot/add-non-interactive-option
swissspidy Jan 20, 2026
891c476
Update src/Package_Command.php
swissspidy Jan 20, 2026
eab45f7
Merge branch 'main' into copilot/add-non-interactive-option
swissspidy Feb 4, 2026
68d4741
Update src/Package_Command.php
swissspidy Feb 19, 2026
559907d
Update src/Package_Command.php
swissspidy Feb 19, 2026
6776563
Merge branch 'main' into copilot/add-non-interactive-option
swissspidy Feb 19, 2026
2edb889
Fix phpdoc
swissspidy Feb 19, 2026
386d5e5
Add newline
swissspidy Feb 19, 2026
5ac3863
Improve tests to actually verify environment variables are set
Copilot Feb 19, 2026
0b70a31
Replace env var tests with actual Git/SSH interaction tests
Copilot Feb 19, 2026
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
17 changes: 17 additions & 0 deletions features/package-install.feature
Original file line number Diff line number Diff line change
Expand Up @@ -1262,3 +1262,20 @@ Feature: Install WP-CLI packages
Error: ZipArchive failed to unzip 'package-dir/zero.zip': Not a zip archive (19).
"""
And STDOUT should be empty

@github-api
Scenario: Install package with --no-interaction fails fast on Git authentication errors
Given an empty directory

# Try to install from a repository that requires authentication
# With --no-interaction and GIT_TERMINAL_PROMPT=0, Git will fail immediately
# instead of prompting for credentials
When I try `wp package install git@github.com:wp-cli-private-test/authentication-required.git --no-interaction`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: replace this with an actual private repo in the wp-cli org.

Suggested change
When I try `wp package install git@github.com:wp-cli-private-test/authentication-required.git --no-interaction`
When I try `wp package install git@github.com:wp-cli/wp-cli-private-test.git --no-interaction`

Then the return code should be 1
# The command should fail fast without hanging
And STDERR should contain:
"""
Package installation failed
"""
# Git should report it couldn't authenticate, not prompt
And STDERR should match /fatal:|Could not read from remote repository|Repository not found/
19 changes: 19 additions & 0 deletions features/package-update.feature
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,22 @@ Feature: Update WP-CLI packages
Error: Package 'non-existent/package' is not installed.
"""
And the return code should be 1

@github-api
Scenario: Update packages with --no-interaction completes without prompting
Given an empty directory

# Install a real package
When I run `wp package install danielbachhuber/wp-cli-reset-post-date-command`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: use a wp-cli owned package

Then STDOUT should contain:
"""
Success: Package installed.
"""

# Update with --no-interaction should complete without hanging
When I run `wp package update --no-interaction`
Then STDOUT should contain:
"""
Packages updated.
"""
And STDERR should be empty
19 changes: 19 additions & 0 deletions features/package.feature
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,25 @@ Feature: Manage WP-CLI packages
{NO_SUCH_PACKAGE_COMPOSER_JSON}
"""

@github-api
Scenario: Uninstall a package with --no-interaction prevents Git credential prompts
Given an empty directory

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

# Uninstall with --no-interaction should complete without hanging
When I run `wp package uninstall danielbachhuber/wp-cli-reset-post-date-command --no-interaction`
Then STDERR should be empty
And STDOUT should contain:
"""
Success: Uninstalled package.
"""

Scenario: List packages with --skip-update-check flag
Given an empty directory

Expand Down
51 changes: 48 additions & 3 deletions src/Package_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ public function browse( $_, $assoc_args ) {
* [--insecure]
* : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
*
* [--interaction]
* : Control interactive mode. Use `--no-interaction` to disable prompts (interactive by default). Useful for scripting.
*
* ## EXAMPLES
*
* # Install a package hosted at a git URL.
Expand All @@ -215,7 +218,12 @@ public function browse( $_, $assoc_args ) {
public function install( $args, $assoc_args ) {
list( $package_name ) = $args;

$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
$interaction = (bool) Utils\get_flag_value( $assoc_args, 'interaction', true );

if ( ! $interaction ) {
$this->set_non_interactive_mode();
}

$this->set_composer_auth_env_var();
$git_package = false;
Expand Down Expand Up @@ -624,6 +632,9 @@ public function get( $args, $assoc_args ) {
* [<package-name>...]
* : One or more package names to update. If not specified, all packages will be updated.
*
* [--interaction]
* : Control interactive mode. Use `--no-interaction` to disable prompts (interactive by default). Useful for scripting.
*
* ## EXAMPLES
*
* # Update all packages.
Expand Down Expand Up @@ -651,8 +662,17 @@ public function get( $args, $assoc_args ) {
* Generating autoload files
* ---
* Success: Package updated successfully.
*
* @param array<string> $args Positional arguments. One or more package names to update.
* @param array{interaction?: bool} $assoc_args Associative arguments.
*/
public function update( $args = [] ) {
public function update( $args, $assoc_args = [] ) {
$interaction = (bool) Utils\get_flag_value( $assoc_args, 'interaction', true );

if ( ! $interaction ) {
$this->set_non_interactive_mode();
}

$this->set_composer_auth_env_var();

// Validate package names if provided
Expand Down Expand Up @@ -749,6 +769,9 @@ function ( $event ) use ( &$updated_packages ) {
* [--insecure]
* : Retry downloads without certificate validation if TLS handshake fails. Note: This makes the request vulnerable to a MITM attack.
*
* [--interaction]
* : Control interactive prompts. Use `--no-interaction` to disable interactive questions (useful for scripting).
*
* ## EXAMPLES
*
* # Uninstall package.
Expand All @@ -761,7 +784,12 @@ function ( $event ) use ( &$updated_packages ) {
public function uninstall( $args, $assoc_args ) {
list( $package_name ) = $args;

$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
$insecure = (bool) Utils\get_flag_value( $assoc_args, 'insecure', false );
$interaction = (bool) Utils\get_flag_value( $assoc_args, 'interaction', true );

if ( ! $interaction ) {
$this->set_non_interactive_mode();
}

$this->set_composer_auth_env_var();
$package = $this->get_installed_package_by_name( $package_name );
Expand Down Expand Up @@ -1645,4 +1673,21 @@ private function get_github_default_branch( $package_name, $insecure = false ) {

return $default_branch;
}

/**
* Sets environment variables to enable non-interactive mode.
*
* This prevents Git from prompting for credentials (e.g., SSH passwords),
* which is useful for scripting and automation.
*
* Note: This uses putenv() which affects the entire PHP process, including
* any Git operations spawned by Composer. This is intentional to ensure
* non-interactive behavior propagates to all child processes.
*/
private function set_non_interactive_mode() {
// Prevent Git from prompting for credentials
putenv( 'GIT_TERMINAL_PROMPT=0' );
// Prevent SSH from prompting for passwords
putenv( 'GIT_SSH_COMMAND=ssh -o BatchMode=yes' );
}
}