Repository navigation
feat: let the cli annotate why the test step failed - #153
Merged
dfrankland merged 4 commits intoNov 4, 2026
Conversation
TylerJang27
approved these changes
Oct 26, 2026
TylerJang27
left a comment
Collaborator
There was a problem hiding this comment.
Will include more notes in the analytics-cli PRs
Collaborator
There was a problem hiding this comment.
Should we an action to call this like https://github.com/trunk-io/trunk2/blob/main/.github/workflows/sync-dynamic-ci-filter-schema.yaml? Just worried about discoverability more than anything
Comment on lines
+129
to
+130
| ALL_FAILURES_QUARANTINED: undefined, | ||
| NO_FAILURES: undefined, |
Collaborator
There was a problem hiding this comment.
nit: Minor smell having these in the map as undefined
Comment on lines
+120
to
+123
| /** | ||
| * A run that fails outright exits with this code, or 0 when it's null. Upload reports exit with their own `exitCode`. | ||
| */ | ||
| exitCode: number | null; |
Collaborator
There was a problem hiding this comment.
Is this a duplicate of the top level exit code?
| * Failed tests that were quarantined, grouped like `failures`. | ||
| */ | ||
| quarantined: FailureGroup[]; | ||
| quarantining: Quarantining; |
Collaborator
There was a problem hiding this comment.
Nit: add one more wrapper/invocation type (was this a analytics-cli test vs. `analytics-cli upload)
dfrankland
force-pushed
the
dylan/trunk-19074-read-cli-summary
branch
from
November 4, 2026 21:30
535ec9e to
24aa9a3
Compare
The action now points the cli's summary at TRUNK_SUMMARY_OUTPUT_FILE and, when the cli exits non-zero, reads its outcome to say which case it is: - NO_TESTS_FOUND: the step failed without running any tests. - FAILURES_NOT_QUARANTINED: the results contain non quarantined failures. - NO_FAILURES_BUT_NON_ZERO_EXIT_CODE: the step failed, but none of the tests it reported did. Older cli versions ignore the env var and write nothing, so they keep the hedged message. A summary path the workflow set itself is read but left in place. Part of TRUNK-19074. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The summary's types and its runtime check now both come from src/summary-schema.json, a copy of `trunk-analytics-cli summary-output-schema`, instead of a hand-written guess at its shape: - `pnpm generate:summary-schema [cli]` refreshes the copy from a cli binary and generates src/summary-schema.ts with json-schema-to-typescript. CI checks the types match the copy. - readSummary validates the file with zod's `fromJSONSchema` against the same copy, so a summary whose shape changed under us reads as no summary rather than a wrong cast. - The outcome messages are keyed by the generated `Outcome`, so a new outcome in a refreshed schema fails the build until it's handled. Workflows can now keep the summary: a `summary-output-file` input picks where the cli writes it, and the `summary-output-file` output gives the path whenever the cli wrote one. The action no longer deletes it; the default lives in RUNNER_TEMP, which the runner empties after the job. Part of TRUNK-19074. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Reading the summary's outcome meant the action had to track the cli's summary format. Now, when the cli exits non-zero, the action asks it to annotate the failure itself with a hidden `trunk-analytics-cli github-annotations --summary-file <path>`, which reads the summary the run just wrote and prints `::error::` lines. The action passes those through and fails the step without an annotation of its own. Older cli versions don't have the command, so its error is swallowed and the action falls back to its own messages, as does a cancelled step, which the cli only sees as exit code 1. This drops zod, json-schema-to-typescript, the copy of the cli's schema and the generated types. The summary-output-file input and output stay; the action only passes that path through. Part of TRUNK-19074. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The CLI doesn't write a summary when clap rejects its arguments, when it is stopped by a signal or panics, or when it predates TRUNK_SUMMARY_OUTPUT_FILE. A summary left at a reused path (the summary-output-file input, or a job-level TRUNK_SUMMARY_OUTPUT_FILE) was then read as this run's: the annotation gave the earlier run's reason, and the output pointed at the old file. The action removes the path before the run and never after, so later steps can still read the summary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dfrankland
force-pushed
the
dylan/trunk-19074-read-cli-summary
branch
from
November 4, 2026 21:31
24aa9a3 to
92e9b2c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Stacked on #152. The action side of ask 1 of TRUNK-19074: when the test step failed without running any tests, the annotation should say so, instead of hedging.
The CLI knows why a run failed; the action only knows that it did. So the action now has the CLI word the failure, rather than reading the CLI's summary format itself.
TRUNK_SUMMARY_OUTPUT_FILE, the env var for--summary-output-file(Add --summary-output-file to upload and test analytics-cli#1246).trunk-analytics-cli github-annotations --summary-file <path>. It reads the summary the run just wrote and prints::error::lines.process.exitCode, notcore.setFailed, which would add a second annotation.A cancelled step skips the CLI and keeps the action's own message, because
cancelledreaches the CLI as exit code 1, the same asfailure. Exit 70 still goes to telemetry asCommand failed with exit code 70.The action never reads the summary, so it doesn't depend on the summary's format.
Keeping the summary
summary-output-fileinput: where the CLI writes the summary, e.g. for a PR comment.summary-output-fileoutput: the path, set whenever the CLI wrote a summary.TRUNK_SUMMARY_OUTPUT_FILEthe workflow already set is used. Otherwise the file goes in$RUNNER_TEMP.$RUNNER_TEMPafter each job.Depends on the CLI
github-annotationsis added by trunk-io/analytics-cli#1265, which isn't merged or released yet. It implements this interface:trunk-analytics-cli github-annotations --summary-file <path>.::error::…on stdout, escaped per GitHub's rules.Until a CLI release has the command, every run takes the fallback, which is exactly #152's behaviour. Nothing checks the CLI version.
Testing
jest: 80 tests pass.errors.test.ts: a CLI-written annotation fails the step withoutsetFailed; exit 70 still reaches telemetry; a cancelled step skips the CLI; the fallback when the CLI can't annotate.summary.test.ts:annotateFromCliagainst a stand-in CLI script. It checks the arguments passed, the output passed through, and the fallbacks for a missing command, empty output and a missing binary. It also checks where the summary path comes from: the input, then the env var, thenRUNNER_TEMP.download.test.ts: the output is set when the summary exists and left unset when it doesn't, andTRUNK_SUMMARY_OUTPUT_FILEis passed to the CLI.tsc --noEmit,eslintandprettier --check: clean.dist/is rebuilt withncc build -m src/index.ts -o dist. That's the pre-commit hook's build step; I committed with--no-verifybecause trunk's hook couldn't findpnpmin my environment.🤖 Generated with Claude Code