Skip to content

Commit ce3ddb1

Browse files
shenxianpengclaude
andauthored
feat: render structured output for step log, job summary, and PR comments (#251)
* feat: render structured output for step log, job summary, and PR comments Collect results via commit-check --format json and render all three output surfaces from the structured data instead of scraping plain text: - step log: grouped sections with ::error annotations per rule ID - job summary: policy report table with rule links and collapsible details - PR comment: compact table with rule links and collapsible details - new result output exposing structured JSON for downstream jobs * fix: make stdout UTF-8 for Windows runners Emoji and check marks (✔ ✖ ❌) cannot be encoded by the default cp1252 codec on Windows, crashing the action. Reconfigure stdout/stderr to UTF-8, matching commit-check core's _reconfigure_io. * refactor: unify job summary and PR comment rendering Use a single report renderer for both surfaces with a plain '# Commit Check' title (dropping 'Policy Report') and identical content on success and failure. PR comment matching also accepts the old hyphenated title so existing comments are still updated rather than duplicated. * refactor: split report title and status into separate lines The report now opens with a plain '# Commit Check' title line followed by the status line ('✅ All checks passed (N scopes)' or the failure count), keeping the PR comment and job summary identical. * feat: show passed checks in a collapsible section on success The success report now includes a '<details>' block listing every check that passed per scope, so users can confirm which rules were actually evaluated without leaving the summary or PR comment. * refactor: render passed checks like the step log in the details block Replace the passed-checks table with a fenced block mirroring the step log layout (group name followed by indented ✔ scope lines), so the success report reads the same as the action log. * feat: show checked values in the success details block Each scope line now shows the concrete value that was checked (PR title, commit subject, branch name, author name/email), truncated to one line, so the details block reads like the step log. * refactor: truncate checked values at 60 chars in the details block Long commit subjects wrapped inside the fenced details block, misaligning the scope lines. Trimming the value at 60 characters keeps the full line (prefix + value + parentheses) short enough to stay on one line. * feat: show checked values in the failure table The scope table now carries a 'Checked value' column (mirroring the success details), so a failing report answers what exactly was checked without expanding the failure details. * feat: show only failed values in the table and all checks in details The report table now fills the checked-value column only for failed scopes so failures stand out, and the collapsible details block lists every scope's value in step-log style (✔/✖) with the failure reason and suggestion under each failing rule, unifying the pass and fail layouts. * feat: add CodSpeed-style pass/fail stats to the report header The status line now reads '❌ 2 failures · ✅ 4 passed (6 scopes)' on failure and '✅ 11 passed (11 scopes)' on success, mirroring the compact emoji-plus-count style of CodSpeed reports so the pass ratio is visible without scanning the table. * feat: reduce the report table to failed scopes Passing scopes no longer pad the failure table with dash rows; the table lists only the failed scopes with their checked value and rule links, while the collapsible details block keeps the full pass/fail picture in step-log style. * docs: document the report output spec and pin it with golden tests Add an output specification block above render_report showing the exact success and failure layouts, and golden tests that assert the full rendered report byte-for-byte so the spec stays accurate as the output evolves. * fix: wire the result output through action.yml The output was declared with only a description, and the step that produces it had no id. Composite actions do not forward step outputs automatically, so steps.<id>.outputs.result resolved to the empty string and the fromJSON call in the README documentation would have failed the calling workflow. The Python side was already writing the payload to $GITHUB_OUTPUT correctly; the mapping above it was missing. The unit tests could not catch this — they exercise set_result_output, not the action.yml plumbing around it. Also notes in the README that a downstream step reading the result needs dry-run or continue-on-error, since a failing check otherwise ends the job before that step runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * feat: rework the report format and identify comments by a hidden marker Comment ownership was decided by `body.startswith("# Commit Check")`, and every match but the newest was deleted. A person opening a comment with that heading would have had it removed with no trace. Comments now carry a hidden `<!-- commit-check-action -->` marker, the way Codecov, SonarQube and CodSpeed identify theirs, and deletion is restricted to comments carrying it. A report from an earlier version has no marker, so it is adopted in place — but only when a bot posted it, since the only other signal is the title a person can type by accident. The header counted two different things at once: failures were counted in checks while "passed" and the total counted scopes, so one commit failing two rules rendered "2 failures · 2 passed (3 scopes)" and the numbers did not reconcile. Counts are now checks throughout — "2 of 4 checks failed" — and the details summary repeats the same total so the two can be checked against each other. The table dropped its Result column: only failed scopes reach the table, so it read ❌ on every row. The title moved to h2 with the project logo beside it, an h1 being louder than anything else in a PR comment, and the footer now names the commit-check version that produced the result, which is the first thing worth knowing when an outcome looks wrong. Smaller things found while reading: - an ImportError from PyGithub made the `except GithubException` clause raise NameError, which propagates past the `except Exception` under it and would have killed a step designed to never be fatal - GITHUB_STEP_SUMMARY was read with os.environ[] at import time, so main.py could not be imported outside Actions - the two details renderers differed only by a branch; the unparsable-output fallback now shows in the report instead of rendering an empty scope - build_result_body had no caller but its own test, and include_footer was never passed False - re and tempfile were imported but unused - GITHUB_REPOSITORY was passed to get_repo() unchecked, which mypy flags once PyGithub types are available The logo ships as PNG in this repository rather than as the SVG on commit-check.com: GitHub proxies comment images through camo, which handles SVG unreliably, and a local asset keeps the report free of a cross-repository dependency. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * fix: count things checked, not rule evaluations The header on this pull request read "1 of 100 checks failed". The number was arithmetically right — sixteen commit messages against six enabled rules, plus two branch rules and one each for author name and email — but it was the wrong number to show. It measured the size of the pull request rather than the strictness of the policy: the same configuration reports 100 on a fifteen-commit branch and 10 on a one-commit branch. Ninety-six of the hundred were the same six rules run again per commit, so the total also overstated the work done. Worst of all, the inflated denominator made a real failure look negligible: one bad commit out of fifteen is not "1 of 100". A check is now one thing that was checked — a commit message, the branch, the author — so the same report reads "1 of 19 checks failed", and the total is what the reader can count in the details block. Rule-level detail is unchanged; it lives in the table and the details, which is where someone goes to find out what to fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * fix: print the failure reason once and keep the log tree intact The job log showed the same sentence twice in a row under every failure: Error: Commit message should use imperative mood (...) value: feat: settle the report format ... Commit message should use imperative mood (...) The first line was an ::error workflow command carrying the first line of the error, the third was the listing printing the whole error underneath it. The annotation also broke the listing it sat inside. An ::error renders as a line of its own wherever it appears, so printing one between the ✖ scope line and its details split the indented tree apart. And its title= — the only place the rule ID was written — is shown in the annotations UI, never inline, so the visible line lost the rule ID entirely. Annotations are now emitted after every group, one per failure, with the scope in the message since the title cannot carry it. The listing keeps the reason, the rule ID, the value, the suggestion and the docs link, indented under the scope that failed. Passing scopes show the value that was checked, which the Markdown report already did. Also drops the "(0 failures)" that unparsable output rendered next to its ✖: there is no check list to count, and zero next to a cross reads as a contradiction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * fix: show the failing value in full and render one tree, not two The details block gave every passing scope its checked value but replaced it with the failure count on the one scope that failed, so the offending commit subject appeared nowhere in the report except the table, truncated at sixty characters. The value a reader has to act on was the only one they could not read. It now prints in full underneath the failing scope, where the step log already showed it. The two surfaces also disagreed about layout despite a docstring claiming one mirrored the other — four spaces of indent in the Markdown, six and eight in the log, and the rule name folded into the reason on one side but not the other. They now come from a single renderer, with the docs URL as the only difference: the Markdown report already links the rule ID from the table above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 6337ec1 commit ce3ddb1

5 files changed

Lines changed: 1377 additions & 496 deletions

File tree

‎README.md‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,34 @@ for all available options.
201201
> [Optional Inputs](#optional-inputs), so env vars and config files are the
202202
> recommended way to customize.
203203

204+
## Outputs
205+
206+
### `result`
207+
208+
Structured check results as JSON, available to downstream steps via
209+
[`fromJSON`](https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#fromjson):
210+
211+
```yaml
212+
- uses: commit-check/commit-check-action@v2
213+
id: commit-check
214+
with:
215+
dry-run: true # (1)
216+
217+
- name: Inspect results
218+
run: |
219+
echo "Status: ${{ fromJSON(steps.commit-check.outputs.result).status }}"
220+
echo "Scopes: ${{ toJSON(fromJSON(steps.commit-check.outputs.result).scopes) }}"
221+
```
222+
223+
1. Without `dry-run`, a failing check ends the job before any later step runs.
224+
Use `dry-run` (or `continue-on-error`) when a downstream step is meant to
225+
read the result and decide for itself.
226+
227+
Each scope carries the check outcomes (`rule_id`, `check`, `status`, `value`,
228+
`error`, `suggest`, `docs_url`) exactly as produced by
229+
`commit-check --format json`, so downstream jobs can build their own reports
230+
or gate on individual rules.
231+
204232
## GitHub Action Job Summary
205233

206234
By default, commit-check-action results are shown on the job summary page of the workflow.

‎action.yml‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,19 @@ inputs:
3737
description: check pull request title following conventional commits
3838
required: false
3939
default: false
40+
outputs:
41+
result:
42+
description: Structured check results as JSON (status + per-scope checks). Consume with fromJSON(steps.<id>.outputs.result).
43+
# Composite actions do not forward step outputs automatically: without this
44+
# mapping (and the step id it refers to) the output is always the empty
45+
# string, and fromJSON('') fails the calling workflow.
46+
value: ${{ steps.commit-check.outputs.result }}
47+
4048
runs:
4149
using: "composite"
4250
steps:
4351
- name: Install dependencies and run commit-check
52+
id: commit-check
4453
shell: bash
4554
run: |
4655
# Platform-specific settings

‎assets/logo.png‎

4.88 KB
Loading

0 commit comments

Comments
 (0)