fix(xce-test-e2e-primitives): shell-style -e/+e errexit with mode-aware default — interactive REPL no longer exits on a bad command

This commit is contained in:
Rongsong Shen 2026-06-20 06:16:43 +00:00
parent cdc6e2377a
commit eda1f4c9d3
2 changed files with 33 additions and 11 deletions

View file

@ -137,6 +137,11 @@ static int worker_house_keeping(void*);
static int g_session_failed = 0;
static int g_continue_on_error = 0;
static int g_last_status = 0;
/* Interactive (TTY) sessions are a REPL: a failed command prints its error and
* the loop CONTINUES — never abort the read loop on a typo. The abort-on-first-
* failure + non-zero-exit verdict is a SCRIPTED/piped-mode concept only. Set
* from isatty(stdin) in main. */
static int g_interactive = 0;
/* Auto-variable state for the fixed `$jobid` / `$jobcount` substitution
* (xce-test-e2e-primitives task 3.1, Req 3.1/3.3/3.5/3.6).
@ -582,8 +587,8 @@ int preprocess_and_run(char* line)
if (status != 0) {
g_session_failed = 1;
if (!g_continue_on_error) {
return 1; /* abort: stop the read loop */
if (!g_continue_on_error && !g_interactive) {
return 1; /* abort: stop the read loop (scripted mode only) */
}
} else {
add_history(line);
@ -606,8 +611,8 @@ int preprocess_and_run(char* line)
if (subrc < 0) {
g_last_status = subrc;
g_session_failed = 1;
if (!g_continue_on_error) {
return 1; /* abort: stop the read loop */
if (!g_continue_on_error && !g_interactive) {
return 1; /* abort: stop the read loop (scripted mode only) */
}
return 0;
}
@ -621,7 +626,7 @@ int preprocess_and_run(char* line)
SET_ERROR(-rc);
g_session_failed = 1;
if (!g_continue_on_error) {
return 1; /* abort: stop the read loop */
return 1; /* errexit (-e): stop the read loop on first failure */
}
} else {
add_history(line);
@ -2534,12 +2539,19 @@ int cmd_set(int argc, char* argv[])
return 0;
}
/* Session verdict toggle (xce-test-e2e-primitives task 1.1):
* `set continue-on-error` keeps the read loop running after a failing
* command (the session still exits non-zero). It is not a per-context
* setting, so it is handled here ahead of the key/value path. */
if ((argc == 2) && (strcmp(argv[1], "continue-on-error") == 0)) {
g_continue_on_error = 1;
/* errexit toggle (xce-test-e2e-primitives task 1.1), shell `-e`/`+e` style.
* Default is mode-dependent (batch `-e` abort-on-failure, interactive `+e`
* continue); these override it for the rest of the session. The session
* still exits non-zero if any command failed. Handled here ahead of the
* per-context key/value path. `continue-on-error` == `+e` (back-compat). */
if ((argc == 2) &&
((strcmp(argv[1], "+e") == 0) ||
(strcmp(argv[1], "continue-on-error") == 0))) {
g_continue_on_error = 1; /* +e: keep the read loop running on failure */
return 0;
}
if ((argc == 2) && (strcmp(argv[1], "-e") == 0)) {
g_continue_on_error = 0; /* -e: abort the read loop on first failure */
return 0;
}
@ -3255,6 +3267,15 @@ int main(int argc, char* argv[])
idgen_ctx_init(&idctx);
/* errexit default, modelled on shell `-e`/`+e`, keyed on the stdin mode:
* batch (piped/redirected stdin) → `-e`: abort the read loop on the first
* failing command and exit non-zero (a script wants fail-fast + verdict).
* interactive (TTY) REPL → `+e`: a failed command prints its error and the
* loop CONTINUES (never exit on a typo).
* Override at any time with `set -e` / `set +e` (or `set continue-on-error`). */
g_interactive = isatty(STDIN_FILENO);
g_continue_on_error = g_interactive ? 1 : 0;
while (!done) {
char* s;
int rc;