Skip to content

Tags: commit-check/commit-check

Tags

v2.18.2

Toggle v2.18.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix: keep a piped message out of the branch, author, tag and push che…

…cks (#579)

* fix: keep a piped message out of the branch, author, tag and push checks

`printf 'feat: ...' | commit-check --message --branch` failed CC201 with
the message as the branch name. Every requested check read the one piped
value as its own: the author checks judged the message as a name and an
email, --tag as tag names, and --no-force-push parsed message lines as
push refs and passed having compared nothing.

Next to --message, stdin is now the message and nothing else. The other
checks read git, as they already do when the message comes from a file.
Without --message nothing changes: a lone --branch, --tag or
--no-force-push still reads what is piped to it.

* refactor: trim the piped-message fix to a context property

Same behaviour, half the diff. The helper that decided whether a check
may read stdin as its own value is now ValidationContext.piped_value,
the long comments are down to one line each, and main.py no longer keeps
a second variable for the value it hands on.

The tests keep what pins the fix -- next to -m, branch, author, tag and
push checks read git; a bad branch still fails; the push check compares
with the upstream; a lone check still reads what is piped -- and drop the
engine test that repeated the CLI case and the one that restated the
reported run. Against main's code the six combination cases fail and the
five lone-check cases pass.

v2.18.1

Toggle v2.18.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: switch the commit-check badge to the new brand (#584)

The badge keeps its Signal Blue #2c9ccd, takes an Ink label, and carries
the Commit Check mark in place of the generic Git logo, matching
commit-check.com and the org branding (commit-check/.github#61). It now
links to commit-check.com, where a reader who wonders what the badge
means finds out.

v2.18.0

Toggle v2.18.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: update commit-check version to v2.18.0 (#576)

v2.17.0

Toggle v2.17.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: update commit-check version to v2.17.0 (#566)

Pin the README's pre-commit snippets to the release that carries the
fix field and the warn level, ahead of the tag, so the snippets a new
user copies install the version the README describes.

v2.16.0

Toggle v2.16.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: Update commit-check version to v2.16.0 (#561)

* docs: Update commit-check version to v2.16.0

* docs: drop the shipped-later notes now the pins name v2.16.0

v2.15.1

Toggle v2.15.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
chore: Update commit-check version to v2.15.1

v2.15.0

Toggle v2.15.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
chore(deps): bump the github-actions group with 2 updates (#542)

Bumps the github-actions group with 2 updates: [CodSpeedHQ/action](https://github.com/codspeedhq/action) and [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance).


Updates `CodSpeedHQ/action` from 5.0.1 to 5.0.3
- [Release notes](https://github.com/codspeedhq/action/releases)
- [Changelog](https://github.com/CodSpeedHQ/action/blob/main/CHANGELOG.md)
- [Commits](CodSpeedHQ/action@8847237...4296e51)

Updates `actions/attest-build-provenance` from 4.1.1 to 4.2.2
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](actions/attest-build-provenance@0f67c3f...4d10147)

---
updated-dependencies:
- dependency-name: CodSpeedHQ/action
  dependency-version: 5.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: actions/attest-build-provenance
  dependency-version: 4.2.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

v2.14.0

Toggle v2.14.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
ci: check this pull request with the commit-check in it (#541)

* ci: check this pull request with the commit-check in it

The workflow ran commit-check-action, which installs a released
commit-check -- so every pull request was checked by the version before
it. #540 had its own title rejected by the bug it was fixing, and the
same thing happened tonight in the .github repository. A self-test that
cannot see the change under test is not a self-test, and the action's
version does not track the engine's, so a fix needed two releases and a
bump before it could be reproduced here.

Install from the checkout instead. The cost is job-summary and
pr-comments, which are the action's rendering rather than the engine's
behaviour and are covered by the action's own repository.

Enumerating the commits is not optional. On a pull_request checkout HEAD
is the synthetic merge commit, which the engine skips, so a bare
'commit-check --message' reports a pass having read nothing. Verified on
a scratch repository holding one plainly bad commit: the bare form exits
0, this workflow exits 1. HEAD^1..HEAD^2 gives the real commits, and a
guard fails loudly if HEAD is not a merge so the failure can never be
silence.

The pull request title reaches the CLI through the environment. Titles
are attacker-controlled, and interpolating one into a run: block is a
script injection.

* ci: install with the flags the other workflows already use

SonarCloud's unpinned-install rule failed the quality gate on a bare
'pip install .'. main.yml and publish-package.yml both carry
--only-binary :all: for exactly this, added in #479; this step was the
one that did not. Verified it still installs a local path: the resulting
version reads 2.13.4.post1.dev4, which is the checkout rather than the
release.

* ci: close stdin so the branch and author checks cannot hang

With stdin left open, commit-check waits to read a message even when
only --branch and --author-name were asked for. Reproduced locally: the
process runs until killed, which in CI is a step that hangs rather than
fails.

Closing stdin also decides what the author checks look at, so the
comment says which: the author of HEAD, matching what the action
reports today.

* ci: revert the install flags that did not do what I thought

I added --only-binary :all: because main.yml and publish-package.yml
carry it and #479 mentions SonarCloud. It does not satisfy this rule --
the alert stayed -- and --upgrade pip added a second flagged line, so
the change took the count from one to two.

Back to one plain line. The remaining alert needs a decision rather than
another flag: main.yml has the identical install and passes only because
it is not new code, so locking would mean a hash-pinned requirements
file or reviving the stale uv.lock.

* refactor: move the checks into the nox session that already existed

The session was already there and already wrong: it ran a bare
'commit-check --message', which on a pull_request checkout inspects the
merge commit, which the engine skips. Leaving it that way while putting
a correct copy in YAML would have left two implementations, with the
broken one being the one a contributor reaches for locally.

So the logic lives in noxfile.py and the workflow is one line. The same
command now reproduces a CI failure on a laptop, which was half the
reason for moving off the action.

The session adapts rather than assuming CI: HEAD^1..HEAD^2 when the
checkout is a merge commit, HEAD otherwise, and the title only when
PR_TITLE is set. It still refuses to pass silently -- missing HEAD^2
during a pull_request event is an error, not a fallback.

Verified locally: enumerates 2 of 2 commits on a merge ref and 0 on a
plain one, exits 1 on a bad title having still run the branch and author
checks, exits 0 on a good one, and no longer hangs on stdin.

* refactor: keep the nox session simple, and CI logic in the workflow

Reverts b95acfd, which moved the pull request checks into the nox
session. The session is a developer command -- run commit-check on your
working copy -- and folding CI's shape into it made the simple thing
complicated for no gain.

The justification was wrong too. I claimed the same command would
reproduce a CI failure locally, then had to branch on whether HEAD^2
exists, because locally there is no merge commit and no PR title. A
command that behaves differently in the two places does not reproduce
one from the other.

It also introduced a bug the shell never had: filtering on .strip()
dropped empty messages, which this repository rejects via
allow_empty_commits = false, so such a commit would have been reported
as 'HEAD is not a merge commit' instead of as the thing it is. The shell
keeps them: printf 'a\0\0b\0' through 'read -r -d' yields a, empty, b.

noxfile.py is now byte-identical to main.

* ci: check the title, and stop checking messages that get discarded

main is linear and every subject ends in (#N): this repository squashes,
so the commits on a branch never reach it. The title becomes the subject.
Checking each commit was protecting history that does not exist, and cost
thirty lines of shell to do it.

What is left is two commands. The bare 'commit-check --message' still
cannot be one of them -- it reads HEAD, the synthetic merge commit, which
the engine skips and would pass having read nothing -- so the title goes
in through stdin instead, which sidesteps HEAD entirely and needs no
enumeration.

Contributors lose CI feedback on intermediate commit messages. The
pre-commit hook is where that belongs anyway: it arrives while the
message is being written rather than a round trip later.

Verified on a merge ref: bad title 1, good title 0, good title with a
bad branch name 1.

v2.13.4

Toggle v2.13.4's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat: report a skipped check as skipped, not as passed (#537)

* feat: report a skipped check as skipped, not as passed

A rule that never ran was reported as a pass. commit-check-action#258 is
the visible cost: every check on it rendered as a green tick and the
summary announced "All 5 checks passed", when in fact nothing had been
validated -- the author is dependabot[bot], which the org config lists in
ignore_authors. A bypassed policy was indistinguishable from an enforced
one, in the JSON, in the Python API, and in anything rendering them.

Measured on that exact case before the change: every rule came back
"status": "pass" with "value": "", the empty value being the only trace
that a skip had happened, and an incidental one at that.

Adds ValidationResult.SKIP and returns it from the guards that already
decide this -- _should_skip_commit_validation, _should_skip_branch_
validation, and the ignored-author branch of _validate_author. Those
helpers are named for skipping; they were simply reporting it as PASS.
validate_all_detailed maps SKIP to "skip" and forces the value empty,
since a rule that did not run examined nothing.

Overall status is "skip" only when every check skipped; one real verdict
still yields "pass" or "fail". Only "fail" is an error, so the exit code
is unchanged for existing callers and code branching on status == "fail"
keeps working.

The overall-status rule was duplicated between the CLI's --format json
and the Python API, which is how the CLI kept printing "pass" for a fully
skipped run after the API had been fixed. It now lives once, in
engine.overall_status(), used by both. That also fixes a latent bug in
the CLI's exit code: `0 if overall == "pass" else 1` would have turned a
skipped run into a failure.

Verified end to end in a repository shaped like #258 -- same repo, same
config, only the author differing:

    dependabot[bot]  -> overall "skip", every check "skip", exit 0
    a human          -> overall "pass", values reported,    exit 0
    a human, bad msg -> exit 1

The twelve existing tests that asserted PASS on these paths are all named
for skipping (ignored_author, skips_validation, skip_conditions); they now
assert SKIP. Four new API tests pin the behaviour, including a control
that only the author differs so the skip test cannot pass by the rules
having quietly stopped running for everyone. Reverting the skip reporting
turns the first of them red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn

* fix: preserve skip when the API merges checks from several runs

Review caught a third and fourth copy of the reduce-to-overall rule that
the first commit missed. validate_author(name=..., email=...) merges two
separate runs, and validate_all() merges up to three; each combined its
checks with a private `"fail" if any(...) else "pass"`, so a call in which
every nested check skipped still reported "pass" -- the exact defect the
skip status exists to prevent, surviving in the two entry points most
likely to be called by automation.

overall_status() now takes plain status strings rather than CheckOutcome
objects, which is what lets every caller share it: the CLI, _build_result,
and both combined paths, which hold already-serialised dicts. Four copies
of this rule is how it drifted in the first place, so there is now one.

Both new tests fail if the per-path rule is restored.

Also covers the two skip branches codecov flagged, in BodyValidator and in
CommitTypeValidator's non-ignore_authors path. Each comes with a control
that changes only the author, so neither can pass by the rule having
quietly stopped running for everyone. No line added by this PR is left
uncovered.

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>

v2.13.3

Toggle v2.13.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
docs: refresh README sample output to match what commit-check prints (#…

…535)

The README's output blocks predate rule IDs, so they showed neither the
CCxxx identifiers nor the Docs links that every failure now prints. Same
staleness #530 fixed in the demo GIF; the docs site was already current,
only the README had been left behind.

Measured by running each documented command against the checkout:

  Type message check failed ==> ...  ->  CC001 message check failed ==> ...
  Type branch check failed  ==> ...  ->  CC201 branch check failed  ==> ...

plus a trailing Docs: https://commit-check.com/rules/#ccNNN line on both,
and two commit types the list had never picked up (perf, build).

Four more blocks were stale the same way: --no-banner carried an
"It doesn't match regex:" line that no longer exists in the source,
--compact now prints the rule id, both --format json examples were missing
rule_id and docs_url and named a subject_imperative check the default run
does not emit (it reports subject_max_length and subject_min_length), and
the Python API return-value schema was missing rule_id and docs_url.

Every block was re-captured and compared byte for byte against the
committed text, so these are transcripts rather than transcriptions.