mirror of
https://github.com/github/spec-kit.git
synced 2026-07-06 14:01:01 +08:00
fix: non-zero exit code when a workflow run ends failed or aborted (#2959)
* fix: non-zero exit code when a workflow run ends failed or aborted workflow run and workflow resume printed Status: failed (or emitted the --json payload) and exited 0, so scripts and CI could not rely on the process exit code. Map terminal outcomes: failed|aborted -> 1, completed|paused -> 0, on both the text and --json paths. The previous exit-0-on-failed behavior was pinned by test_workflow_run_failing_yaml_without_project; the pin is updated to the new contract. Fixes #2958 * test: portable exit-code step commands + cover resume failed->exit-1 Address review (#2959): replace non-portable run: 'true'/'false' with 'exit 0'/'exit 1' (Windows cmd.exe has no true/false builtins under shell=True), and add an end-to-end 'workflow resume' test asserting a resumed failed run exits non-zero. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2100,6 +2100,16 @@ def _workflow_run_payload(state: Any) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
def _run_outcome_exit_code(status_value: str) -> int:
|
||||
"""Exit code for a finished run/resume: non-zero on terminal failure.
|
||||
|
||||
``failed`` and ``aborted`` map to 1 so scripts and orchestrators can
|
||||
rely on the process exit code; ``completed`` and ``paused`` map to 0
|
||||
(paused is a legitimate waiting state, not a failure).
|
||||
"""
|
||||
return 1 if status_value in ("failed", "aborted") else 0
|
||||
|
||||
|
||||
def _emit_workflow_json(payload: dict[str, Any]) -> None:
|
||||
"""Write a workflow payload as machine-readable JSON to stdout.
|
||||
|
||||
@@ -2214,7 +2224,7 @@ def workflow_run(
|
||||
|
||||
if json_output:
|
||||
_emit_workflow_json(_workflow_run_payload(state))
|
||||
return
|
||||
raise typer.Exit(_run_outcome_exit_code(state.status.value))
|
||||
|
||||
status_colors = {
|
||||
"completed": "green",
|
||||
@@ -2229,6 +2239,8 @@ def workflow_run(
|
||||
if state.status.value == "paused":
|
||||
console.print(f"\nResume with: [cyan]specify workflow resume {state.run_id}[/cyan]")
|
||||
|
||||
raise typer.Exit(_run_outcome_exit_code(state.status.value))
|
||||
|
||||
|
||||
@workflow_app.command("resume")
|
||||
def workflow_resume(
|
||||
@@ -2269,7 +2281,7 @@ def workflow_resume(
|
||||
|
||||
if json_output:
|
||||
_emit_workflow_json(_workflow_run_payload(state))
|
||||
return
|
||||
raise typer.Exit(_run_outcome_exit_code(state.status.value))
|
||||
|
||||
status_colors = {
|
||||
"completed": "green",
|
||||
@@ -2280,6 +2292,8 @@ def workflow_resume(
|
||||
color = status_colors.get(state.status.value, "white")
|
||||
console.print(f"\n[{color}]Status: {state.status.value}[/{color}]")
|
||||
|
||||
raise typer.Exit(_run_outcome_exit_code(state.status.value))
|
||||
|
||||
|
||||
@workflow_app.command("status")
|
||||
def workflow_status(
|
||||
|
||||
Reference in New Issue
Block a user