Skip to content

ci: automate releases via Conventional Commits + develop/main (#4829) - #4848

Closed
TomOnTime wants to merge 3 commits into
mainfrom
tlim_auto_release
Closed

TomOnTime wants to merge 3 commits into
mainfrom
tlim_auto_release

Conversation

@TomOnTime

@TomOnTime TomOnTime commented Sep 4, 2026 •

Copy link
Copy Markdown
Collaborator

Implements the release-automation design from #4829. Draft — needs review, the one-time repo settings below, and a live dry-run before it goes live.

Model

  • Contributor PRs squash-merge into develop with Conventional-Commit titles (enforced).
  • A human opens + merges develop → main as a merge commit to cut a release (this is the only manual step).
  • The push to main triggers full automation: svu computes the next semver → tag → GoReleaser publishes.

What's here

  • pr_conventional_title.yml — enforces Conventional-Commit PR titles on PRs into develop (amannn/action-semantic-pull-request@v6). Types that release: feat→minor, fix→patch, !/BREAKING→major. chore/ci/docs/…→no release.
  • release_gate.yml — runs the full longtest suite as a required check on develop → main PRs; a skip-longtest label is an auditable emergency bypass (single always-running release-gate check so branch protection never sticks on a skipped job).
  • release_on_merge.yml — on push to main: svu → tag → GoReleaser. Skips entirely if nothing releasable landed. Gated behind the AUTO_RELEASE_ENABLED repo variable so this PR can merge dormant.
  • .goreleaser.yml — draft: false (auto-publish), and the hand-maintained provider-name regexp is replaced by a permanent p/<name> scope rule (fix(p/route53): …) → no per-provider list to maintain ever again.
  • release-engineering.md — rewritten around the new flow; manual dispatch/tag escape hatch and all Tips preserved.

Provider changelog: how the giant regexp went away

Provider changes use the p/<name> scope. The changelog group is now a permanent one-liner. Because the type stays feat/fix, svu and the title-linter need zero special knowledge. I verified GoReleaser's routing end-to-end (matching is by list order, display by order):

### Breaking changes:        fix(p/route53)!, feat!
### New features:            feat, feat(core)
### Provider-specific:       feat(p/route53), fix(p/bind), perf(p/bind)
### Bug fixes:               fix, fix(spf)
### Documentation:           docs, docs(p/route53)
### CI/CD:                    build, ci
### Dependencies:            chore(deps)
### Deprecation warnings:    chore: deprecate ...
### Other:                    refactor, chore

Validation done

  • svu bump semantics probed on a scratch repo (chore/ci/docs → no bump; fix→patch; feat→minor; !→major).
  • GoReleaser changelog routing tested end-to-end on a scratch repo (table above).
  • goreleaser check passes on the real .goreleaser.yml; all workflows pass actionlint.

⚠️ Not yet validated / needs a live dry-run

The workflows only run in GHA. Before enabling: cut a throwaway -rc (or run on a fork) to confirm release_on_merge tags + GoReleaser publishes correctly.

One-time rollout (settings + order)

  1. Create develop from main; point contributions at it.
  2. Repo settings: squash-merge uses PR title; enable merge commits (for develop→main) and squash (for feature PRs); create the skip-longtest label.
  3. Branch protection: require release-gate on main; require PR: Conventional title on develop.
  4. Merge this PR (lands dormant — AUTO_RELEASE_ENABLED unset).
  5. When ready, set repo variable AUTO_RELEASE_ENABLED=true and do a controlled first develop→main release.

Note: the existing release_draft.yml (dispatch / tag push) stays as the manual escape hatch. There's some GoReleaser-step overlap between it and release_on_merge.yml; happy to DRY it into a workflow_call reusable in a follow-up once this is proven.

🤖 Generated with Claude Code


Update: one-command releases + auto-merge

  • bin/auto_release.sh opens/reuses the develop → main PR and enables GitHub auto-merge (as a merge commit, never squash). Once the release-gate passes, GitHub merges automatically and the release publishes — the only human action is running the script. Flags: -y, -w (watch), --skip-longtest (emergency).
  • No Release vX.Y.Z commit is needed: the version is computed by svu and lives only in the tag; the merge commit is filtered out of the changelog.
  • Extra one-time setting: enable "Allow auto-merge"; for zero clicks, main protection should require release-gate but not a review approval.

TomOnTime and others added 3 commits September 4, 2026 12:56
Implements the release-automation design discussed in #4829.

Model: contributor PRs squash-merge into `develop` with Conventional-Commit
titles; a human opens+merges `develop -> main` (merge commit) to cut a
release; the merge to `main` triggers full automation (svu computes the
version, tags it, GoReleaser publishes).

Added:
- .github/workflows/pr_conventional_title.yml: enforce Conventional-Commit
  PR titles on PRs into `develop` (amannn/action-semantic-pull-request).
- .github/workflows/release_gate.yml: run the full longtest integration
  suite as a required check on `develop -> main` PRs, with a `skip-longtest`
  label as an auditable emergency bypass.
- .github/workflows/release_on_merge.yml: on push to `main`, svu computes the
  next semver (feat->minor, fix->patch, `!`->major; chore/ci/docs->no
  release), tags it, and runs GoReleaser. Gated behind the
  AUTO_RELEASE_ENABLED repo variable so it lands dormant.

Changed:
- .goreleaser.yml: publish directly (draft:false); replace the hand-maintained
  provider-name regexp in the changelog with a permanent `p/<name>` scope
  rule, so adding a provider needs no changelog edits. Groups tightened to
  Conventional-Commit types (verified end-to-end with `goreleaser`).
- documentation/release/release-engineering.md: rewritten around the new flow;
  the manual dispatch/tag escape hatch and all Tips are preserved.

The existing release_draft.yml (workflow_dispatch / tag push) is kept as the
manual escape hatch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Refine the release flow so cutting a release is a single command and the
merge itself is automatic once tests pass.

- bin/auto_release.sh: opens (or reuses) the develop->main PR and enables
  GitHub auto-merge with a MERGE COMMIT (never squash). When the release gate
  (full longtest) passes, GitHub merges automatically and the release
  publishes. Flags: -y (no prompt), -w (watch checks), --skip-longtest
  (emergency bypass). Warns if AUTO_RELEASE_ENABLED is not set.
- docs: the "how to cut a release" step is now `bin/auto_release.sh`; note
  that auto-merge must use a merge commit and that "Allow auto-merge" must be
  enabled. Clarify that there is no "Release vX.Y.Z" commit -- the version
  lives only in the tag.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clarify that main protection requires one approval, so cutting a release is
two human actions (run bin/auto_release.sh, then a *different* maintainer
approves the PR); the gate + approval let GitHub auto-merge, and the rest is
automatic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cafferata

Copy link
Copy Markdown
Member

@TomOnTime FYI: #4899 updates the contributor docs for the Conventional Commits titles (CONTRIBUTING.md, the pull request template, the requested provider guide and the GitHub Actions overview). I left documentation/release/release-engineering.md out because this pull request rewrites it. Together they cover the list in #4266.

One thing I noticed: this pull request adds .github/workflows/pr_conventional_title.yml, while .github/workflows/pr_commitlint.yml from #4873 already lints the pull request title against commitlint.config.js. Is the plan to replace pr_commitlint.yml, or to run both? If it replaces it, I'll update the check name in documentation/developer-info/github-actions.md and CONTRIBUTING.md (#4899 now refers to PR: Commitlint).

@TomOnTime

Copy link
Copy Markdown
Collaborator Author

FYI: I've decided that the develop -> main split adds complexity without much benefit. See #4829 (comment)

As far as .github/workflows/pr_conventional_title.yml, we only need to check the title once. Remove pr_commitlint.yml

I'm not sure what release process I want at this time.

TomOnTime added a commit that referenced this pull request Sep 19, 2026
Since #4873 the `PR:
Commitlint` check requires Conventional Commits pull request titles, but
the contributor documentation still describes the old uppercase
prefixes. A contributor who follows `CONTRIBUTING.md`, the pull request
template or the requested provider guide ends up with a title like
`DOCS: Added requested provider ...`, which the check rejects.

This updates those places:

- `CONTRIBUTING.md` replaces the `FEATURE:`/`BUG:`/`PROVIDERNAME:` table
with the types from `commitlint.config.js`, the `p/PROVIDERNAME` scope,
the lowercase subject rule and a local `npx commitlint` example.
- The pull request template uses Conventional Commits examples.
- `documentation/developer-info/provider-request.md` uses a `docs: add
requested provider ...` commit, so the auto-filled title passes the
check.
- `documentation/developer-info/github-actions.md` lists the `PR:
Commitlint` workflow next to the other pull request checks.

`documentation/release/release-engineering.md` still has the `CHORE:`
commands and the all-caps provider rule for release notes. I left that
file out on purpose, because
#4848 rewrites it. Once
that one lands, #4266 can
be closed.

I checked every example title in the changed files with `npx commitlint`
(all pass), and confirmed that `fix(p/ROUTE53): Fix TTL` and `DOCS:
Fixed table` fail, as the new text says.

Refs #4266

## AI-attributie

Assisted-by: Claude Code:claude-opus-5

---------

Co-authored-by: Tom Limoncelli <tal@whatexit.org>
@cafferata

Copy link
Copy Markdown
Member

@TomOnTime agreed, one title check is enough. Before removing pr_commitlint.yml, note that the two checks aren't interchangeable yet:

  • pr_conventional_title.yml in this pull request only runs when the base branch is develop (if: github.base_ref == 'develop'), so without develop it wouldn't check pull requests to main at all.
  • It only checks the type. The p/PROVIDER scope rule and the lowercase subject from commitlint.config.js aren't covered, and the type list differs (revert instead of style and BREAKING CHANGE).
  • The contributor docs from docs: describe the conventional commits pull request titles #4899 describe the PR: Commitlint check and npx commitlint for local checks, and package.json has the commitlint dev dependencies (chore(deps): update commitlint to v21 #4900).

Which one would you like to keep? If it's pr_commitlint.yml, this pull request can drop pr_conventional_title.yml. If it's pr_conventional_title.yml, I'll update the docs and package.json once it runs on main.

@TomOnTime

Copy link
Copy Markdown
Collaborator Author

Closing this PR. Until I know what I want, it doesn't make sense to leave this open.

@TomOnTime TomOnTime closed this Sep 19, 2026
@TomOnTime
TomOnTime deleted the tlim_auto_release branch October 14, 2026 18:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants