From 6c5c978edbb611c9bc5a4163421e13543398a573 Mon Sep 17 00:00:00 2001 From: Jack Platten Date: Wed, 22 Jul 2026 22:29:25 -0700 Subject: [PATCH] ci: switch PR merges to merge-commit, lint every commit Squash-merge gives every merged PR a new commit hash on main, so the original local commits can never become ancestors of trunk even after the PR lands. That was the root cause of a jj dangling-fork backlog cleaned up manually earlier. Move to a real merge commit instead. Since the merge commit keeps GitHub's default (non-conventional) message, release-please skips it and reads each PR commit directly off main, so the PR title no longer needs a format. Replace the pr-title check with commit-lint (wagoid/commitlint-github-action), which enforces Conventional Commits on every commit in the PR instead of just the title. Update CONTRIBUTING.md, CLAUDE.md, AGENTS.md, and the PR template to describe the new flow, and add revert to the documented allowed types to match commitlint's default config. --- .github/PULL_REQUEST_TEMPLATE.md | 5 +++-- .github/workflows/CI.yml | 11 +++++------ AGENTS.md | 9 +++++---- CLAUDE.md | 2 +- CONTRIBUTING.md | 32 ++++++++++++++++++++------------ 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 39119b2..d56b7b8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,6 +1,7 @@ - - + + + #### Description diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index cf50680..a48eec3 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -59,11 +59,10 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - pr-title: + commit-lint: runs-on: ubuntu-latest - permissions: - pull-requests: read steps: - - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + fetch-depth: 0 + - uses: wagoid/commitlint-github-action@6cf16efdf4da5277c791d335142c03a0bdf1766e # v6.2.1 diff --git a/AGENTS.md b/AGENTS.md index d390486..133937f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,8 @@ Do not open pull requests against `IlanCosman/tide` (upstream) for work done in For testing, use `mise` rather than `make`. -PR titles must follow [Conventional Commits](https://www.conventionalcommits.org/) -(e.g. `fix: ...`, `feat: ...`) — see `CONTRIBUTING.md`. Since PRs are -squash-merged, the title becomes the commit message that release-please -reads to generate the changelog. +Every commit must follow [Conventional Commits](https://www.conventionalcommits.org/) +(e.g. `fix: ...`, `feat: ...`) — see `CONTRIBUTING.md`. PRs merge via a real +merge commit, not squash, so release-please reads each commit on `main` +directly to generate the changelog; the PR title itself has no format +requirement. diff --git a/CLAUDE.md b/CLAUDE.md index 59b792f..e20893a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -82,7 +82,7 @@ The git backend minimizes subprocess spawns (currently 3 git invocations) since - Style: prefer `test` over `[ ]`; prefer `&&`/`||` over `and`/`or` for simple conditionals, `if`/`else`/`else if` for anything more complex; prefer piping over command substitution when it doesn't require extra commands. - Naming: everything `snake_case`. User-facing names (variables, functions, files) are prefixed `tide_`; internal-only names are prefixed `_tide_`. Items live in `_tide_item_.fish`; subcommands in `_tide_sub_.fish`. -- PR titles must follow [Conventional Commits][conventional-commits] (`[(scope)][!]: `) — PRs are squash-merged, so the PR title becomes the commit message on `main`, and [release-please][release-please] reads it to generate the changelog. This is CI-enforced. +- PRs merge via a real merge commit (not squash), so every commit — not the PR title — must follow [Conventional Commits][conventional-commits] (`[(scope)][!]: `); [release-please][release-please] reads commits directly off `main` to generate the changelog. This is CI-enforced per-commit. - Releases are fully automated by release-please; don't hand-edit `functions/tide.fish`'s version string or the floating `vN`/`vN.M` tags. [clownfish]: https://github.com/IlanCosman/clownfish diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f1c7fac..966037e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,21 +19,26 @@ If you have any questions that aren't addressed in this document, please don't h ### Pull Requests -PR titles must follow [Conventional Commits][]: `[(scope)][!]: `, -e.g. `fix: correct jj branch color` or `feat(vcs)!: unify git/jj into a single item`. - -- Allowed types: `feat`, `fix`, `build`, `chore`, `ci`, `docs`, `style`, `refactor`, `perf`, `test`. +PRs are merged into `main` with a real merge commit, not squashed. Because of +that, every commit in the PR — not just the title — must follow +[Conventional Commits][]: `[(scope)][!]: `, e.g. +`fix: correct jj branch color` or `feat(vcs)!: unify git/jj into a single +item`. [release-please][] reads these commits directly off `main` to +generate the next release's changelog entry. A CI check (`commit-lint`) +enforces this on every commit in the PR. + +- Allowed types: `feat`, `fix`, `build`, `chore`, `ci`, `docs`, `style`, `refactor`, `perf`, `test`, `revert`. - Add `!` before the `:` (or a `BREAKING CHANGE:` footer) for breaking changes. -This isn't just style: since PRs are squash-merged, the title becomes the -commit message on `main`, which [release-please][] reads to generate the -next release's changelog entry. A CI check enforces this on every PR. +The PR title itself has no format requirement — write it as plain, +descriptive text for anyone skimming the PR list. Each pull request should only impact a single `(scope)`. In other words, -`fix: correct branch color` or `feat(foobar): introduce foobar item` are -acceptable, but `perf(items): improve both docker and kubectl` is not. +a PR with only `fix: correct branch color` commits or only +`feat(foobar): ...` commits is acceptable, but mixing `perf(docker): ...` and +`perf(kubectl): ...` commits in the same PR is not. -This is to ensure that the changelog generates cleanly off of PR titles. +This is to keep the changelog generating cleanly off `main`'s commit history. ### Naming Conventions @@ -111,8 +116,11 @@ All links should be in reference style, with references at the bottom of the doc Releases are automated by [release-please][]. It watches `main` and keeps an open "release PR" containing the next version bump and a generated -`CHANGELOG.md` entry, derived from Conventional Commit PR titles merged -since the last release. +`CHANGELOG.md` entry, derived from Conventional Commit messages on `main` +since the last release. Merge commits themselves carry GitHub's default +message ("Merge pull request #N from branch"), which never matches the +Conventional Commits pattern, so release-please skips them and reads only +the PR's individual commits. - [ ] Before merging the release PR, hand-edit its `CHANGELOG.md` diff to clean up the generated bullets into prose, and add any narrative -- knotmirror 0.0.1