From 7ddbd6bdc6f6a1388cdff40e6231014ed4a3654f Mon Sep 17 00:00:00 2001 From: Gavin Inglis Date: Thu, 30 Jul 2026 17:39:28 +0000 Subject: [PATCH] ci: add commitlint workflow for PR commit messages Signed-off-by: Gavin Inglis --- .commitlint.config.mjs | 48 ++++++++++++++++++++++++++++++++ .github/workflows/commitlint.yml | 33 ++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 .commitlint.config.mjs create mode 100644 .github/workflows/commitlint.yml diff --git a/.commitlint.config.mjs b/.commitlint.config.mjs new file mode 100644 index 00000000..b8e9d0af --- /dev/null +++ b/.commitlint.config.mjs @@ -0,0 +1,48 @@ +/* [commitlint](https://github.com/conventional-changelog/commitlint) configuration + * + * Bottlerocket kit repos use a `scope: description` convention where the scope + * is typically a package name (e.g. `kernel-6.12: update to 6.12.94`) or an + * area (e.g. `changelog: add release notes for v7.1.0`). + * + * This differs from standard Conventional Commits which require a fixed type + * prefix (feat, fix, etc.). We enforce the structural rules (colon separator, + * line lengths, casing) while allowing any lowercase scope before the colon. + */ +import { RuleConfigSeverity } from "@commitlint/types"; + +// Custom plugin to validate the "scope: description" format used in kit repos. +const kitScopePlugin = { + rules: { + // Validates that the header matches `lowercase-scope: lowercase description` + "kit-scope-format": (parsed, _when, _value) => { + const header = parsed.header; + // Match: one or more lowercase words/numbers/dots/dashes, colon, space, then description + const pattern = /^[a-z][a-z0-9._-]*: .+$/; + return [ + pattern.test(header), + "header must match the format 'scope: description' (e.g. 'kernel-6.12: update to 6.12.94')", + ]; + }, + }, +}; + +export default { + plugins: [kitScopePlugin], + rules: { + // Structural rules + "header-max-length": [RuleConfigSeverity.Error, "always", 72], + "header-trim": [RuleConfigSeverity.Error, "always"], + "body-max-line-length": [RuleConfigSeverity.Error, "always", 72], + "body-leading-blank": [RuleConfigSeverity.Error, "always"], + + // Subject rules (applied to text after the colon) + "subject-full-stop": [RuleConfigSeverity.Error, "never", "."], + + // Custom kit scope format + "kit-scope-format": [RuleConfigSeverity.Error, "always"], + }, + ignores: [ + (message) => message.includes("Merge pull request #"), + (message) => message.startsWith("Revert \""), + ], +}; diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml new file mode 100644 index 00000000..cebf04fc --- /dev/null +++ b/.github/workflows/commitlint.yml @@ -0,0 +1,33 @@ +name: Lint Commit Messages + +on: + pull_request: + types: + - opened + - edited + - reopened + - synchronize + +jobs: + commitlint: + if: github.repository == 'bottlerocket-os/bottlerocket-kernel-kit' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Setup Node + uses: actions/setup-node@v4 + - name: Install commitlint + run: npm install @commitlint/cli @commitlint/types + - name: Lint commits in PR + env: + WORKSPACE: ${{ github.workspace }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + npx commitlint \ + -g "${WORKSPACE}/.commitlint.config.mjs" \ + --from "${BASE_SHA}" \ + --to "${HEAD_SHA}" \ + --verbose