feat(release): automate release via PR model - #260
Merged
Conversation
Replaces manual tag-push releases with a fully automated release PR workflow. On every push to main (and daily at 06:00 UTC), a release PR is created or updated reflecting the next CalVer version and a git-cliff-generated changelog. Merging the PR creates the tag and GitHub Release, which in turn triggers the existing web and Android deployment workflows unchanged. New files: - cliff.toml: git-cliff config matching the vYYYY.M.N tag pattern; includes feat/fix/perf/refactor/docs, skips chore/ci/build/style/test - .github/workflows/release-pr.yml: computes next version, generates changelog, updates pubspec.yaml + CHANGELOG.md, opens/updates PR - .github/workflows/release.yml: on release/next merge, tags and creates GitHub Release Updated: - .github/workflows/pr-lint.yml: skip semantic title check for release/next PRs (title "Release X.Y.Z" is intentionally non-CC) - docs/processes/release.md: rewritten to describe the automated flow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ting Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR migrates the project’s release process from manual tag pushes to an automated “release PR” flow, where CI generates/updates a release/next PR with version + changelog updates and merging it triggers tagging and releasing.
Changes:
- Add
release-pr.ymlto compute the next CalVer, generate release notes via git-cliff, updatepubspec.yaml+CHANGELOG.md, and open/update arelease/next→mainPR. - Add
release.ymlto tag and create a GitHub Release when therelease/nextPR is merged. - Update PR linting and documentation to reflect/allow the new release PR model.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/processes/release.md | Updates release documentation to the automated release PR flow. |
| cliff.toml | Adds git-cliff configuration for generating release notes from conventional commits. |
| .github/workflows/release-pr.yml | Automates version calculation, changelog generation, and release PR creation/update. |
| .github/workflows/release.yml | Tags the merged release PR and creates a GitHub Release. |
| .github/workflows/pr-lint.yml | Skips semantic PR title linting for the release/next PR. |
- cliff.toml: set header="" to prevent duplicate '# Changelog' header being prepended on every release; CHANGELOG.md is now initialised with the header once on first run - release-pr.yml: initialise CHANGELOG.md with header when absent so the header appears exactly once across all releases - release.yml: make tag creation idempotent (skip if tag exists) to survive workflow re-runs without a git error - release.yml: remove GitHub Release creation — release-android.yml already creates a release for the same tag with assets attached; a second creation would conflict or overwrite its content Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
release.yml creates the GitHub Release with the CHANGELOG.md body immediately after pushing the tag. release-android.yml now appends its Android-specific section (APK/AAB install notes, package info) and attaches artifacts to the existing release rather than creating a competing release for the same tag. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- release-pr.yml: fix grep -c bug — exits with code 1 on zero matches causing '$(grep -c ... || echo 0)' to produce '0\n0' (non-integer); replaced with grep -q which sets has_content cleanly - release-pr.yml: fix CHANGELOG.md header ordering — previous logic prepended release-notes.md before the '# Changelog' header it had just created; simplified to touch + prepend with no embedded header (CHANGELOG.md starts directly with '## [version]' sections) - release-web.yml, release-android.yml: switch trigger from 'push: tags: v*' to 'release: types: [published]' so they only start after release.yml has created the GitHub Release, eliminating the race where android could create the release before release.yml - release.yml: check out merge_commit_sha rather than the default merge ref so the tag points to the exact commit that landed on main Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix publish-google-play job condition: was checking for push+tag event but workflow now triggers on release:published, so the condition never matched and Play uploads were silently skipped - Add paths-ignore for pubspec.yaml/CHANGELOG.md to release-pr.yml so merging release/next doesn't race with release.yml and regenerate a duplicate release PR - Update docs to reflect that deployment workflows fire on release:published not on the tag push https://claude.ai/code/session_01JDVdCKNUcJfnKCaZZipMWW
- release.yml: extract only the top CHANGELOG section for the GitHub Release body rather than the full accumulated file, which would grow with every release - release-pr.yml: replace fragile '^- ' bullet check with '^## \[' version-section check so has_content is robust to template changes - docs: document GITHUB_TOKEN CI limitation and build-date staleness as accepted trade-offs https://claude.ai/code/session_01JDVdCKNUcJfnKCaZZipMWW
…output Prevents a blank GitHub Release body going unnoticed if CHANGELOG.md is missing or contains no ## [ version section. https://claude.ai/code/session_01JDVdCKNUcJfnKCaZZipMWW
…limitation GitHub Actions does not fire workflow runs in response to events created by GITHUB_TOKEN. The release: published trigger on release-web.yml and release-android.yml therefore never fired in the automated path — web and Android deploys were silently skipped on every release. Fix: release.yml explicitly triggers both deploy workflows via gh workflow run (workflow_dispatch) after creating the GitHub Release. GITHUB_TOKEN can invoke workflow_dispatch directly, so no PAT is needed. Also: - Add upload_to_play boolean input to release-android.yml so automated releases (upload_to_play=true) can be distinguished from manual test builds (default false); replaces the stale github.event_name == 'release' condition that could never be true - Remove release: published triggers from both deploy workflows; they are now workflow_dispatch-only - Simplify dead if/else version extraction branches in both deploy workflows - Make tag push fully idempotent: check remote existence before pushing so re-runs don't fail on non-fast-forward tag errors - Add actions: write permission to release.yml for gh workflow run https://claude.ai/code/session_01JDVdCKNUcJfnKCaZZipMWW
…ploy trigger - Replace outdated 'release: published' trigger description with the actual mechanism (release.yml calls gh workflow run after creating the GitHub Release) - Add explanation of why workflow_dispatch is used instead of release: published (GITHUB_TOKEN cascade limitation) - Note that deploy workflows can be triggered manually from the Actions tab for hotfix deployments https://claude.ai/code/session_01JDVdCKNUcJfnKCaZZipMWW
- release-android.yml: always run tests regardless of upload_to_play; using that flag to skip tests conflated two unrelated concerns and would silently skip tests for manual Play uploads - release.yml: when the remote tag already exists, verify it points at the expected merge commit SHA and fail loudly if not, preventing a re-run from silently deploying code from a stale tag - release-pr.yml: document the paths-ignore side effect so future maintainers understand why pubspec.yaml-only pushes don't immediately trigger a release PR refresh https://claude.ai/code/session_01JDVdCKNUcJfnKCaZZipMWW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
main(and daily cron at 06:00 UTC),release-pr.ymlcomputes the next CalVer version, generates a changelog via git-cliff, and opens/updates arelease/next→mainPRrelease.yml, which creates the git tag and GitHub Release — the existingrelease-web.ymlandrelease-android.ymlthen fire from the tag push, unchangedNew files
cliff.tomlvYYYY.M.Ntags; includes feat/fix/perf/refactor/docs; skips chore/ci/build/style/test.github/workflows/release-pr.ymlpubspec.yaml+CHANGELOG.md, opens/updatesrelease/nextPR.github/workflows/release.ymlrelease/next: pushes tag, creates GitHub ReleaseChanged files
.github/workflows/pr-lint.ymlrelease/nextPRs — titleRelease X.Y.Zis intentionally not a conventional commitdocs/processes/release.mdgit tag && git pushinstructionsReviewer notes
0per month (matching existing tag history:v2026.5.0…v2026.5.4)release-pr.ymlis idempotent: re-runs always reflect the latestmainand today's date; skipped entirely if no releasable commits exist since the last tagrelease/nextbranch is long-lived and force-updated each run —delete-branch: falseis set on the create-pull-request actionrelease-web.ymlorrelease-android.yml— they continue to trigger onv*tag pushes as beforeTest plan
release-pr.ymlruns without error on the next push tomainrelease/nextPR is created with the correct next version and changelog bodyrelease.ymlcreates the correct tag and GitHub Releaserelease-web.ymlandrelease-android.ymlfire from the tag push🤖 Generated with Claude Code