Skip to content

Release — bump version (PR) #13

Release — bump version (PR)

Release — bump version (PR) #13

Workflow file for this run

# Semver version bump via pull request (for repos where main is ruleset-protected:
# PR required, status checks, signed commits, etc.).
#
# 1) Run this workflow → opens PR from branch `chore-bump-vX.Y.Z` → main.
# (Do not use `release/...` — a branch or tag named `release` causes push conflicts.)
# 2) Wait for CI / Code Scanning on the PR; merge when green (prefer **Squash merge**
# so your verified/signed identity applies if you require signed commits on main).
# 3) Run **Release — push tag** (`release-tag.yml`) to create `vX.Y.Z` and trigger `release.yml`.
#
# Semver: https://semver.org — patch / minor / major (same as before).
#
# Who can run: repository variable RELEASE_ALLOWED_ACTORS (comma-separated). If unset,
# only github.repository_owner. For org-owned repos, set RELEASE_ALLOWED_ACTORS.
#
# Token: secrets.RELEASE_AUTOMATION_PAT (required) — fine-grained PAT: Contents + Pull requests
# for this repo. Many orgs block GITHUB_TOKEN from creating PRs (createPullRequest).
name: Release — bump version (PR)
on:
workflow_dispatch:
inputs:
release_type:
description: "Semver bump (see https://semver.org)"
type: choice
required: true
default: patch
options:
- patch
- minor
- major
permissions:
contents: write
pull-requests: write
concurrency:
group: release-bump
cancel-in-progress: false
jobs:
open-version-pr:
runs-on: ubuntu-latest
# Uncomment for manual approval before opening the PR:
# environment: release
steps:
- name: Verify release trigger permission
env:
ACTOR: ${{ github.actor }}
REPO_OWNER: ${{ github.repository_owner }}
ALLOWED_ACTORS: ${{ vars.RELEASE_ALLOWED_ACTORS }}
run: |
set -euo pipefail
python3 << 'PY'
import os
actor = os.environ["ACTOR"]
owner = os.environ["REPO_OWNER"]
raw = os.environ.get("ALLOWED_ACTORS", "").strip()
if raw:
allowed = [x.strip() for x in raw.split(",") if x.strip()]
else:
allowed = [owner]
if actor not in allowed:
print(f"::error::User '{actor}' is not allowed to run releases. "
f"Allowed: {allowed}. Set repository variable RELEASE_ALLOWED_ACTORS.")
raise SystemExit(1)
print(f"OK: {actor} is authorized to release.")
PY
- name: Require automation PAT
env:
RELEASE_AUTOMATION_PAT: ${{ secrets.RELEASE_AUTOMATION_PAT }}
run: |
if [ -z "${RELEASE_AUTOMATION_PAT:-}" ]; then
echo "::error::Add repository secret RELEASE_AUTOMATION_PAT. A fine-grained personal access token needs Contents: Read and write and Pull requests: Read and write on this repository. The default GITHUB_TOKEN cannot create pull requests when GitHub reports: createPullRequest is not permitted for GitHub Actions."
exit 1
fi
- name: Checkout main
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
token: ${{ secrets.RELEASE_AUTOMATION_PAT }}
- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Pull latest main
run: git pull origin main
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Compute next version & bump files
id: ver
env:
RELEASE_TYPE: ${{ inputs.release_type }}
run: |
set -euo pipefail
CURRENT=$(python3 -c "import json; print(json.load(open('apps/desktop/src-tauri/tauri.conf.json'))['version'])")
export CURRENT
NEW=$(python3 - <<'PY'
import os
cur = os.environ["CURRENT"]
rt = os.environ["RELEASE_TYPE"]
major, minor, patch = map(int, cur.split("."))
if rt == "patch":
print(f"{major}.{minor}.{patch + 1}")
elif rt == "minor":
print(f"{major}.{minor + 1}.0")
elif rt == "major":
print(f"{major + 1}.0.0")
else:
raise SystemExit(1)
PY
)
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "new=$NEW" >> "$GITHUB_OUTPUT"
echo "Bumping $CURRENT → $NEW ($RELEASE_TYPE)"
python3 scripts/bump-version.py "$NEW"
- name: Refresh Cargo.lock
run: cargo generate-lockfile
- name: Create branch, commit, open PR
env:
GH_TOKEN: ${{ secrets.RELEASE_AUTOMATION_PAT }}
run: |
set -euo pipefail
NEW="${{ steps.ver.outputs.new }}"
BRANCH="chore-bump-v${NEW}"
git checkout -b "$BRANCH"
git add apps/desktop/src-tauri/tauri.conf.json \
apps/desktop/src-tauri/Cargo.toml \
apps/desktop/src/components/settings/Settings.tsx \
Cargo.lock
git commit -m "chore: bump version to $NEW"
# Earlier runs may have left the same branch on the remote; a plain push then fails (non-fast-forward).
if git ls-remote --heads origin "$BRANCH" | grep -q .; then
git fetch origin "+refs/heads/${BRANCH}:refs/remotes/origin/${BRANCH}"
echo "Remote branch $BRANCH already exists (retry); pushing with --force-with-lease"
git push --force-with-lease -u origin "$BRANCH"
else
git push -u origin "$BRANCH"
fi
PR_COUNT=$(gh pr list --head "$BRANCH" --base main --json number --jq 'length')
if [ "$PR_COUNT" != "0" ]; then
echo "Pull request already open for $BRANCH; skipping gh pr create."
exit 0
fi
gh pr create \
--base main \
--head "$BRANCH" \
--title "chore: bump version to $NEW" \
--body "## Version bump $NEW
Automated by **Release — bump version (PR)**.
### Next steps
1. Wait for required checks (including Code Scanning if configured for PRs).
2. Merge this PR (prefer **Squash merge** if your rules require verified commits on \`main\`).
3. Run [**Release — push tag**](${{ github.server_url }}/${{ github.repository }}/actions/workflows/release-tag.yml) to create tag \`v$NEW\` and start the build (\`release.yml\`).
---
* [Semver](https://semver.org) bump type: \`${{ inputs.release_type }}\`"
- name: Summary
run: |
NEW="${{ steps.ver.outputs.new }}"
echo "### Branch chore-bump-v$NEW pushed" >> "$GITHUB_STEP_SUMMARY"
echo "If the PR was new, it was opened; if you re-ran the workflow, the branch was updated and an existing PR was left as-is. Merge when checks pass, then run **Release — push tag**." >> "$GITHUB_STEP_SUMMARY"