Skip to content

feat(test): GitHub-native CI output for "test run --all" (--gh-output, --summary-file)#264

Merged
zeshi-du merged 3 commits into
TestSprite:mainfrom
Andy00L:feat/ci
Jul 23, 2026
Merged

feat(test): GitHub-native CI output for "test run --all" (--gh-output, --summary-file)#264
zeshi-du merged 3 commits into
TestSprite:mainfrom
Andy00L:feat/ci

Conversation

@Andy00L

@Andy00L Andy00L commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Reshaped per the maintainer review: the top-level ci command is gone; the CI-native output layer now attaches to the existing test run --all path.

  • New src/lib/gh-output.ts keeps the reducer (summarizeAcceptedPayload), the $GITHUB_STEP_SUMMARY Markdown renderer, and the ::error:: annotations emitter, with their unit tests moved to src/lib/gh-output.test.ts.
  • test run --all gains --gh-output (force the GitHub-native surfaces even off-Actions) and --summary-file <path> (write the reduced {total, passed, failed, timedOut, runs[]} JSON artifact). Auto-enabled when GITHUB_ACTIONS=true, so the official Action gets all of it for free by invoking test run --all --wait.
  • Emission happens after the batch envelope prints and before the gate throws, so artifacts land even when the batch exits non-zero; a broken summary/step file downgrades to a stderr warning and never masks the gate's exit code.
  • --output contract defect fixed by construction: nothing extra is written to stdout (the envelope owns it; workflow commands are only emitted under Actions or explicit --gh-output); the summary file is a machine artifact written in both output modes.
  • Both flags are --all-only and rejected loudly otherwise (same rule as --filter).
  • Verified against current main: typecheck, build, full suite (2023 tests), lint and prettier clean.

Closes #99 CLI-side per the direction note (the one-shot entrypoint ships as the official GitHub Action).

Discord: interferon0

Summary by CodeRabbit

  • New Features
    • Added GitHub Actions output support for testsprite test run --all --wait, including workflow error annotations and a $GITHUB_STEP_SUMMARY job summary.
    • Introduced --gh-output and --summary-file (with --summary-file writing a reduced JSON summary).
  • Bug Fixes
    • Improved robustness: malformed/unexpected payloads produce an empty summary without failing, and step-summary writing issues degrade to warnings.
  • Tests
    • Added/extended coverage for summary reduction, step-summary rendering, and correct routing of annotations/output across modes.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 49cd00a9-54fd-42dc-a00b-3b09de88786f

📥 Commits

Reviewing files that changed from the base of the PR and between 887bb4a and 28030c8.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (4)
  • src/commands/test.run.spec.ts
  • src/commands/test.ts
  • src/lib/gh-output.test.ts
  • src/lib/gh-output.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/lib/gh-output.ts
  • src/commands/test.ts
  • src/lib/gh-output.test.ts

Walkthrough

The batch test command now supports GitHub Actions summaries and annotations plus reduced machine-readable summary files. New helpers normalize accepted test results, render Markdown, emit annotations, and validate CI and off-CI behavior.

Changes

Batch CI outputs

Layer / File(s) Summary
GitHub output helpers and validation
src/lib/gh-output.ts, src/lib/gh-output.test.ts
Defines CI result types, summarizes accepted payloads, renders job-summary Markdown, emits error annotations, and tests CI, off-CI, forced, and append-failure behavior.
Batch CLI options and validation
src/commands/test.ts
Adds --gh-output and --summary-file to test run --all --wait, validates their usage, extends option types, and passes values to the batch runner.
Batch summary output execution
src/commands/test.ts, src/commands/test.run.spec.ts
Generates reduced summaries after batch execution, writes optional JSON files, emits GitHub outputs, preserves JSON stdout routing, and updates runner fixtures with explicit environments.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant runTestRunAll
  participant summarizeAcceptedPayload
  participant emitGithubOutputs
  participant GitHubActions
  CLI->>runTestRunAll: batch options
  runTestRunAll->>summarizeAcceptedPayload: captured JSON payload
  summarizeAcceptedPayload-->>runTestRunAll: reduced CiSummary
  runTestRunAll->>emitGithubOutputs: summary and environment
  emitGithubOutputs->>GitHubActions: step summary and error annotations
Loading

Suggested reviewers: ruili-testsprite, zeshi-du

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: GitHub-native CI output for test run --all with the new flags.
Linked Issues check ✅ Passed The PR matches the core goals: it adds GitHub Actions output, step summary markdown, per-failure annotations, and machine-summary output on the batch path.
Out of Scope Changes check ✅ Passed The changes stay focused on the batch test CI-output path and related tests, with no clear unrelated additions.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/ci.ts`:
- Around line 93-143: Update renderJobSummaryMarkdown to escape Markdown
table-cell values for testId, runId, and dashboardUrl, including pipe characters
and newlines. Update emitGithubOutputs to percent-encode workflow-command
parameters and data for testId, error, and dashboardUrl before writing ::error
messages. Add regression coverage exercising pipe, percent, and newline inputs
across both output formats.
- Around line 196-266: Update createCiCommand so --project either uses the
existing project-id resolution flow for TESTSPRITE_PROJECT_ID and config
project_id, or remove those fallback claims from its help text. Replace the
hardcoded max-concurrency upper bound and corresponding help text value with the
shared MAX_BATCH_CONCURRENCY constant from test.ts, keeping validation and
documentation aligned.
- Around line 268-273: Remove the local parseRequestTimeoutFlag helper and
update the ci command’s --request-timeout handling to call the shared
request-timeout parser used by other commands. Preserve the central parser’s
validation-error behavior for invalid or non-positive values instead of silently
falling back to the default timeout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b1463b67-81e6-4117-b323-bda179b6b972

📥 Commits

Reviewing files that changed from the base of the PR and between 2708a40 and 995555a.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (3)
  • src/commands/ci.test.ts
  • src/commands/ci.ts
  • src/index.ts

Comment thread src/commands/ci.ts Outdated
Comment on lines +93 to +143
export function renderJobSummaryMarkdown(summary: CiSummary): string {
return [
'## TestSprite results',
'',
`**${summary.passed}/${summary.total} passed** (${summary.failed} failed, ${summary.timedOut} timed out)`,
'',
'| Test | Status | Run |',
'| --- | --- | --- |',
...summary.runs.map(
row =>
`| ${row.testId} | ${row.status} | ${
row.dashboardUrl ? `[dashboard](${row.dashboardUrl})` : (row.runId ?? '')
} |`,
),
'',
].join('\n');
}

/**
* Emit the GitHub-native surfaces. Self-gating on the standard env vars:
* `$GITHUB_STEP_SUMMARY` (a file path Actions provides) receives the Markdown
* table; `GITHUB_ACTIONS=true` enables one `::error::` workflow command per
* non-passed run on stdout (Actions parses workflow commands from stdout).
* Both writes are best-effort: a broken summary file must not mask the gate.
*/
export function emitGithubOutputs(
summary: CiSummary,
env: NodeJS.ProcessEnv,
sinks: {
stdout: (line: string) => void;
stderr: (line: string) => void;
appendFile: (path: string, content: string) => void;
},
): void {
const summaryPath = env.GITHUB_STEP_SUMMARY;
if (typeof summaryPath === 'string' && summaryPath.length > 0) {
try {
sinks.appendFile(summaryPath, renderJobSummaryMarkdown(summary));
} catch {
sinks.stderr('[ci] could not append to GITHUB_STEP_SUMMARY; continuing');
}
}
if (env.GITHUB_ACTIONS === 'true') {
for (const row of summary.runs) {
if (row.status === 'passed') continue;
const detail = row.error !== undefined ? ` ${row.error}` : '';
const link = row.dashboardUrl !== undefined ? ` ${row.dashboardUrl}` : '';
sinks.stdout(`::error title=TestSprite ${row.testId}::status=${row.status}${detail}${link}`);
}
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'File list:\n'
git ls-files | rg '^src/commands/ci\.ts$|^src/commands/ci\.test\.ts$|^DOCUMENTATION\.md$'

printf '\nOutline for src/commands/ci.ts:\n'
ast-grep outline src/commands/ci.ts --view expanded || true

printf '\nRelevant excerpts from src/commands/ci.ts:\n'
sed -n '1,240p' src/commands/ci.ts

printf '\nRelevant excerpts from src/commands/ci.test.ts:\n'
if [ -f src/commands/ci.test.ts ]; then
  sed -n '1,260p' src/commands/ci.test.ts
else
  echo 'No src/commands/ci.test.ts found'
fi

printf '\nSearch for escaping helpers / workflow command handling:\n'
rg -n "escapeWorkflow|GITHUB_STEP_SUMMARY|::error|markdown|dashboardUrl|testId|runId" src/commands src/lib || true

printf '\nRelevant docs snippets:\n'
rg -n "Signals & pipes|Output & scripting|workflow commands|step summary|GITHUB_STEP_SUMMARY|GITHUB_ACTIONS" DOCUMENTATION.md || true

Repository: TestSprite/testsprite-cli

Length of output: 50382


Escape CI summary and workflow-command values

testId, runId, and dashboardUrl go straight into the Markdown table, and testId, error, and dashboardUrl go straight into ::error. Escape table cells and encode workflow-command data/parameters before emitting them; add a regression test for |, %, and newline inputs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/ci.ts` around lines 93 - 143, Update renderJobSummaryMarkdown to
escape Markdown table-cell values for testId, runId, and dashboardUrl, including
pipe characters and newlines. Update emitGithubOutputs to percent-encode
workflow-command parameters and data for testId, error, and dashboardUrl before
writing ::error messages. Add regression coverage exercising pipe, percent, and
newline inputs across both output formats.

Comment thread src/commands/ci.ts Outdated
Comment on lines +196 to +266
/** Default overall deadline for the CI gate, in seconds (mirrors run --all). */
const CI_DEFAULT_TIMEOUT_SECONDS = 600;
/** Default poll fan-out bound (mirrors run --all's --max-concurrency). */
const CI_DEFAULT_MAX_CONCURRENCY = 50;

export function createCiCommand(deps: CiDeps = {}): Command {
return new Command('ci')
.description(
'One-shot CI gate: run the project suite, gate on the exit code, and emit CI-native output (machine summary; GitHub job summary + ::error:: PR annotations when on Actions)',
)
.option('--project <id>', 'project id (or TESTSPRITE_PROJECT_ID / config project_id)')
.option(
'--timeout <s>',
`overall deadline in seconds (1-3600, default ${CI_DEFAULT_TIMEOUT_SECONDS})`,
)
.option(
'--max-concurrency <n>',
`max concurrent run polls (1-100, default ${CI_DEFAULT_MAX_CONCURRENCY})`,
)
.option('--summary-file <path>', 'also write the machine summary JSON to this file')
.addHelpText(
'after',
'\nExamples:\n' +
' testsprite ci --project proj_123 # gate a pipeline step\n' +
' testsprite ci --summary-file testsprite.json # keep the summary as a build artifact',
)
.addHelpText('after', GLOBAL_OPTS_HINT)
.action(
async (
cmdOpts: {
project?: string;
timeout?: string;
maxConcurrency?: string;
summaryFile?: string;
},
command: Command,
) => {
const globals = command.optsWithGlobals() as Partial<FactoryCommonOptions> & {
requestTimeout?: string;
};
const timeoutSeconds =
cmdOpts.timeout === undefined ? CI_DEFAULT_TIMEOUT_SECONDS : Number(cmdOpts.timeout);
if (!Number.isInteger(timeoutSeconds) || timeoutSeconds < 1 || timeoutSeconds > 3600) {
throw localValidationError('timeout', 'must be an integer between 1 and 3600');
}
const maxConcurrency =
cmdOpts.maxConcurrency === undefined
? CI_DEFAULT_MAX_CONCURRENCY
: Number(cmdOpts.maxConcurrency);
if (!Number.isInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 100) {
throw localValidationError('max-concurrency', 'must be an integer between 1 and 100');
}
await runCi(
{
profile: globals.profile ?? 'default',
output: globals.output ?? 'text',
endpointUrl: globals.endpointUrl,
debug: globals.debug ?? false,
verbose: globals.verbose ?? false,
dryRun: globals.dryRun ?? false,
requestTimeoutMs: parseRequestTimeoutFlag(globals.requestTimeout),
projectId: cmdOpts.project,
timeoutSeconds,
maxConcurrency,
summaryFile: cmdOpts.summaryFile,
},
deps,
);
},
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate how --project resolves TESTSPRITE_PROJECT_ID / config elsewhere.
rg -n "TESTSPRITE_PROJECT_ID" -C3 src
rg -nP "\-\-project\s*<" -C5 src/commands/test.ts

Repository: TestSprite/testsprite-cli

Length of output: 14872


🏁 Script executed:

#!/bin/bash
rg -n "MAX_BATCH_CONCURRENCY" src

Repository: TestSprite/testsprite-cli

Length of output: 482


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the CI command and the batch runner it calls.
sed -n '1,260p' src/commands/ci.ts
printf '\n---\n'
sed -n '6300,7090p' src/commands/test.ts

# Find any shared project-id resolution helpers used by ci.
printf '\n=== project-id resolution search ===\n'
rg -n "requireProjectId|projectId.*env|TESTSPRITE_PROJECT_ID|config project_id|default.*project" src/commands src/lib src -g '!**/*.map'

Repository: TestSprite/testsprite-cli

Length of output: 47303


--project fallback and concurrency bound drift

  • --project here doesn’t resolve TESTSPRITE_PROJECT_ID or config; either wire in the same project-id resolution used elsewhere or drop the fallback from the help text.
  • Reuse MAX_BATCH_CONCURRENCY from src/commands/test.ts instead of hardcoding 100 so the --max-concurrency help text and validation stay aligned.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/ci.ts` around lines 196 - 266, Update createCiCommand so
--project either uses the existing project-id resolution flow for
TESTSPRITE_PROJECT_ID and config project_id, or remove those fallback claims
from its help text. Replace the hardcoded max-concurrency upper bound and
corresponding help text value with the shared MAX_BATCH_CONCURRENCY constant
from test.ts, keeping validation and documentation aligned.

Comment thread src/commands/ci.ts Outdated
Comment on lines +268 to +273
function parseRequestTimeoutFlag(raw: string | undefined): number | undefined {
if (raw === undefined) return undefined;
const seconds = Number(raw);
if (!Number.isFinite(seconds) || seconds <= 0) return undefined;
return Math.round(seconds * 1000);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether a shared --request-timeout parser/clamp already exists.
rg -n "requestTimeout" -C3 src/lib/client-factory.ts
rg -n "REQUEST_TIMEOUT_MIN_MS|REQUEST_TIMEOUT_MAX_MS" src

Repository: TestSprite/testsprite-cli

Length of output: 3563


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- src/commands/ci.ts (timeout-related lines) ---'
sed -n '240,320p' src/commands/ci.ts

echo
echo '--- src/lib/client-factory.ts (resolveRequestTimeoutMs and clamp) ---'
sed -n '110,170p' src/lib/client-factory.ts

echo
echo '--- search for parseRequestTimeoutFlag and requestTimeoutMs flow ---'
rg -n "parseRequestTimeoutFlag|requestTimeoutMs|request-timeout|REQUEST_TIMEOUT_MIN_MS|REQUEST_TIMEOUT_MAX_MS" src/commands src/lib -C2

Repository: TestSprite/testsprite-cli

Length of output: 41988


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- request-timeout handling in ci.ts ---'
grep -n -C4 "parseRequestTimeoutFlag" src/commands/ci.ts || true

echo
echo '--- comparable flag parsers in the CLI ---'
rg -n "function parse.*Timeout|parse.*Concurrency|Invalid.*timeout|must be a positive" src/commands src/lib -C2

Repository: TestSprite/testsprite-cli

Length of output: 6288


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,220p' src/commands/ci.ts | nl -ba | sed -n '250,310p'
echo
sed -n '110,170p' src/lib/client-factory.ts | nl -ba
echo
rg -n "parseTimeout|timeout.*validate|max-concurrency|positive integer|clamp" src/commands src/lib -C2

Repository: TestSprite/testsprite-cli

Length of output: 203


Use the shared request-timeout parser here instead of the local helper. Invalid or non-positive --request-timeout values are currently ignored and fall back to the default timeout, while the central parser raises a validation error; that makes ci behave differently from the other commands.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/ci.ts` around lines 268 - 273, Remove the local
parseRequestTimeoutFlag helper and update the ci command’s --request-timeout
handling to call the shared request-timeout parser used by other commands.
Preserve the central parser’s validation-error behavior for invalid or
non-positive values instead of silently falling back to the default timeout.

@zeshi-du

Copy link
Copy Markdown
Contributor

Maintainer verdict: the engineering is good, but a new top-level ci command is the one shape we decided not to build — asking for a reshape rather than closing, because most of this PR survives it.

Context, restated from #99 (07-18): the CI close-out ships as an official GitHub Action (in progress internally), not a new CLI subcommand. A permanent top-level command is a long-term maintenance commitment (the concern raised in #99's original triage), and testsprite ci as a wrapper around test run --all --wait adds a second way to do the same thing — which we've been deliberate about avoiding.

What we do want from this PR — the CI-native output, which is the genuinely missing half:

  • the ::error:: per-failed-run annotations emitter
  • the $GITHUB_STEP_SUMMARY Markdown table renderer
  • the --summary-file stable-JSON artifact

Reshape: attach those to the existing test run path (activated by GITHUB_ACTIONS=true, or an explicit --gh-output flag), drop src/commands/ci.ts as a command registration, keep the reducer/renderer/tests. The official Action then gets them for free by invoking test run --all --wait.

One defect to fix in the same pass: the JSON summary writes to stdout unconditionally, ignoring the global --output text flag (your own test at ci.test.ts:140 pins the bypass). Under the reshape this becomes moot on stdout, but the summary-file path should still respect the flag's contract.

Happy to re-review fast — the test discipline here is exactly what we want to merge.

@Andy00L Andy00L changed the title feat(cli): add "testsprite ci": one-shot run + gate + GitHub-native output feat(test): GitHub-native CI output for "test run --all" (--gh-output, --summary-file) Jul 23, 2026
@Andy00L

Andy00L commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Reshaped as requested: the top-level command is gone; the reducer/renderer/emitter moved to src/lib/gh-output.ts and hook into test run --all (auto on GITHUB_ACTIONS=true, forced by --gh-output). --summary-file is now a machine artifact on the run path and stdout stays owned by the batch envelope, which also settles the --output contract defect. Full suite green (2023 tests). Ready for the fast re-review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/test.ts`:
- Around line 9221-9234: Extend the validation around the existing ghOutput and
summaryFile checks to reject both flags when --wait is not enabled, using
localValidationError and the existing validation exit code. Apply this to the
--all path while preserving the current single-id restrictions, so CI-result
artifacts are only accepted for test run --all --wait.

In `@src/lib/gh-output.ts`:
- Around line 114-119: Keep GitHub annotations off JSON stdout: update the
annotation loop in gh-output.ts to use an output-mode-aware auxiliary sink,
revise gh-output.test.ts so annotations are not treated as JSON-command stdout,
and update test.ts to avoid wiring annotations to process.stdout when --output
json is active. Add or update the integration assertion ensuring JSON stdout
remains parseable.
- Around line 40-60: Update summarizeAcceptedPayload to validate the JSON.parse
result is a non-null record before accessing accepted, and filter or safely skip
accepted entries that are null or not records before reading row.error or other
fields. Preserve valid-row mapping behavior and add regression coverage for JSON
null and accepted arrays containing null entries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ecc7b5ec-6909-43a3-9826-dc987e24dbdd

📥 Commits

Reviewing files that changed from the base of the PR and between 995555a and 07d4e03.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (3)
  • src/commands/test.ts
  • src/lib/gh-output.test.ts
  • src/lib/gh-output.ts

Comment thread src/commands/test.ts Outdated
Comment thread src/lib/gh-output.ts Outdated
Comment thread src/lib/gh-output.ts Outdated
@Andy00L

Andy00L commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

CI red root-caused: the three failing run-spec tests inherited the runner's real GITHUB_ACTIONS=true, which (correctly) activated the new gh-output layer and appended ::error:: lines to the stdout they JSON.parse. Fixed by injecting an empty env into those deps; verified locally under GITHUB_ACTIONS=true (full suite 2023 green).

@Andy00L

Andy00L commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

All three review points addressed:

  1. --gh-output / --summary-file now require --all --wait (without --wait there is no terminal envelope to reduce; both flags reject loudly, help text updated).
  2. Annotations no longer touch JSON stdout: emitGithubOutputs gained a dedicated annotations sink and the run path wires it to stderr under --output json (the Actions runner parses workflow commands on both streams); integration test asserts stdout stays parseable JSON while ::error:: lands on stderr.
  3. summarizeAcceptedPayload now treats null/non-record payloads and null rows as no-data instead of crashing; regressions added for null, primitives, and accepted: [null, 42, ...].
    Full suite green under GITHUB_ACTIONS=true (2027 tests).

@zeshi-du zeshi-du left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-reviewed after the reshape — this is exactly what was asked: no new top-level command, --gh-output/--summary-file attached to test run --all --wait with GITHUB_ACTIONS auto-enable, artifact still written on a failing gate, stdout contract kept clean. Fast turnaround appreciated.

@zeshi-du
zeshi-du merged commit fe07bc9 into TestSprite:main Jul 23, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Hackathon] Add "testsprite ci": one-shot run + gate + GitHub-native output

2 participants