Skip to content

Commit d6a95c8

Browse files
committed
Move publish job to its own workflow with commit-message skip guard
1 parent 0c60aef commit d6a95c8

File tree

3 files changed

+98
-79
lines changed

3 files changed

+98
-79
lines changed

.github/workflows/ci-stable.yml

Lines changed: 0 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -58,80 +58,3 @@ jobs:
5858
with:
5959
name: coverage-xml
6060
path: coverage.xml
61-
62-
publish:
63-
needs: pytest
64-
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
65-
runs-on: ubuntu-latest
66-
permissions:
67-
contents: write
68-
steps:
69-
- uses: actions/checkout@v4
70-
with:
71-
token: ${{ secrets.GITHUB_TOKEN }}
72-
- name: Set up Python
73-
uses: actions/setup-python@v5
74-
with:
75-
python-version: "3.12"
76-
cache: pip
77-
- name: Install build tools
78-
run: |
79-
python -m pip install --upgrade pip
80-
pip install build twine
81-
- name: Bump patch version in stable.toml and dev.toml
82-
id: version
83-
run: |
84-
python - <<'PY'
85-
import os
86-
import pathlib
87-
import re
88-
89-
def bump(path: pathlib.Path) -> str:
90-
text = path.read_text(encoding="utf-8")
91-
match = re.search(r'^version = "(\d+)\.(\d+)\.(\d+)"', text, re.MULTILINE)
92-
if match is None:
93-
raise SystemExit(f"no version line found in {path}")
94-
major, minor, patch = (int(g) for g in match.groups())
95-
new = f"{major}.{minor}.{patch + 1}"
96-
path.write_text(text.replace(match.group(0), f'version = "{new}"', 1), encoding="utf-8")
97-
return new
98-
99-
stable_version = bump(pathlib.Path("stable.toml"))
100-
dev_version = bump(pathlib.Path("dev.toml"))
101-
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as fp:
102-
fp.write(f"version={stable_version}\n")
103-
fp.write(f"dev_version={dev_version}\n")
104-
print(f"stable.toml -> {stable_version}")
105-
print(f"dev.toml -> {dev_version}")
106-
PY
107-
- name: Commit and push bumped versions back to main
108-
id: bump_commit
109-
env:
110-
VERSION: ${{ steps.version.outputs.version }}
111-
run: |
112-
git config user.name "github-actions[bot]"
113-
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
114-
git add stable.toml dev.toml
115-
git commit -m "Bump version to v${VERSION} [skip ci]"
116-
git push origin HEAD:main
117-
echo "oid=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
118-
- name: Use stable.toml as pyproject.toml
119-
run: cp stable.toml pyproject.toml
120-
- name: Build sdist and wheel
121-
run: python -m build
122-
- name: Twine check
123-
run: twine check dist/*
124-
- name: Twine upload to PyPI
125-
env:
126-
TWINE_USERNAME: __token__
127-
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
128-
run: twine upload --non-interactive dist/*
129-
- name: Create GitHub Release
130-
env:
131-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
132-
BUMP_OID: ${{ steps.bump_commit.outputs.oid }}
133-
run: |
134-
gh release create "v${{ steps.version.outputs.version }}" dist/* \
135-
--title "v${{ steps.version.outputs.version }}" \
136-
--generate-notes \
137-
--target "$BUMP_OID"

.github/workflows/publish.yml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
branches: ["main"]
6+
7+
permissions:
8+
contents: write
9+
10+
concurrency:
11+
group: publish-pypi
12+
cancel-in-progress: false
13+
14+
jobs:
15+
publish:
16+
runs-on: ubuntu-latest
17+
if: "!contains(github.event.head_commit.message, 'chore: bump version')"
18+
steps:
19+
- uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Set up Python
24+
uses: actions/setup-python@v5
25+
with:
26+
python-version: "3.12"
27+
28+
- name: Install build tools
29+
run: |
30+
python -m pip install --upgrade pip
31+
pip install build twine
32+
33+
- name: Bump patch version in stable.toml and dev.toml
34+
id: bump
35+
run: |
36+
python <<'EOF'
37+
import os
38+
import pathlib
39+
import re
40+
41+
def bump(path: pathlib.Path) -> str:
42+
text = path.read_text(encoding="utf-8")
43+
match = re.search(r'^version\s*=\s*"(\d+)\.(\d+)\.(\d+)"', text, re.MULTILINE)
44+
if not match:
45+
raise SystemExit(f"Could not find version in {path}")
46+
major, minor, patch = (int(part) for part in match.groups())
47+
new_version = f"{major}.{minor}.{patch + 1}"
48+
new_text = re.sub(
49+
r'^(version\s*=\s*)"\d+\.\d+\.\d+"',
50+
rf'\1"{new_version}"',
51+
text,
52+
count=1,
53+
flags=re.MULTILINE,
54+
)
55+
path.write_text(new_text, encoding="utf-8")
56+
return new_version
57+
58+
stable_version = bump(pathlib.Path("stable.toml"))
59+
dev_version = bump(pathlib.Path("dev.toml"))
60+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
61+
output.write(f"new_version={stable_version}\n")
62+
output.write(f"dev_version={dev_version}\n")
63+
print(f"stable.toml -> {stable_version}")
64+
print(f"dev.toml -> {dev_version}")
65+
EOF
66+
67+
- name: Use stable.toml as pyproject.toml
68+
run: cp stable.toml pyproject.toml
69+
70+
- name: Build distribution
71+
run: python -m build
72+
73+
- name: Publish to PyPI
74+
env:
75+
TWINE_USERNAME: __token__
76+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
77+
run: twine upload --non-interactive dist/*
78+
79+
- name: Commit and tag version bump
80+
run: |
81+
git config user.name "github-actions[bot]"
82+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
83+
git add stable.toml dev.toml
84+
git commit -m "chore: bump version to ${{ steps.bump.outputs.new_version }}"
85+
git tag "v${{ steps.bump.outputs.new_version }}"
86+
git push origin main
87+
git push origin "v${{ steps.bump.outputs.new_version }}"
88+
89+
- name: Create GitHub Release
90+
env:
91+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
92+
run: |
93+
gh release create "v${{ steps.bump.outputs.new_version }}" \
94+
dist/* \
95+
--title "v${{ steps.bump.outputs.new_version }}" \
96+
--generate-notes

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ automation_file/
9595
- `main` branch: stable releases, publishes `automation_file` to PyPI (version in `stable.toml`).
9696
- `dev` branch: development, publishes `automation_file_dev` to PyPI (version in `dev.toml`).
9797
- Keep `dependencies` and `[project.optional-dependencies]` (`dev`) in sync across both TOMLs. Backends (`boto3`, `azure-storage-blob`, `dropbox`, `paramiko`) and `PySide6` are first-class runtime deps — do not move them back under extras.
98-
- **Version bumping is automatic.** The stable publish job bumps the patch in both `stable.toml` and `dev.toml`, commits the bump back to `main` with `[skip ci]`, then builds and releases. Do not hand-bump before merging to `main`.
98+
- **Version bumping is automatic.** A dedicated publish workflow bumps the patch in both `stable.toml` and `dev.toml`, builds, uploads to PyPI, then commits the bump back to `main` tagged as `vX.Y.Z`. Do not hand-bump before merging to `main`. The next publish run is skipped via a commit-message guard (`chore: bump version`), so the bump itself never re-triggers publishing.
9999
- CI: GitHub Actions (Windows, Python 3.10 / 3.11 / 3.12) — one matrix workflow per branch: `.github/workflows/ci-dev.yml`, `.github/workflows/ci-stable.yml`.
100100
- CI steps: `lint` (ruff check + ruff format --check + mypy) → `pytest` with coverage → uploads `coverage.xml` as an artifact.
101-
- Stable branch additionally runs a `publish` job on push to `main`: auto-bumps the patch in both TOMLs and commits back, then builds the sdist + wheel, `twine check`, `twine upload` using `PYPI_API_TOKEN`, then `gh release create v<version> --generate-notes`.
101+
- Publishing lives in a separate workflow (`.github/workflows/publish.yml`) that runs on push to `main`: bumps both TOMLs, copies `stable.toml` to `pyproject.toml`, builds the sdist + wheel, `twine upload` via `PYPI_API_TOKEN`, then commits + tags + pushes and creates `gh release create v<version> --generate-notes`.
102102
- `pre-commit` is configured (`.pre-commit-config.yaml`): trailing-whitespace, eof-fixer, check-yaml, check-toml, check-added-large-files, ruff, ruff-format, mypy. Install with `pre-commit install` after cloning.
103103

104104
## Development

0 commit comments

Comments
 (0)