Skip to content

ci: use a maintained commitlint action fork - #36

Merged
robinbowes merged 1 commit into
mainfrom
fix/commitlint-ci-version-skew
Aug 11, 2026
Merged

ci: use a maintained commitlint action fork#36
robinbowes merged 1 commit into
mainfrom
fix/commitlint-ci-version-skew

Conversation

@robinbowes

@robinbowes robinbowes commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What

Points the commitlint job at yo61/commitlint-github-action — a maintained fork running commitlint v21 on Node 24.

Why

Dependabot PRs fail the commitlint job on body-max-line-length, while the pre-commit lint job passes the same commit. Same commit, same commitlint.config.mjs, different verdict.

@commitlint/ensure v20 added an exemption for long lines containing a URL:

const URL_REGEX = /\bhttps?:\/\/\S+/;
value.split(/\r?\n/).every((line) => URL_REGEX.test(line) || ensure(line, max));

wagoid/commitlint-github-action@v6.2.1 is the latest release and pins @commitlint/ensure: ^19.0.3, so CI enforced a stricter rule than the config intends. Dependabot bodies are markdown link lists — one real example is 314 characters on a single line.

Upstream is unmaintained: no commit to master since 2025-01-14, and the v20 bump (PR #837) has been open and mergeable since July with no response.

The fork

yo61/commitlint-github-action@v6.3.0 — all @commitlint/* at ^21, Node 24.19.0 LTS (v21 needs node >= 22.12), commitlint-plugin-function-rules at ^5 (v4's peer range caps at <20 and breaks npm ci), published to ghcr.io from a pushed tag. Actions SHA-pinned, least-privilege permissions, actionlint and zizmor clean.

Verified against the action's own load+lint path with a real commitlint.config.mjs:

Case Result
Dependabot commit, 314-char body line valid
Sentence-case subject with subject-case: [0] valid — the .mjs override is honored
Malformed commit rejected

Commit subjects stay validated for Dependabot as well as humans, rather than exempting bot commits.

yo61-lastlight[bot]
yo61-lastlight Bot previously approved these changes Aug 11, 2026

@yo61-lastlight yo61-lastlight 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.

Replaces wagoid/commitlint-github-action (pinned @commitlint/ensure ^19, no URL-length exemption) with a direct npx invocation pinned to @commitlint/cli@21.2.1 + @commitlint/config-conventional@21.2.0, matching the pre-commit hook's ^21 resolution. The permissions change (drop pull-requests: read, add contents: read) correctly reflects that the commit range now comes from the local checkout rather than the GitHub API. The --from/--to range uses the PR's base/head SHAs from the event context, both reachable via the existing fetch-depth: 0 checkout. Verified against the diff and the sibling pre-commit config (which already pins config-conventional@^21.2.0) — versions are consistent across the two enforcement points, which is the actual fix for the skew described in the issue. No correctness, security, or contract issues found.

@yo61-lastlight yo61-lastlight 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.

Re-review (prior yo61-lastlight review at 055f254 was DISMISSED after the head SHA moved to 50c879a, which added .github/commitlint/package.json + package-lock.json and the matching dependabot entry). The pin-in-a-manifest approach and the permissions change are sound in principle, but the CI job is currently red for a real, reproducible reason: commitlint resolves extends: ['@commitlint/config-conventional'] relative to the directory containing commitlint.config.mjs (repo root), which has no node_modules — the pinned install lives under .github/commitlint instead. Reproduced locally: after npm ci in .github/commitlint, running the exact workflow command throws Error: Cannot find module "@commitlint/config-conventional" from "<repo root>". Setting NODE_PATH=<repo>/.github/commitlint/node_modules before the same invocation makes it resolve and lint cleanly (found 0 problems on both commits in this PR's range) — that's the missing piece, and it matches the currently-failing 'Lint commit messages' check exactly.

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +37 to +31
- name: Install commitlint
working-directory: .github/commitlint
run: npm ci
- name: Lint commit messages

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[Critical] commitlint can't resolve @commitlint/config-conventional — reproduces the current CI failure

commitlint.config.mjs lives at the repo root and its extends is resolved relative to that file's directory, not to .github/commitlint where this step installs node_modules. Every invocation throws Error: Cannot find module "@commitlint/config-conventional" from "<repo root>" (reproduced locally with the exact command from this step) — this is the currently-failing 'Lint commit messages' step on this PR's own head commit. Setting NODE_PATH to the nested node_modules before the call fixes it (verified locally: found 0 problems, 0 warnings on both commits in this PR's range).

Suggested change
- name: Lint commit messages
- name: Lint commit messages
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
NODE_PATH: ${{ github.workspace }}/.github/commitlint/node_modules
run: |
set -euo pipefail
./.github/commitlint/node_modules/.bin/commitlint \
--config commitlint.config.mjs \
--from "$BASE_SHA" --to "$HEAD_SHA" --verbose

@robinbowes
robinbowes force-pushed the fix/commitlint-ci-version-skew branch from 50c879a to 055f254 Compare August 11, 2026 13:13
wagoid/commitlint-github-action pins @commitlint/ensure ^19. The
exemption for long lines containing a URL landed in v20, so CI enforced
a stricter rule than the config intended and failed Dependabot commits
on body-max-line-length, while the pre-commit hook resolving v21 passed
the same commit.

Upstream is unmaintained: master has had no commit since 2025-01-14 and
the v20 bump has sat open since July. Point at yo61/commitlint-github-action
instead, which runs commitlint v21 on node 24 and is published to ghcr.io.
@robinbowes robinbowes changed the title ci: run commitlint directly instead of stale action ci: use a maintained commitlint action fork Aug 11, 2026
@robinbowes
robinbowes force-pushed the fix/commitlint-ci-version-skew branch from 055f254 to 17e0f6d Compare August 11, 2026 13:39

@yo61-lastlight yo61-lastlight 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.

Prior-findings ledger: the earlier last-light[bot] CHANGES_REQUESTED review (at 50c879a) flagged that the .github/commitlint-based approach (a pinned local install invoked directly) couldn't resolve @commitlint/config-conventional because commitlint.config.mjs's extends resolves relative to the repo root, not the nested node_modules — fixed, but not by patching NODE_PATH: the whole approach was replaced. The PR now (17e0f6d) is a single-line swap of the commitlint job's action reference from wagoid/commitlint-github-action@v6.2.1 (pins @commitlint/ensure ^19, missing the v20 URL-length exemption) to yo61/commitlint-github-action@v6.3.0 (all @commitlint/* at ^21, matching the pre-commit hook's resolution), with a comment explaining why. The .github/commitlint node_modules that triggered the earlier finding are gone from the tree/diff entirely, so that bug no longer exists to re-raise. The change is minimal, SHA-pinned, and CI is green on this head commit including the commitlint job itself, which is direct evidence the fork resolves the config correctly against real commits in this range (including the long-body Dependabot-style case the PR describes). Checked the fork's action.yml/package.json directly: configFile input and @actions/github usage are unchanged from upstream, so the unchanged permissions: pull-requests: read block is still correct. No correctness or contract issues in the diff itself. One inherent tradeoff worth naming for the record rather than blocking: the fork's action.yml still resolves its Docker image by a mutable tag (docker://ghcr.io/yo61/commitlint-github-action:6.3.0) rather than a digest, so the Actions-repo SHA pin in this workflow doesn't pin the actual container contents — but that mirrors upstream's own pattern (wagoid's action.yml uses the same floating-tag scheme) and the fork lives under the same account that owns this repo, so it isn't a new or worse trust boundary than before.

@robinbowes
robinbowes merged commit 25887d3 into main Aug 11, 2026
4 checks passed
@robinbowes
robinbowes deleted the fix/commitlint-ci-version-skew branch August 11, 2026 14:05
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.

1 participant