Skip to content

Commit bc9a48f

Browse files
authored
Merge branch 'main' into copilot/support-restarting-shell
2 parents ae700bf + cce3e64 commit bc9a48f

3 files changed

Lines changed: 64 additions & 12 deletions

File tree

.gitattributes

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/.actrc export-ignore
2+
/.distignore export-ignore
3+
/.editorconfig export-ignore
4+
/.github export-ignore
5+
/.gitignore export-ignore
6+
/.typos.toml export-ignore
7+
/AGENTS.md export-ignore
8+
/behat.yml export-ignore
9+
/features export-ignore
10+
/phpcs.xml.dist export-ignore
11+
/phpstan.neon.dist export-ignore
12+
/phpunit.xml.dist export-ignore
13+
/tests export-ignore
14+
/wp-cli.yml export-ignore

.github/workflows/copilot-setup-steps.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525

2626
- name: Set up PHP environment
2727
if: steps.check_composer_file.outputs.files_exists == 'true'
28-
uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2
28+
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2
2929
with:
3030
php-version: 'latest'
3131
ini-values: zend.assertions=1, error_reporting=-1, display_errors=On
@@ -36,7 +36,7 @@ jobs:
3636

3737
- name: Install Composer dependencies & cache dependencies
3838
if: steps.check_composer_file.outputs.files_exists == 'true'
39-
uses: ramsey/composer-install@a35c6ebd3d08125aaf8852dff361e686a1a67947 # v3
39+
uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # v3
4040
env:
4141
COMPOSER_ROOT_VERSION: dev-${{ github.event.repository.default_branch }}
4242
with:

src/WP_CLI/Shell/REPL.php

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ private static function create_prompt_cmd( $prompt, $history_path ) {
156156
} elseif ( is_file( '/bin/bash' ) && is_readable( '/bin/bash' ) ) {
157157
// Prefer /bin/bash when available since we use bash-specific commands.
158158
$shell_binary = '/bin/bash';
159-
} elseif ( getenv( 'SHELL' ) && self::is_bash_shell( (string) getenv( 'SHELL' ) ) ) {
160-
// Only use SHELL as fallback if it's bash (we use bash-specific commands).
159+
} elseif ( getenv( 'SHELL' ) && self::is_supported_shell( (string) getenv( 'SHELL' ) ) ) {
160+
// Use SHELL as fallback if it's a supported shell (bash or ksh).
161161
$shell_binary = (string) getenv( 'SHELL' );
162162
} else {
163163
// Final fallback for systems without /bin/bash.
@@ -168,16 +168,27 @@ private static function create_prompt_cmd( $prompt, $history_path ) {
168168
WP_CLI::error( "The shell binary '{$shell_binary}' is not valid. You can override the shell to be used through the WP_CLI_CUSTOM_SHELL environment variable." );
169169
}
170170

171+
$is_ksh = self::is_ksh_shell( $shell_binary );
171172
$shell_binary = escapeshellarg( $shell_binary );
172173

173-
$cmd = 'set -f; '
174-
. "history -r {$history_path}; "
175-
. 'LINE=""; '
176-
. "read -re -p {$prompt} LINE; "
177-
. '[ $? -eq 0 ] || exit; '
178-
. 'history -s -- "$LINE"; '
179-
. "history -w {$history_path}; "
180-
. 'echo $LINE; ';
174+
if ( $is_ksh ) {
175+
// ksh does not support bash-specific history commands or `read -e`/`read -p`.
176+
// Use POSIX-compatible read and print the prompt via printf to stderr.
177+
$cmd = 'set -f; '
178+
. 'LINE=""; '
179+
. "printf %s {$prompt} >&2; "
180+
. 'IFS= read -r LINE || exit; '
181+
. 'printf \'%s\n\' "$LINE"; ';
182+
} else {
183+
$cmd = 'set -f; '
184+
. "history -r {$history_path}; "
185+
. 'LINE=""; '
186+
. "read -re -p {$prompt} LINE; "
187+
. '[ $? -eq 0 ] || exit; '
188+
. 'history -s -- "$LINE"; '
189+
. "history -w {$history_path}; "
190+
. 'printf \'%s\n\' "$LINE"; ';
191+
}
181192

182193
return "{$shell_binary} -c " . escapeshellarg( $cmd );
183194
}
@@ -197,6 +208,33 @@ private static function is_bash_shell( $shell_path ) {
197208
return 'bash' === $basename || 0 === strpos( $basename, 'bash-' );
198209
}
199210

211+
/**
212+
* Check if a shell binary is ksh or a ksh-compatible shell (mksh, pdksh, ksh93, etc.).
213+
*
214+
* @param string $shell_path Path to the shell binary.
215+
* @return bool True if the shell is ksh-compatible, false otherwise.
216+
*/
217+
private static function is_ksh_shell( $shell_path ) {
218+
if ( ! is_file( $shell_path ) || ! is_readable( $shell_path ) ) {
219+
return false;
220+
}
221+
$basename = basename( $shell_path );
222+
// Matches ksh, ksh93, ksh88, mksh, pdksh, etc.
223+
return 0 === strpos( $basename, 'ksh' )
224+
|| 'mksh' === $basename
225+
|| 'pdksh' === $basename;
226+
}
227+
228+
/**
229+
* Check if a shell binary is supported (bash or ksh-compatible).
230+
*
231+
* @param string $shell_path Path to the shell binary.
232+
* @return bool True if the shell is supported, false otherwise.
233+
*/
234+
private static function is_supported_shell( $shell_path ) {
235+
return self::is_bash_shell( $shell_path ) || self::is_ksh_shell( $shell_path );
236+
}
237+
200238
private function set_history_file() {
201239
$data = getcwd() . get_current_user();
202240

0 commit comments

Comments
 (0)