Build1 publisher2 min readPublished Updated
Signed commits and commitlint move the first governance gate onto the developer's laptop
A dev.to walkthrough puts commit signing, commitlint, Gitleaks and OWASP SAST into Husky hooks, then runs commitlint again in GitHub Actions because any developer can skip the local hooks with --no-verify.
The Engineer · Build desk

What happened
- The post defines governance as a set of control points running through the whole pipeline, from the developer's first commit to the record of the production deploy, not a stage that appears at the end.
- On the developer's machine, git commit -S signs with an SSH or ED25519 key while a Husky commit-msg hook runs commitlint against the Conventional Commits format feat(scope): descripcion.
- The pre-commit hook can also run lint-staged with Prettier and ESLint over staged files, static analysis with OWASP rules and Gitleaks secret detection, in parallel, before the commit is confirmed.
- Because hooks can be skipped, the workflow has GitHub Actions re-validate every commit in the pull request against the same convention once the developer pushes and opens the PR.
- The stated payoff is machine-readable history: Changesets and semantic-release derive major, minor and patch versions from the commit types instead of a hand-picked bump.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint The enforceable copy of the commit rule has to sit in CI, so the team configures one convention in two places and spends runner time on a check that already passed on a laptop.
- exposure Secret scanning that lives only in a pre-commit hook protects the history for developers who run the hook; keeping credentials out for everyone means duplicating Gitleaks in the workflow.
- decision Once semantic-release derives version bumps from commit types, picking feat over fix is a release decision taken in the commit message by whoever writes it.
Two controls arrive in one command, by different routes. `git commit -S` signs the object with the developer's SSH or ED25519 key, which is what establishes that he wrote it [2]. The message check runs only because Husky wired commitlint into the commit-msg hook [3]. The post notes that local hooks are bypassable with `--no-verify` [6], and the bypass takes out the hook; the signature came from a flag on the same command, so skipping hooks does not unsign anything [14].
So the same rule runs again on the runner. The post's own framing is that the hooks are the first fast filter and CI is the real guarantee [6]. Every commit that reaches a pull request therefore has its message parsed twice, once on the laptop and once in Actions [15].
Take the timing figures in the post as characterisations, offered without a published measurement. A local hook runs in milliseconds on the developer's machine and consumes no CI resources [4]. The same trivial formatting failure, caught only in CI, could cost several minutes of feedback loop and block integration until the developer fixes it [7]. For those minutes to transfer to your repository, two things have to hold: queue and checkout time have to dominate the check itself, and the tree has to be large enough that linting everything costs materially more than linting the staged files, which is the scoping the post recommends [8].
What pays for the friction sits downstream of the commit. With the convention in place, `git log --oneline` stops being noise and becomes a changelog, each entry saying what changed and, with a scope, where [10]. Changesets and semantic-release then derive major, minor and patch bumps from the commit types, and without the convention that bump is manual and error-prone [11]. In my view the release derivation is the reason to accept a hook that can abort a commit over a missing colon; if nothing downstream consumes the types, the rule only enforces tidiness.
The published post breaks off mid-sentence while describing `git bisect` across `fix(...)` and `feat(...)` commits [13], so the rest of its CI check list is not in the record. The rule it does state for placement is that each validation runs at the cheapest layer that can detect its failure [12].
What to watch
- Whether a later post in the series shows a CI job verifying commit signatures, not just commit message format.
- Whether the Gitleaks and OWASP SAST scans that currently sit in the pre-commit hook also get a job in the GitHub Actions workflow.
- Whether the hook and the CI job share one commitlint configuration file or two that can diverge.