From 62432b7a53595fa799288455189dbc9b18703362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Barras=20Hampel?= Date: Thu, 2 Jul 2026 15:56:06 +0200 Subject: [PATCH] ci: add commitlint + PR-lint gate for Linear-key discipline Conventional Commits + a Linear issue key required on release-note types (feat/fix/perf); maintenance types exempt. Feeds Linear releases as changie is retired (OPS-1541). Same gate as youdeploy-http-api #5981. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01FAv8ZjMKaAk7v2hF3Vf4j1 --- .github/workflows/pr-lint.yml | 48 +++++++++++++++++++ .omc/state/hud-stdin-cache.json | 1 + .../hud-state.json | 6 +++ commitlint.config.mjs | 37 ++++++++++++++ 4 files changed, 92 insertions(+) create mode 100644 .github/workflows/pr-lint.yml create mode 100644 .omc/state/hud-stdin-cache.json create mode 100644 .omc/state/sessions/69d8b816-f4fa-4732-852a-b55361c3b234/hud-state.json create mode 100644 commitlint.config.mjs diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml new file mode 100644 index 000000000..891526cd9 --- /dev/null +++ b/.github/workflows/pr-lint.yml @@ -0,0 +1,48 @@ +name: PR Lint + +# Commit convention gate feeding Linear releases (changie retirement). +# Validates PR commits AND the PR title (the PR title becomes the commit once we +# squash-merge). Conventional Commits + a Linear issue key on release-note types +# (see commitlint.config.js). No secrets, no self-hosted runner needed — runs on +# GitHub-hosted ubuntu-latest (free for this trust-safe lint). + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read + pull-requests: read + +concurrency: + group: pr-lint-${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + commitlint: + name: Commit convention + runs-on: ubuntu-latest + # Same-repo PRs only; skip dependency bots (they carry no Linear key and are + # exempt "Maintenance" changes). Fork PRs are skipped here (contributors have no + # Linear access); a maintainer re-runs from an internal branch if needed. + if: >- + github.event.pull_request.head.repo.full_name == github.repository && + github.actor != 'dependabot[bot]' && + github.actor != 'renovate[bot]' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Lint PR commits + uses: wagoid/commitlint-github-action@v6 + with: + configFile: commitlint.config.mjs + + - name: Lint PR title (becomes the squash commit) + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + printf '%s\n' "$PR_TITLE" | npx --yes \ + -p @commitlint/cli -p @commitlint/config-conventional \ + commitlint --config commitlint.config.mjs diff --git a/.omc/state/hud-stdin-cache.json b/.omc/state/hud-stdin-cache.json new file mode 100644 index 000000000..a11697609 --- /dev/null +++ b/.omc/state/hud-stdin-cache.json @@ -0,0 +1 @@ +{"session_id":"69d8b816-f4fa-4732-852a-b55361c3b234","transcript_path":"/home/stammfrei/.claude/projects/-home-stammfrei-projects-cy-legacy/69d8b816-f4fa-4732-852a-b55361c3b234.jsonl","cwd":"/home/stammfrei/projects/cy-legacy/wt-cycloid-cli-ops1541","prompt_id":"853511e5-bcc6-4a41-80ae-c238653aa434","effort":{"level":"high"},"session_name":"orchestrator","model":{"id":"claude-opus-4-8","display_name":"Opus 4.8"},"workspace":{"current_dir":"/home/stammfrei/projects/cy-legacy/wt-cycloid-cli-ops1541","project_dir":"/home/stammfrei/projects/cy-legacy","added_dirs":[],"git_worktree":"wt-cycloid-cli-ops1541"},"version":"2.1.198","output_style":{"name":"default"},"cost":{"total_cost_usd":301.84669425000055,"total_duration_ms":247097298,"total_api_duration_ms":24876351,"total_lines_added":5016,"total_lines_removed":1169},"context_window":{"total_input_tokens":551221,"total_output_tokens":488,"context_window_size":1000000,"current_usage":{"input_tokens":2,"output_tokens":488,"cache_creation_input_tokens":2249,"cache_read_input_tokens":548970},"used_percentage":55,"remaining_percentage":45},"exceeds_200k_tokens":true,"fast_mode":false,"thinking":{"enabled":true},"rate_limits":{"five_hour":{"used_percentage":21,"resets_at":1783016400},"seven_day":{"used_percentage":17,"resets_at":1783288800}},"vim":{"mode":"INSERT"}} \ No newline at end of file diff --git a/.omc/state/sessions/69d8b816-f4fa-4732-852a-b55361c3b234/hud-state.json b/.omc/state/sessions/69d8b816-f4fa-4732-852a-b55361c3b234/hud-state.json new file mode 100644 index 000000000..6ae6a2b51 --- /dev/null +++ b/.omc/state/sessions/69d8b816-f4fa-4732-852a-b55361c3b234/hud-state.json @@ -0,0 +1,6 @@ +{ + "timestamp": "2026-07-02T13:56:14.061Z", + "backgroundTasks": [], + "sessionStartTimestamp": "2026-07-01T09:18:11.998Z", + "sessionId": "69d8b816-f4fa-4732-852a-b55361c3b234" +} \ No newline at end of file diff --git a/commitlint.config.mjs b/commitlint.config.mjs new file mode 100644 index 000000000..926bf159d --- /dev/null +++ b/commitlint.config.mjs @@ -0,0 +1,37 @@ +// Cycloid commit convention — feeds Linear releases (changie is being retired). +// +// Conventional Commits + a Linear issue key on release-note-bearing commits. +// Release-note types (feat/fix/perf) MUST reference a Linear issue key (e.g. BE-123) +// in the subject or body, so the release pipeline can link the issue to the Linear +// release. Maintenance types (chore/build/ci/docs/style/test/refactor/revert) are +// exempt — they land in the "Maintenance" bucket and need no key. +// +// ESM (.mjs) is required by wagoid/commitlint-github-action@v6. +// NOTE: intended to be extracted into a shared @cycloid/commitlint-config package +// once the monorepo lands; kept inline per-repo for now. + +const RELEASE_NOTE_TYPES = ['feat', 'fix', 'perf']; +const LINEAR_KEY = /\b[A-Z]{2,}-\d+\b/; + +export default { + extends: ['@commitlint/config-conventional'], + plugins: [ + { + rules: { + 'linear-key-on-release-types': (parsed) => { + const { type, header, body } = parsed; + if (!type || !RELEASE_NOTE_TYPES.includes(type)) return [true]; + const hasKey = LINEAR_KEY.test(`${header || ''}\n${body || ''}`); + return [ + hasKey, + `a "${type}" commit must reference a Linear issue key (e.g. BE-123) ` + + `in the subject or body so it can be linked to the Linear release`, + ]; + }, + }, + }, + ], + rules: { + 'linear-key-on-release-types': [2, 'always'], + }, +};