diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 264536d..df17a68 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,12 +1,16 @@ name: Release # Manually-triggered release workflow. -# Runs `npm run bump-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 ` 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: @@ -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: @@ -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