Merge pull request #46 from cloudengine-labs/release/v0.4.1 #4
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
| name: Release — Version Bump & Publish | |
| # Runs on every direct push to main (human or PR merge). | |
| # Bot-authored commits are skipped to prevent infinite loops. | |
| on: | |
| push: | |
| branches: [main] | |
| permissions: | |
| contents: write # needed to push the release branch and create tags | |
| pull-requests: write # needed to open the version-bump PR | |
| jobs: | |
| version-bump: | |
| name: Bump patch version, update docs, open PR & tag | |
| runs-on: ubuntu-latest | |
| # Skip commits that were already made by this workflow (or any bot) to | |
| # avoid triggering an infinite bump loop. | |
| # Also skip merges of the automated release/vX.Y.Z PRs: a regular merge | |
| # produces a commit message starting with "Merge pull request" that also | |
| # references the release/vX.Y.Z branch name. | |
| if: >- | |
| github.actor != 'github-actions[bot]' && | |
| !contains(github.event.head_commit.message, '[skip ci]') && | |
| !(startsWith(github.event.head_commit.message, 'Merge pull request') && | |
| contains(github.event.head_commit.message, 'release/v')) | |
| steps: | |
| - name: Checkout repository (full history for tagging) | |
| uses: actions/checkout@v4 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.11" | |
| # ── 1. Compute new patch version ───────────────────────────────────── | |
| - name: Compute and apply patch version bump | |
| id: bump | |
| run: | | |
| python - <<'PYEOF' | |
| import re, sys | |
| path = "cli/__version__.py" | |
| with open(path) as fh: | |
| content = fh.read() | |
| m = re.search(r'__version__\s*=\s*"(\d+)\.(\d+)\.(\d+)"', content) | |
| if not m: | |
| print("ERROR: could not find __version__ in cli/__version__.py", file=sys.stderr) | |
| sys.exit(1) | |
| major, minor, patch = int(m.group(1)), int(m.group(2)), int(m.group(3)) | |
| new_version = f"{major}.{minor}.{patch + 1}" | |
| new_content = re.sub( | |
| r'(__version__\s*=\s*)"[^"]+"', | |
| f'\\1"{new_version}"', | |
| content, | |
| ) | |
| with open(path, "w") as fh: | |
| fh.write(new_content) | |
| print(f"Bumped {major}.{minor}.{patch} → {new_version}") | |
| # Expose new version to subsequent steps via GITHUB_OUTPUT | |
| import os | |
| with open(os.environ["GITHUB_OUTPUT"], "a") as out: | |
| out.write(f"new_version={new_version}\n") | |
| PYEOF | |
| # ── 2. Update CHANGELOG.md ──────────────────────────────────────────── | |
| - name: Prepend CHANGELOG entry for new version | |
| env: | |
| NEW_VERSION: ${{ steps.bump.outputs.new_version }} | |
| run: | | |
| python - <<'PYEOF' | |
| import os | |
| from datetime import date | |
| new_version = os.environ["NEW_VERSION"] | |
| today = date.today().isoformat() | |
| entry = ( | |
| f"\n## [{new_version}] - {today}\n\n" | |
| f"### Changed\n" | |
| f"- Automated patch release — version bump to {new_version}.\n\n" | |
| f"---\n" | |
| ) | |
| with open("CHANGELOG.md") as fh: | |
| content = fh.read() | |
| # Insert the new entry before the first existing release section | |
| marker = "\n## [" | |
| if marker not in content: | |
| print("WARNING: no existing release section found in CHANGELOG.md; appending entry") | |
| new_content = content + entry | |
| else: | |
| insert_at = content.index(marker) | |
| new_content = content[:insert_at] + entry + content[insert_at:] | |
| with open("CHANGELOG.md", "w") as fh: | |
| fh.write(new_content) | |
| print(f"CHANGELOG updated with {new_version} entry") | |
| PYEOF | |
| # ── 3. Update version badge in Hugo docs index ──────────────────────── | |
| - name: Update version badge in Hugo docs | |
| env: | |
| NEW_VERSION: ${{ steps.bump.outputs.new_version }} | |
| run: | | |
| # Replace any existing version-X.Y.Z-blue badge with the new version | |
| sed -i -E \ | |
| "s|version-[0-9]+\.[0-9]+\.[0-9]+-blue|version-${NEW_VERSION}-blue|g" \ | |
| hugo-docs/content/_index.md | |
| # ── 4. Open a PR with the version-bump changes ──────────────────────── | |
| # peter-evans/create-pull-request creates commits via the GitHub API so | |
| # they are automatically verified (signed), satisfying the branch | |
| # protection rule that requires signed commits. It also opens a PR | |
| # instead of pushing directly to main, satisfying the rule that all | |
| # changes must go through a pull request. | |
| - name: Create pull request for version bump | |
| id: cpr | |
| uses: peter-evans/create-pull-request@v7 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| branch: release/v${{ steps.bump.outputs.new_version }} | |
| commit-message: "chore: bump version to v${{ steps.bump.outputs.new_version }} [skip ci]" | |
| title: "chore: bump version to v${{ steps.bump.outputs.new_version }}" | |
| body: | | |
| Automated patch version bump to `v${{ steps.bump.outputs.new_version }}`. | |
| - Updates `cli/__version__.py` | |
| - Prepends entry to `CHANGELOG.md` | |
| - Updates version badge in `hugo-docs/content/_index.md` | |
| labels: | | |
| release | |
| automated | |
| add-paths: | | |
| cli/__version__.py | |
| CHANGELOG.md | |
| hugo-docs/content/_index.md | |
| # ── 5. Tag the PR branch's head commit ────────────────────────────────── | |
| # Tag the commit on the release branch (not the local main checkout) so | |
| # the tag always points to the exact commit that carries the version bump. | |
| - name: Create and push git tag | |
| if: steps.cpr.outputs.pull-request-number != '' | |
| env: | |
| NEW_VERSION: ${{ steps.bump.outputs.new_version }} | |
| TAG_SHA: ${{ steps.cpr.outputs.pull-request-head-sha }} | |
| run: | | |
| git tag "v${NEW_VERSION}" "${TAG_SHA}" | |
| git push origin "v${NEW_VERSION}" |