Release — bump version (PR) #13
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
| # 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" |