From f7c3980fc5023f9bf71ae09ef6fb6d6c8c2bb882 Mon Sep 17 00:00:00 2001 From: Cowork Lead Date: Thu, 30 Apr 2026 08:31:52 +0000 Subject: [PATCH 1/3] ci: add GitHub Actions release pipeline Triggers on every push to main (skips version-bump commits). - Detects semver bump type from conventional commit prefix (BREAKING CHANGE / !: = major, feat: = minor, everything else = patch) - Updates version in pyproject.toml and src/main.py - Builds wheel + sdist via hatch - Tags and pushes the bump commit - Creates a GitHub Release with generated notes and dist artefacts attached --- .github/workflows/release.yml | 93 +++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e806ea9 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,93 @@ +name: Release + +on: + push: + branches: [main] + +concurrency: + group: release + cancel-in-progress: false + +jobs: + release: + name: Bump version and publish release + runs-on: ubuntu-latest + if: "!startsWith(github.event.head_commit.message, 'chore: bump version')" + + permissions: + contents: write + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + # ------------------------------------------------------------------ # + # Determine semver bump type from the merge commit message. # + # Conventional commits: # + # BREAKING CHANGE / !: -> major # + # feat: -> minor # + # anything else -> patch # + # ------------------------------------------------------------------ # + - name: Determine bump type + id: bump + run: | + MSG="${{ github.event.head_commit.message }}" + if echo "$MSG" | grep -qiE "(BREAKING CHANGE|^[a-z]+(\([^)]*\))?!:)"; then + echo "type=major" >> "$GITHUB_OUTPUT" + elif echo "$MSG" | grep -qE "^feat(\([^)]*\))?:"; then + echo "type=minor" >> "$GITHUB_OUTPUT" + else + echo "type=patch" >> "$GITHUB_OUTPUT" + fi + + - name: Compute new version + id: version + run: | + CURRENT=$(grep '^version = ' pyproject.toml | sed 's/version = "\(.*\)"/\1/') + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" + case "${{ steps.bump.outputs.type }}" in + major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; + minor) MINOR=$((MINOR + 1)); PATCH=0 ;; + patch) PATCH=$((PATCH + 1)) ;; + esac + NEW="${MAJOR}.${MINOR}.${PATCH}" + echo "current=$CURRENT" >> "$GITHUB_OUTPUT" + echo "new=$NEW" >> "$GITHUB_OUTPUT" + echo "Bumping $CURRENT -> $NEW (type: ${{ steps.bump.outputs.type }})" + + - name: Bump version in source files + run: | + NEW="${{ steps.version.outputs.new }}" + sed -i "s/^version = \".*\"/version = \"${NEW}\"/" pyproject.toml + sed -i "s/@click\.version_option(\"[^\"]*\")/@click.version_option(\"${NEW}\")/" src/main.py + + - name: Set up Python 3.11 + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Install Hatch + run: pip install hatch --quiet + + - name: Build wheel and sdist + run: hatch build + + - name: Commit, tag and push + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add pyproject.toml src/main.py + git commit -m "chore: bump version ${{ steps.version.outputs.current }} -> ${{ steps.version.outputs.new }}" + git tag "v${{ steps.version.outputs.new }}" + git push origin main --follow-tags + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "v${{ steps.version.outputs.new }}" \ + --title "v${{ steps.version.outputs.new }}" \ + --generate-notes \ + dist/* From 1103298f824d59aca15e27a8081a0fe6f53f4be2 Mon Sep 17 00:00:00 2001 From: Cowork Lead Date: Thu, 30 Apr 2026 08:37:46 +0000 Subject: [PATCH 2/3] ci(temp): add one-shot workflow to open the draft PR Self-describing: runs on push to this branch, calls gh pr create --draft, exits early if PR already exists. Will be removed before merge. --- .github/workflows/open-pr.yml | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/open-pr.yml diff --git a/.github/workflows/open-pr.yml b/.github/workflows/open-pr.yml new file mode 100644 index 0000000..d0c4403 --- /dev/null +++ b/.github/workflows/open-pr.yml @@ -0,0 +1,51 @@ +name: Open draft PR + +on: + push: + branches: [feat/github-release-pipeline] + +jobs: + open-pr: + runs-on: ubuntu-latest + permissions: + pull-requests: write + contents: read + steps: + - uses: actions/checkout@v4 + + - name: Create draft PR (idempotent) + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + EXISTING=$(gh pr list \ + --head feat/github-release-pipeline \ + --base main \ + --json number \ + --jq '.[0].number' 2>/dev/null) + + if [ -n "$EXISTING" ]; then + echo "PR #$EXISTING already exists — nothing to do" + exit 0 + fi + + gh pr create \ + --draft \ + --base main \ + --head feat/github-release-pipeline \ + --title "ci: add GitHub Actions release pipeline" \ + --body "## What + +Adds \`.github/workflows/release.yml\` — a pipeline that fires on every push to \`main\` and: + +1. **Detects semver bump type** from the merge commit message (conventional commits) + - \`BREAKING CHANGE\` / \`!:\` → **major** + - \`feat:\` → **minor** + - anything else → **patch** +2. **Updates version** in \`pyproject.toml\` and \`src/main.py\` +3. **Builds** wheel + sdist via \`hatch build\` +4. **Commits + tags** the bump (\`chore: bump version X → Y\`) and pushes with \`--follow-tags\` +5. **Creates a GitHub Release** with auto-generated notes and the \`dist/*\` artefacts attached + +### Loop prevention + +The job has an \`if:\` guard that skips when the triggering commit starts with \`chore: bump version\`, so the version-bump push does not re-trigger itself." From 5b64b5fbb43e80054c443403ee643fb86d935585 Mon Sep 17 00:00:00 2001 From: "Viko S." <57849063+0x56696B@users.noreply.github.com> Date: Thu, 30 Apr 2026 12:27:49 +0300 Subject: [PATCH 3/3] chore: remove open-pr workflow --- .github/workflows/open-pr.yml | 51 ----------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 .github/workflows/open-pr.yml diff --git a/.github/workflows/open-pr.yml b/.github/workflows/open-pr.yml deleted file mode 100644 index d0c4403..0000000 --- a/.github/workflows/open-pr.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Open draft PR - -on: - push: - branches: [feat/github-release-pipeline] - -jobs: - open-pr: - runs-on: ubuntu-latest - permissions: - pull-requests: write - contents: read - steps: - - uses: actions/checkout@v4 - - - name: Create draft PR (idempotent) - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - EXISTING=$(gh pr list \ - --head feat/github-release-pipeline \ - --base main \ - --json number \ - --jq '.[0].number' 2>/dev/null) - - if [ -n "$EXISTING" ]; then - echo "PR #$EXISTING already exists — nothing to do" - exit 0 - fi - - gh pr create \ - --draft \ - --base main \ - --head feat/github-release-pipeline \ - --title "ci: add GitHub Actions release pipeline" \ - --body "## What - -Adds \`.github/workflows/release.yml\` — a pipeline that fires on every push to \`main\` and: - -1. **Detects semver bump type** from the merge commit message (conventional commits) - - \`BREAKING CHANGE\` / \`!:\` → **major** - - \`feat:\` → **minor** - - anything else → **patch** -2. **Updates version** in \`pyproject.toml\` and \`src/main.py\` -3. **Builds** wheel + sdist via \`hatch build\` -4. **Commits + tags** the bump (\`chore: bump version X → Y\`) and pushes with \`--follow-tags\` -5. **Creates a GitHub Release** with auto-generated notes and the \`dist/*\` artefacts attached - -### Loop prevention - -The job has an \`if:\` guard that skips when the triggering commit starts with \`chore: bump version\`, so the version-bump push does not re-trigger itself."