Skip to content

feat: upgrade to commitlint v21 on node 24 - #1

Merged
robinbowes merged 3 commits into
masterfrom
feat/commitlint-v21
Aug 11, 2026
Merged

feat: upgrade to commitlint v21 on node 24#1
robinbowes merged 3 commits into
masterfrom
feat/commitlint-v21

Conversation

@robinbowes

Copy link
Copy Markdown

Fork of wagoid/commitlint-github-action, brought up to commitlint v21.

Why fork

Upstream is unmaintained — last commit to master was 2025-01-14, and the maintainer has no public activity. PR #837 bumps @commitlint to v20 and has been open and MERGEABLE since 2026-07-20 with no response.

The bug that matters to us: @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));

Pinned to ^19, this action fails Dependabot commits on body-max-line-length — their bodies are markdown link lists (one real example is 314 chars on a line) — while a local pre-commit hook resolving v21 passes the same commit.

What changed

  • All @commitlint/* packages to ^21.
  • Node 20.16.0 → 24.19.0 LTS in the Dockerfile, .nvmrc, engines, and workflows. commitlint v21 requires node >= 22.12, which is why upstream PR fix(deps): bump @commitlint packages to v20 wagoid/commitlint-github-action#837 stopped at v20.
  • commitlint-plugin-function-rules ^4^5.0.1. v4's peer range is @commitlint/lint ">=19 <20", which breaks npm ci outright; ^5.0.1 allows ">=19 <22".
  • Release moved from Docker Hub (commit-and-tag-version + DOCKER_USERNAME/DOCKER_TOKEN) to ghcr.io, triggered by pushing a v* tag.
  • CI gained an image-builds job. That job is what surfaced the peer conflict — npm install had silently resolved around it, only npm ci in the container failed.
  • Fixed the FromAsCasing Dockerfile warning.

Verification

  • npm run lint clean, 39/39 tests pass, npm run build succeeds.
  • docker build succeeds; image reports v24.19.0 and @commitlint/lint 21.2.0.
  • Replayed the action's own load+lint path (src/action.mjs:146) against a real commitlint.config.mjs and the Dependabot commit that broke CI:
Case Result
Dependabot commit, 314-char body line valid ✅
Sentence-case subject with subject-case: [0] valid ✅ — the .mjs override is honored, so #836 does not bite us
Malformed commit rejected ✅

After merge

  1. Push tag v6.3.0publish-image.yml builds ghcr.io/yo61/commitlint-github-action:6.3.0.
  2. The GHCR package must be set public, or consuming repos cannot pull it.
  3. Point the 9 repos at this action by SHA.

Upstream is unmaintained: the last commit to master was 2025-01-14, and
PR wagoid#837 (bump @commitlint to v20) has sat open since 2026-07-20.

@commitlint/ensure v20 added an exemption for long lines containing a
URL. Pinned to ^19, this action fails Dependabot commits on
body-max-line-length, because their bodies are markdown link lists well
over 100 chars, while a local pre-commit hook running v21 passes them.

Take all @commitlint packages to ^21. That needs node >= 22.12, so move
the image and the workflows to node 24.19.0 LTS. Bump
commitlint-plugin-function-rules to ^5, the first release whose peer
range admits v21; ^4 caps at "<20" and breaks npm ci.

Publish to ghcr.io from a pushed tag rather than to Docker Hub via
commit-and-tag-version, which needed credentials this fork does not
have. CI now also builds the image on every push, which is what caught
the peer conflict.
The commitlint-pulling-from-docker-hub job ran the action without
rewriting action.yml, so it pulled whichever tag action.yml named. That
tag only exists once the image is published, which happens after merge,
so the job failed on every pull request that changed the version.

Drop it and verify the image in publish-image.yml, after the push, where
it exists. The check pulls from a cleared local cache so it exercises the
registry rather than the layer just built.
actionlint refused checkout@v3 and cache@v3 outright as too old to run,
and the runners were already forcing their node 20 entrypoints onto node
24. zizmor reported 20 findings, 7 of them high.

Pin every action to a SHA with a version comment, set persist-credentials
false so a checkout token cannot be picked up from the workspace, default
the workflows to no permissions and grant each job only what it uses, and
pass the commitlint results through the environment rather than
interpolating them into a run block.

actionlint and zizmor are both clean afterwards.
@robinbowes
robinbowes merged commit 31e5e04 into master Aug 11, 2026
4 checks passed
@robinbowes
robinbowes deleted the feat/commitlint-v21 branch August 11, 2026 17:25
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