Skip to content
Merged
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
50 changes: 43 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
name: Release

# Manually-triggered release workflow.
# Runs `npm run bump-version <version>` to update every manifest, commits
# the bump on `main`, tags it, pushes, and creates a GitHub release with
# auto-generated notes assembled from PRs since the previous tag.
# Runs `npm run bump-version <version>` to update every manifest, then
# opens a one-shot release PR (because main is protected and rejects
# direct pushes — see GH013), squash-merges it, tags the merged commit,
# pushes the tag, and creates a GitHub release with auto-generated notes
# assembled from PRs since the previous tag.
#
# Usage: GitHub web UI → Actions → "Release" → "Run workflow" → enter the
# new semver (e.g. `1.0.1`).
# new semver (e.g. `1.0.1`). Or via CLI:
#
# gh workflow run release.yml -f version=1.0.1

on:
workflow_dispatch:
Expand All @@ -17,7 +21,8 @@ on:
type: string

permissions:
contents: write # commit + tag + release
contents: write # tag + release
pull-requests: write # open + merge the release PR (main is protected)

jobs:
release:
Expand Down Expand Up @@ -77,14 +82,45 @@ jobs:
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Commit, tag, and push
- name: Push the bump on a release branch
run: |
set -euo pipefail
version="${{ inputs.version }}"
branch="release/v${version}"
git checkout -b "$branch"
git add package.json package-lock.json plugins/opencode/.claude-plugin/plugin.json .claude-plugin/marketplace.json
git commit -m "chore(release): v${version}"
git push origin "$branch"

- name: Open the release PR and merge it
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
version="${{ inputs.version }}"
branch="release/v${version}"
# Direct pushes to main are blocked by the "Changes must be made
# through a pull request" branch protection rule, so the release
# workflow opens a one-shot PR and immediately squash-merges it.
# This still satisfies the rule (the change goes via a PR) while
# keeping the workflow fully automated.
pr_url=$(gh pr create \
--base main \
--head "$branch" \
--title "chore(release): v${version}" \
--body "Automated release PR generated by \`.github/workflows/release.yml\`. Bumps every version-bearing manifest to v${version}.")
echo "Created release PR: $pr_url"
gh pr merge "$pr_url" --squash --delete-branch

- name: Tag the merged commit on main
run: |
set -euo pipefail
version="${{ inputs.version }}"
# Pick up the squash-merge commit that gh pr merge just created.
git fetch origin main
git checkout main
git reset --hard origin/main
git tag -a "v${version}" -m "Release v${version}"
git push origin HEAD:main
git push origin "v${version}"

- name: Create GitHub release
Expand Down