mirror of
https://github.com/github/spec-kit.git
synced 2026-07-05 21:49:47 +08:00
feat(workflows): opt-in output_format: json exposes parsed shell stdout as output.data (#2963)
* feat(workflows): opt-in output_format: json exposes parsed shell stdout as output.data No step that runs external code could hand a typed value to a later step, so e.g. a fan-out could never consume a runtime-computed collection. With output_format: json declared, stdout is parsed and exposed under output.data (raw keys unchanged); a parse failure fails the step with a clear error. Without the key, behavior is unchanged. Reference implementation for the proposal in #2962. Addresses #2962 * test(shell): emit JSON via sys.executable for cross-platform output_format tests Address review (#2963): replace non-portable echo '{...}' (Windows cmd.exe keeps the single quotes, breaking JSON) with the established '"{py}" "{script}"' pattern using sys.executable + a temp script, so the output_format tests pass on the Windows CI matrix. Also make the validate test's run inert (exit 0). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
@@ -49,6 +50,23 @@ class ShellStep(StepBase):
|
||||
error=f"Shell command exited with code {proc.returncode}.",
|
||||
output=output,
|
||||
)
|
||||
if config.get("output_format") == "json":
|
||||
# Opt-in structured output: expose the parsed stdout under
|
||||
# ``output.data`` so later steps can consume typed values
|
||||
# (e.g. a fan-out's ``items:``). A parse failure fails the
|
||||
# step — declaring ``output_format: json`` is a contract.
|
||||
try:
|
||||
output["data"] = json.loads(proc.stdout)
|
||||
except json.JSONDecodeError as exc:
|
||||
return StepResult(
|
||||
status=StepStatus.FAILED,
|
||||
error=(
|
||||
f"Shell step {config.get('id', '?')!r} declared "
|
||||
f"output_format: json but stdout is not valid "
|
||||
f"JSON: {exc}"
|
||||
),
|
||||
output=output,
|
||||
)
|
||||
return StepResult(
|
||||
status=StepStatus.COMPLETED,
|
||||
output=output,
|
||||
@@ -72,4 +90,10 @@ class ShellStep(StepBase):
|
||||
errors.append(
|
||||
f"Shell step {config.get('id', '?')!r} is missing 'run' field."
|
||||
)
|
||||
output_format = config.get("output_format")
|
||||
if output_format is not None and output_format != "json":
|
||||
errors.append(
|
||||
f"Shell step {config.get('id', '?')!r}: 'output_format' must "
|
||||
f"be 'json' when present, got {output_format!r}."
|
||||
)
|
||||
return errors
|
||||
|
||||
Reference in New Issue
Block a user