| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
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>
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.
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>
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.
fix: resolve merge-base refs that exist only on the remote (#532) * fix: resolve merge-base refs that exist only on the remote CC202 reported "not rebased onto target branch" for a branch that was correctly based on main the whole time — in every CI checkout of a pull request. Two halves, same mistake: exit 128 from git means "could not resolve that name", and both call sites read it as "not an ancestor". _find_target_branch verifies refs/heads/main, falls back to verifying refs/remotes/origin/main, then returns the bare name either way. A pull request checkout has only the remote-tracking ref, so the caller ran git merge-base --is-ancestor main HEAD fatal: Not a valid object name main (exit 128) and the failure was reported as a rebase problem. The remote fallback now returns origin/<name> — the ref that was just verified. Note that writing require_rebase_target = "origin/main" in config is not a workaround: _find_target_branch tries refs/heads/origin/main and refs/remotes/origin/origin/main, finds neither, returns None, and the check silently passes without checking anything. The second half: get_branch_name() falls back to GITHUB_HEAD_REF, so a detached CI checkout reports a branch name that exists on no local ref. Same 128, same misreading. validate() now retries against HEAD — the same commit, always resolvable — and only a real non-zero ancestry answer fails. Measured in a clone shaped like the runner's checkout: _find_target_branch('main') -> 'main' (before fix) git_merge_base('main', 'HEAD') -> 128 git_merge_base('origin/main', 'HEAD') -> 0 The existing tests never caught this because none of them ran the code they named: two patched commit_check.util.git_merge_base while the engine imports the name directly, so the mock never bound and real git ran against whatever checkout pytest was in — one of them passed only because 128 was misread as FAIL. A third built its rule with no regex, so validate() returned PASS before reaching the mocked call (call count: 0). All three now assert against the engine's own reference, and two new tests drive real git in pull-request-shaped clones. Reverting either fix fails its test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * ci: run the checks again after a runner provisioning failure The build job died before reaching any code: GitHub's runner could not download its own actions ("Failed to resolve action download info. Error: Service Unavailable", three attempts). The workflow token is read-only, so a re-run cannot be requested through the API — an empty commit re-triggers everything and disappears in the squash merge. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * fix: check the remote branch before HEAD when resolving merge-base Re-review caught this before merge: the HEAD fallback traded the false failure for a false pass. On a pull_request event the runner checks out GitHub's synthetic merge commit, whose first parent IS the target tip — so is-ancestor(target, HEAD) is true by construction, for every branch, rebased or not. Measured on a diverged branch in that shape: git_merge_base('origin/main', 'feat/work') -> 128 git_merge_base('origin/main', 'HEAD') -> 0 <- wrong git_merge_base('origin/main', 'origin/feat/work') -> 1 <- the truth An unresolvable branch name now resolves through its remote-tracking ref first; HEAD remains only as the last resort, where it still gives a real answer on checkouts whose HEAD is the branch commit itself (push events, or a branch that was never pushed). A new test builds the merge-ref shape with a genuinely diverged branch and asserts FAIL — disabling the origin/<branch> step fails it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * chore: retrigger checks after a stuck workflow queue The CodSpeed run for 47cd595 has sat in "queued" for nine hours after yesterday's GitHub Actions incident and can no longer be cancelled ("Cannot cancel a workflow re-run that has not yet queued"), so its check never reports. CodeQL's Analyze (python) on the same SHA cannot be re-run through the API either — it answers 403 "cannot be retried". An empty commit is the only lever that reaches both: a new head SHA starts fresh check runs for the whole suite. No file changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * fix: resolve the pull request head when merge-base falls back to HEAD Review caught a residual false pass in the fallback chain. When the branch is unresolvable under both its own name and origin/<branch>, the last resort asked about HEAD -- but on a pull_request event HEAD is GitHub's synthetic merge commit, whose first parent IS the target tip, so it passes any branch. Measured in that shape with the remote ref removed, on a branch that is genuinely behind: git_merge_base('origin/main', 'feat/work') -> 128 git_merge_base('origin/main', 'origin/feat/work') -> 128 git_merge_base('origin/main', 'HEAD') -> 0 <- wrong git_merge_base('origin/main', 'HEAD^2') -> 1 <- the truth HEAD's second parent is the pull request head, the commit actually under review, so the fallback now asks about that whenever HEAD is a merge. This answers rather than giving up: where HEAD has a single parent it is the branch commit itself and still answers for itself, so the rebased detached-checkout case keeps passing. Adds git_rev_parse_verify to test for the second parent, and a test that builds the merge-ref shape with no remote ref and asserts FAIL -- restoring the plain HEAD fallback fails it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn * test: share the merge-ref setup between the diverged cases SonarCloud failed the quality gate at 14.2% duplication on new code (limit 3%). The two diverged-branch tests repeated the same twenty lines of setup and the same chdir/patch/validate dance. Extracts _diverged_merge_ref_clone for the shape and _validate_merge_base for the invocation, leaving each test as its distinguishing step plus an assertion. Net 13 lines lighter, and the regression test still fails when the plain HEAD fallback is restored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U9zFxq8V4qxG4aMzJhGBFn --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
chore: add AGENTS.md with AI agent guidelines (#523) * chore: add agent.md with AI agent guidelines * chore: rename agent.md to AGENTS.md * docs: require conventional branch and conventional commit in git rules * chore: Correct file name from agent.md to AGENTS.md * chore: Update AI agent guidelines in AGENTS.md Removed AI attribution guidelines and clarified commit practices.
fix: link rule IDs to their documentation in terminal output (#520) * fix: link rule IDs to their documentation in terminal output The rule ID now carries an OSC 8 hyperlink to its section of the rules reference, the way ruff links its codes, so the ID itself is what you click. Where that renders, the separate "Docs:" line is dropped: it was repeating an address the reader already has. Where it does not — a pipe, a CI log, a terminal that would print the escape as junk — the line stays, because there it is the only way to reach the address at all. Three things the previous output got wrong, all visible in a two-failure run: The blank line closed the suggestion rather than the block, so "Docs:" was separated from the rule it belonged to and butted against the next one. It now closes the block. The name was printed as the config key, subject_min_length, while the rules reference titles its sections in kebab-case. Reading a name off the terminal and searching the documentation for it found nothing. Both now say subject-min-length. The advice for the two length rules named no length: "Provide a meaningful subject (>= configured min)", directly under an error that had already said "at least 5 characters". The suggestion is now templated on the same value as the error. * fix: treat FORCE_HYPERLINK as an explicit boolean Any non-empty value counted as on, so FORCE_HYPERLINK=0 — the obvious way to ask for links to be turned off — turned them on instead. It now follows what FORCE_COLOR established and what ruff does: 0 disables even on a terminal that renders links, any other value enables. Also covers the length-rule substitution that had none. Removing the .format() call leaves the four new cases failing, so they hold the placeholder from reaching a user as a literal brace.
fix: detect space-separated AI model names in co-author trailers (#506) * fix: detect space-separated AI model names in trailers * ci: auto fixes from pre-commit.com hooks --------- Co-authored-by: sshevchenko <sshevchenko@evolution.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
chore: remove dead else branch in print_suggestion() (#487) * fix: remove dead else branch in print_suggestion() (#486) Removed the unreachable else branch in print_suggestion() that could never be entered since all callers already guard with a truthy check. Updated the type hint from str | None to str, and removed the corresponding test. Close #486 * fix: resolve mypy type error in print_suggestion call Fix the mypy error: the argument passed to print_suggestion in _print_failure is now guaranteed to be str (still works at runtime, just shuts up the type checker). --------- Co-authored-by: XEDAB <XEDAB@users.noreply.github.com>
| Back | FazBrowse Home | New Git URL |