Skip to content

ci(release): release-please owns version, CHANGELOG and tag; merging the release PR ships - #63

Merged
grimen merged 1 commit into
mainfrom
ci/release-please
Sep 6, 2026
Merged

grimen merged 1 commit into
mainfrom
ci/release-please

Conversation

@grimen

@grimen grimen commented Sep 5, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Stable releases are cut today with make release V=X.Y.Z (gh release create --generate-notes). The tag-is-the-version model from #24 stays; what changes is who decides the version and writes the notes. The v0.2.0 notes listed 26 PR titles in one bucket, 22 of them ci/docs/chore that never shipped in the packages, and the useful summary plus the bump (0.1.0 → 0.2.0) were written and chosen by hand.

release-please reads the Conventional Commit PR titles commitlint already enforces, infers the bump, keeps a grouped CHANGELOG.md, and opens one PR titled chore(release): X.Y.Z. Merging that PR (make release) is the whole release step. Nothing per-PR is added; ci/docs/chore commits never open a release PR.

What changes

  • New release-please-config.json + .release-please-manifest.json (root component, release-type: node, vX.Y.Z tags, pre-1.0 policy: feat → minor, breaking → minor until 1.0; PR title chore(release): ${version} because release is a commitlint scope and the default chore(main) is not; feat/fix/perf/revert visible in the changelog, everything else hidden).
  • New .github/workflows/release-please.yml: on push to main, updates the release PR; when the merged commit is the release PR, tags, creates the GitHub Release (body = changelog entry) and dispatches ci.yml at the tag with release_tag.
  • ci.yml / e2e.yml: workflow_dispatch gains release_tag; a dispatch carrying it is treated exactly like a release event (tag = version, latest, green-main gate). The release: trigger stays for hand-cut rc tags.
  • scripts/release/rerun-blocked.sh: also re-runs blocked dispatch-at-tag release runs.
  • Makefile: make release merges the open release PR; make release-rc V=X.Y.Z-rc.1 hand-cuts a prerelease tag (ships under next).
  • New CHANGELOG.md backfilled for 0.1.0 and 0.2.0 in release-please's format so the history is continuous; root package.json version 0.0.1 → 0.2.0 to match the manifest (release-please maintains it from here). The three publishable packages stay at 0.0.0-development.
  • New docs/releasing.md (walkthrough + worked example), and CONTRIBUTING / CLAUDE.md / AGENTS.md / consuming.md / development-guide / docs index updated. .github/release.yml removed (only --generate-notes read it).

Why the dispatch instead of the release: event

GitHub never triggers workflows from events created with the workflow's own GITHUB_TOKEN. A release created by release-please would not fire ci.yml's release: trigger, so release-please.yml runs gh workflow run ci.yml --ref vX.Y.Z -f release_tag=vX.Y.Z itself. This keeps the "no extra secrets" property ci.yml advertises; the alternative is a GitHub App token.

The same rule means CI does not run on the release PR itself. It only touches CHANGELOG.md, the manifest and the root package.json/package-lock.json; the tagged run executes the full pipeline plus the green-main gate before anything ships. The ruleset requires one approval and no status checks, so the PR is mergeable, and since the bot authors it, any maintainer can approve.

Example: the release PR release-please would have opened for 0.2.0

Written by hand in release-please's exact output format from the commits between v0.1.0 and v0.2.0 (the six fix/feat commits; fix(ci) / fix(e2e) would also appear under Bug Fixes, which is why CONTRIBUTING now says to use ci: for CI-only fixes). Also in docs/releasing.md.

Title: chore(release): 0.2.0 · Files: CHANGELOG.md, package.json, package-lock.json, .release-please-manifest.json

Body / release notes:

0.2.0 (2026-09-04)

Features

  • headless useESignature hook and ESignature theming for RN and web (#60) (a990939)

Bug Fixes

  • api: suppress false-positive CodeQL password-hash alert on JWT secret (b4496dd)
  • demo: regenerate Podfile.lock for react-native 0.86.3 (#51) (9249130)
  • resolve the CodeQL security-and-quality alerts (#55) (58adb33)

Compare with the current v0.2.0 notes: 26 flat lines, 22 of them CI.

To reproduce with the real tool against this branch (read-only, needs a token, which is why it is not pasted here as tool output):

GITHUB_TOKEN=$(gh auth token) npx release-please@17 release-pr --repo-url blinkbitcoin/esign --target-branch ci/release-please --dry-run

As-is it reports that no release is needed: nothing releasable has merged since v0.2.0, which is the correct answer. To see the 0.2.0 PR rendered by the tool, temporarily set the manifest to 0.1.0 and add "last-release-sha": "7c3bd08708c5e0643896e541d39d2eef22711acb" to the config on a scratch branch.

Verified locally

  • make check-ci (actionlint + shellcheck) clean; npm run format:check, make docs-check clean.
  • DRY_RUN=1 EVENT=release TAG=v0.2.0 bash scripts/release/resolve-version.sh → 0.2.0 / latest; push shape → 0.2.1-pre.<run>.<sha> / next. The script is unchanged; only how EVENT/TAG reach it.

Test plan (after merge)

  • Release Please runs on the merge commit and opens no PR (this commit is ci:). If it fails with "GitHub Actions is not permitted to create or approve pull requests", enable that in Settings → Actions → General (org setting first). I could not read that setting (403).
  • The next feat: / fix: merge opens chore(release): X.Y.Z with the grouped entry.
  • Approve + make release: tag vX.Y.Z and GitHub Release appear; a CI run starts at the tag with release_tag; Publish waits for the main run, ships X.Y.Z under latest; Verify green.
  • make release-rc V=X.Y.Z-rc.1 still ships under next via the release: event.

…the release PR ships

The v0.2.0 notes listed 26 PR titles in one bucket, 22 of them ci/docs/
chore that never shipped in the packages, and the useful summary plus the
bump (0.1.0 -> 0.2.0) were chosen by hand. release-please reads the
Conventional Commit titles commitlint already enforces, infers the bump,
keeps a grouped CHANGELOG.md, and opens one "chore(release): X.Y.Z" PR.
Merging that PR (`make release`) is the whole release step.

Everything downstream stays: the tag is the version, the three packages
keep 0.0.0-development and are stamped at build time, the green-main gate
and release-retry.yml still apply. Because a release created by the
workflow token never fires ci.yml's `release:` trigger, release-please.yml
dispatches ci.yml at the new tag with `release_tag`, which e2e.yml and the
Publish gate treat exactly like a release event. The `release:` trigger is
kept for hand-cut rc tags (`make release-rc V=X.Y.Z-rc.1`).

CHANGELOG.md is backfilled for 0.1.0 and 0.2.0 in release-please's format;
docs/releasing.md walks through the flow with the 0.2.0 release PR as the
worked example. .github/release.yml (auto-notes config) is removed; the
root package.json version now tracks the released version.

Claude-Session: https://claude.ai/code/session_013oNrMV7cjfetiYaX4uFRee
@blink-admin
blink-admin marked this pull request as ready for review September 6, 2026 16:12
@grimen
grimen merged commit 9e5084b into main Sep 6, 2026
17 checks passed
@grimen
grimen deleted the ci/release-please branch September 6, 2026 16:13
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.

2 participants