Switch to static versioning and two-workflow release model

Replace hatch-vcs dynamic versioning with static version in _version.py
and pyproject.toml. Split CI into build.yml (test + stage draft release
on main) and release.yml (publish from draft on tag push). Docker images
are built on main pushes and re-tagged with the release version on tag
push without rebuilding.
This commit is contained in:
James R. Barlow
2026-02-20 23:34:03 -08:00
parent a899f0d59a
commit e19ea653aa
7 changed files with 539 additions and 67 deletions
+22 -58
View File
@@ -9,8 +9,6 @@ on:
- ci
- release/*
- feature/*
tags:
- v*
paths-ignore:
- README*
pull_request:
@@ -34,8 +32,6 @@ jobs:
steps:
- uses: actions/checkout@v6
with:
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
- name: Install uv
uses: astral-sh/setup-uv@v7
@@ -112,8 +108,6 @@ jobs:
steps:
- uses: actions/checkout@v6
with:
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
- name: Install Homebrew deps
continue-on-error: true
@@ -176,8 +170,6 @@ jobs:
steps:
- uses: actions/checkout@v6
with:
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
- name: Install uv
uses: astral-sh/setup-uv@v7
@@ -216,8 +208,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
- name: Install uv
uses: astral-sh/setup-uv@v7
@@ -235,64 +225,42 @@ jobs:
./dist/*.whl
./dist/*.tar.gz
upload_pypi:
name: Deploy artifacts to PyPI
stage_release:
name: Stage release artifacts
needs: [wheel_sdist_linux, test_linux, test_macos, test_windows]
runs-on: ubuntu-latest
environment: release
if: github.ref == 'refs/heads/main'
permissions:
id-token: write # mandatory for PyPI publishing
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v7
with:
name: artifact
path: dist
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
create_release:
name: Create GitHub release
needs: [upload_pypi]
runs-on: ubuntu-latest
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
permissions:
# Required to create a release
contents: write
id-token: write
steps:
- uses: actions/checkout@v6
- uses: actions/download-artifact@v7
with:
name: artifact
path: dist
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v3.2.0
with:
inputs: |
./dist/*.tar.gz
./dist/*.whl
- name: Read version from source
id: version
run: |
VERSION=$(python3 -c "exec(open('src/ocrmypdf/_version.py').read()); print(__version__)")
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create GitHub Release
- name: Create or update draft release
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh release create
"$GITHUB_REF_NAME"
--repo "$GITHUB_REPOSITORY"
--notes ""
run: |
TAG="v${{ steps.version.outputs.version }}"
- name: Upload artifact signatures to GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
# Upload to GitHub Release using the `gh` CLI.
# `dist/` contains the built packages, and the
# sigstore-produced signatures and certificates.
run: >-
gh release upload
"$GITHUB_REF_NAME" dist/**
--repo "$GITHUB_REPOSITORY"
# Delete existing draft release if it exists (ignore errors)
gh release delete "$TAG" --yes 2>/dev/null || true
# Create new draft release with all artifacts
gh release create "$TAG" \
--draft \
--title "$TAG" \
--notes "Draft release - will be updated when tag is pushed" \
dist/*
docker_ubuntu:
name: Build Ubuntu-based Docker image
@@ -314,8 +282,6 @@ jobs:
run: echo "DOCKER_IMAGE_NAME=ocrmypdf" >> $GITHUB_ENV
- uses: actions/checkout@v6
with:
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
- name: Login to Docker Hub
uses: docker/login-action@v3
@@ -362,8 +328,6 @@ jobs:
run: echo "DOCKER_IMAGE_NAME=ocrmypdf-alpine" >> $GITHUB_ENV
- uses: actions/checkout@v6
with:
fetch-depth: "0" # 0=all, needed for setuptools-scm to resolve version tags
- name: Login to Docker Hub
uses: docker/login-action@v3
+114
View File
@@ -0,0 +1,114 @@
# SPDX-FileCopyrightText: 2022 James R. Barlow
# SPDX-License-Identifier: MPL-2.0
name: Publish Release
on:
push:
tags:
- "v*"
jobs:
publish:
name: Publish release
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/ocrmypdf
permissions:
contents: write
id-token: write
steps:
- uses: actions/checkout@v6
- name: Download artifacts from draft release
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
mkdir -p dist
gh release download "$GITHUB_REF_NAME" --dir dist --pattern '*.whl'
gh release download "$GITHUB_REF_NAME" --dir dist --pattern '*.tar.gz'
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# PyPI doesn't support sigstore publishing, so generate after publishing to PyPI
- name: Sign the dists with Sigstore
uses: sigstore/gh-action-sigstore-python@v3.2.0
with:
inputs: |
./dist/*.tar.gz
./dist/*.whl
- name: Extract release notes
run: |
VERSION="${GITHUB_REF_NAME#v}"
MAJOR="${VERSION%%.*}"
MAJOR_PADDED=$(printf "%02d" "$MAJOR")
RELEASE_FILE="docs/releasenotes/version${MAJOR_PADDED}.md"
python3 << EOF
import re
version = "${VERSION}"
release_file = "${RELEASE_FILE}"
try:
with open(release_file) as f:
content = f.read()
# Find the section for this version
# Match from "## vX.Y.Z" until the next "## v" or end of file
pattern = rf"## v{re.escape(version)}\n(.*?)(?=\n## v|\Z)"
match = re.search(pattern, content, re.DOTALL)
notes = match.group(1).strip() if match else ""
except FileNotFoundError:
notes = ""
with open("release_notes.md", "w") as f:
f.write(notes)
EOF
- name: Publish release (convert draft to published)
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
# Update release: remove draft status, add release notes
gh release edit "$GITHUB_REF_NAME" \
--draft=false \
--notes-file release_notes.md
# Upload signatures to the release
gh release upload "$GITHUB_REF_NAME" dist/*.sigstore.json --clobber
docker_tag:
name: Tag Docker images with release version
needs: [publish]
runs-on: ubuntu-latest
steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: jbarlow83
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Tag ocrmypdf (Ubuntu) image
run: |
docker buildx imagetools create \
--tag "jbarlow83/ocrmypdf:$GITHUB_REF_NAME" \
"jbarlow83/ocrmypdf:latest"
- name: Tag ocrmypdf-ubuntu image
run: |
docker buildx imagetools create \
--tag "jbarlow83/ocrmypdf-ubuntu:$GITHUB_REF_NAME" \
"jbarlow83/ocrmypdf-ubuntu:latest"
- name: Tag ocrmypdf-alpine image
run: |
docker buildx imagetools create \
--tag "jbarlow83/ocrmypdf-alpine:$GITHUB_REF_NAME" \
"jbarlow83/ocrmypdf-alpine:latest"