Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# The gates of AGENTS.md and the commit check of CONTRIBUTING, on every push
# and pull request; on a push to main that passes both, the release.
# The gates of AGENTS.md and the commit check of CONTRIBUTING, on every pull
# request and push to main; on a push to main that passes both, the release.
name: ci

on:
push:
branches:
- main
pull_request:
# edited: a new title is a new merge subject, so it is checked again.
types: [opened, synchronize, reopened, edited]

permissions:
contents: read
Expand Down Expand Up @@ -58,6 +62,22 @@ jobs:
uses: cocogitto/cocogitto-action@v4.2.0
with:
install-only: true
- name: Give cog the author identity cog verify reads
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Check the pull request title as the merge subject
if: github.event_name == 'pull_request'
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
set -eu
printf '%s\n' "$PR_TITLE" > "$RUNNER_TEMP/merge-subject"
grep -Eq '^merge\([a-z0-9-]+\): ' "$RUNNER_TEMP/merge-subject" || {
echo "::error::the title becomes the merge commit's subject: merge(<scope>): <what lands and why>"
exit 1
}
cog verify --file "$RUNNER_TEMP/merge-subject"
- name: Check the pushed range
env:
EVENT: ${{ github.event_name }}
Expand Down
11 changes: 7 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ The highest bump across the batch wins. The core stays at `0.y.z` until the eval

## Branches and integration

- `main` is always releasable. Work happens on a topic branch named `<type>/<topic>`, for example `feat/outcome-record`.
- Integrate by rebasing the branch onto `main`, then merging with `--no-ff`. The merge commit is `merge(<scope>): <what landed and why>`, so the merge itself records the reason.
- Remove the branch and its worktree once it has landed.
- `main` is always releasable and changes only through pull requests. Work happens on a topic branch named `<type>/<topic>`, for example `feat/outcome-record`.
- Push the branch and open a pull request against `main`. Its title becomes the merge commit's subject, `merge(<scope>): <what lands and why>`, so the merge itself records the reason. Its body becomes the merge commit's body; an agent-authored one ends with the `Co-Authored-By:` trailer.
- CI runs the gates and the commit check on the pull request. Before merging, rebase the branch onto `main` (`git rebase origin/main`, then `git push --force-with-lease`).
- Merge with a merge commit; the repository refuses squash and rebase merges. The merge is a push to `main`, which runs CI again and then the release (see [Enforcement](#enforcement)).
- GitHub deletes the merged branch. Remove the local branch and its worktree, then pull `main`, which may also carry the release job's `chore(release)` commit.

## Tools

Expand All @@ -113,7 +115,8 @@ Git does not copy hooks on clone, so `cog install-hook --all` is the per-clone s
| Check | Runs | Rejects |
|---|---|---|
| `commit-msg` hook | On every local commit, once `cog install-hook --all` has been run | A subject without a `type(scope):` prefix, then, through `cog verify`, a type or scope outside the tables above |
| `cog check <base>..<head>` | In CI (`.github/workflows/ci.yml`), on the range each push or pull request adds (run it by hand as `cog check main..HEAD`) | The same, for every commit in the range |
| `cog check <base>..<head>` | In CI (`.github/workflows/ci.yml`), on the range each pull request or push to `main` adds (run it by hand as `cog check main..HEAD`) | The same, for every commit in the range |
| Pull request title | In CI, on every pull request, as the subject of the merge commit it becomes | A title that is not `merge(<scope>): …` with a scope from the tables above |
| `release-plz` | In CI (`.github/workflows/ci.yml`), on a push to `main` once the gates and the commit check pass: `release-plz update` commits the new versions and changelogs as `chore(release): … [skip ci]`, then `release-plz release` tags each new version and creates its GitHub release | Nothing; a push with no release-triggering commit releases nothing, and nothing goes to crates.io |

The types and scopes live in `cog.toml` and equal the tables in this file; change both together. `cog` itself does not require a scope, which is why the hook checks the prefix first. A checkout without `cog.toml` passes the hook, so a branch cut before the file landed can still commit.
Expand Down
Loading