Skip to content

ci: add release-please changelog and release notes generation - #13

Merged
lloydsk merged 1 commit into
mainfrom
ci/release-please-changelog
Sep 21, 2026
Merged

lloydsk merged 1 commit into
mainfrom
ci/release-please-changelog

Conversation

@lloydsk

@lloydsk lloydsk commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

Adds release-please so version bumps, CHANGELOG.md, and GitHub Release notes derive from Conventional Commits — plus a check so a PR title release-please cannot parse fails loudly instead of silently dropping a changelog entry.

What's here

  • release-please.yml — keeps one Release PR open. Merging it tags the commit, creates the Release with generated notes, and fires release.yml to publish to npm.
  • pr-title.yml + scripts/check-conventional-commit.ts — enforces Conventional Commits on the message that actually reaches main.
  • release-please-config.json / .release-please-manifest.json — release type, tag format, changelog sections, last released version.
  • docs/releasing.md — the process and the settings.

Deliberately NOT touched

release.yml is unchanged. A PAT-created Release carries a user identity, so its existing release: published trigger fires normally and npm publishes over OIDC exactly as before.

Under GITHUB_TOKEN that event is suppressed, and working around it would have meant chaining release.yml as a reusable workflow — plus registering release-please.yml as a second npm trusted publisher on both packages, because npm validates a workflow_call publish against the calling workflow's filename. One secret replaces that entire apparatus, so npm trusted publishing needs no changes.

Two design points worth reviewing

The type vocabulary is derived, not duplicated. The checker reads valid types from release-please-config.json's changelog-sections, so adding a type there immediately permits it in a title. release-please's schema is additionalProperties: false, so a custom key wasn't an option. It fails closed on an unusable config.

include-component-in-tag: false is required. The manifest default searches for pi-background-run-vX.Y.Z, which matches none of this repo's vX.Y.Z tags — the run fails rather than guessing.

One secret required

RELEASE_PLEASE_TOKEN — a fine-grained PAT scoped to this repo with Contents: Read and write + Pull requests: Read and write.

main requires lint-test and enforce_admins is true, so there is no bypass. GITHUB_TOKEN-created events start no workflow run, so a GITHUB_TOKEN Release PR can never report lint-test and can never be merged. Discovered via a rejected direct push (GH006: … Required status check "lint-test" is expected), not assumed.

The workflow falls back to GITHUB_TOKEN if the secret is unset, so it degrades to "Release PRs open but cannot be merged" rather than failing outright. A PAT expires, so it needs periodic rotation — a GitHub App token would avoid that if the org is willing to own one.

Also worth fixing (outside the repo)

Merge settings are wrong for this design — squash_merge_commit_title is COMMIT_OR_PR_TITLE, which means the commit's title if only one commit, so a single-commit PR squashes to the branch commit's message and pr-title.yml never sees it. allow_merge_commit and allow_rebase_merge are also both still true. gh api -X PATCH one-liner in docs/releasing.md.

Verification

  • Checker: 20/20 cases — valid shapes (feat:, fix(ci):, feat!:, feat(scope)!:), squash suffixes, multi-line bodies, and 10 rejections (Feat:, feat:no space, feat:, feat():, fix (ci):, unknown type, empty). Includes the real repo commit Address review findings; bound bggrep matching (#11), which it blocks.
  • Fail-closed: 7/7 — missing/non-JSON/empty/sections-without-type configs all exit non-zero, plus both stdin paths.
  • actionlint clean on all workflows; tsc --noEmit clean (now covers scripts/); npm pack still 7 files, so ci.yml's allowlist assertion is untouched.
  • release.yml verified byte-identical to main (git diff main -- .github/workflows/release.yml is empty).

@github-actions

github-actions Bot commented Sep 21, 2026

Copy link
Copy Markdown

CI report

Check Result
tsc --noEmit success
tests success
npm pack --dry-run success (7 files in tarball)

Ref: 253fb546cbaee893dbd85ac5e74e21e5c82fd3be

@lloydsk
lloydsk force-pushed the ci/release-please-changelog branch from 8d8e916 to 702b35e Compare September 21, 2026 00:40
Version bumps, CHANGELOG.md, and GitHub Release notes now come from
release-please, driven by Conventional Commits. A new check rejects a PR title
release-please cannot parse, so a change that would be dropped from the changelog
fails loudly instead of vanishing.

release.yml is deliberately untouched. A PAT-created Release carries a user
identity, so its existing `release: published` trigger fires and npm publishes
over OIDC exactly as before. Under GITHUB_TOKEN that event is suppressed, and
avoiding it would have meant chaining release.yml as a reusable workflow — plus
registering release-please.yml as a second npm trusted publisher on both packages,
since npm validates a workflow_call publish against the CALLING workflow's
filename. One secret replaces all of that machinery.

pr-title.yml enforces the message that actually reaches main.
scripts/check-conventional-commit.ts takes its type vocabulary from
release-please-config.json's changelog-sections, so there is one source of truth
rather than two lists that drift, and it fails closed on an unusable config.

`include-component-in-tag: false` is required: the manifest default searches for
pi-background-run-vX.Y.Z, which matches none of this repo's vX.Y.Z tags.
`bump-minor-pre-major` keeps a breaking change from ending pre-release status by
accident.

Requires one repository secret, RELEASE_PLEASE_TOKEN. `main` requires the
`lint-test` check and enforce_admins is true, so there is no bypass, and
GITHUB_TOKEN-created events start no workflow run — a GITHUB_TOKEN Release PR can
never report lint-test and so can never be merged. The workflow falls back to
GITHUB_TOKEN until the secret exists, so Release PRs still open meanwhile. Scope
the PAT to this repository: contents: write + pull_requests: write.

docs/releasing.md also records the merge settings, which are currently wrong for
this design: squash_merge_commit_title is COMMIT_OR_PR_TITLE, so a single-commit PR
squashes to the branch commit's title rather than the PR title — which pr-title.yml
never sees.
@lloydsk
lloydsk force-pushed the ci/release-please-changelog branch from 702b35e to 253fb54 Compare September 21, 2026 01:03
@lloydsk
lloydsk merged commit e018d39 into main Sep 21, 2026
3 checks passed
@lloydsk
lloydsk deleted the ci/release-please-changelog branch September 21, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant