Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,19 @@ jobs:
- name: Run unit tests
run: python3 -m unittest discover -s tests -t tests -v

# The release workflow only builds after a merge to the default branch, by
# which point the tag and the GitHub release already exist — a build that
# fails there burns a version number. Build here instead, on the same
# interpreter the tests above ran under, so a broken pyproject.toml is
# stopped at the pull request.
#
# The version is deliberately not rewritten: what this checks is that the
# tree as committed builds. Choosing the number belongs to the tag.
- name: Build the package
run: |
python3 -m pip install --upgrade build
python3 -m build

- name: Setup Python 3.14
uses: actions/setup-python@v5
with:
Expand Down
103 changes: 94 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
# Copy this file into a downstream repository as `.github/workflows/release.yml`.
# `scripts/sync.sh` does that automatically when the file does not exist yet.
# Release pipeline: version, tag, GitHub release, then the built package.
#
# This is the source-only shape: compute the version, create the tag, create the
# GitHub release. GitHub attaches the source tarball and zip to every release by
# itself, so nothing else is required.
#
# Add the `publish` job from examples/publish.yml when the repository publishes to
# a registry, and the `package` job from examples/package.yml when it ships build
# artifacts. Both are independent siblings of `release`: add or delete them freely.
# Adapted from nexform-tech/repo-template: the `release` job is the synced stub
# and stays as written, while `publish` and `package` come from
# `examples/publish.yml` and `examples/package.yml`. All three are siblings and
# both new jobs are gated on `release` producing a tag, so either can be deleted
# without touching the others.
name: Release

on:
Expand All @@ -30,3 +27,91 @@ jobs:
release:
# Pin to a moving major tag (`@v1`), or to a commit SHA for maximum safety.
uses: nexform-tech/repo-template/.github/workflows/release.yml@v1

# Publish to PyPI through Trusted Publishing: GitHub mints an OIDC token that
# PyPI exchanges for a one-shot upload credential, so this repository stores no
# API token at all. PyPI must know about the repository first — a pending
# publisher for owner `nexform-tech`, repository `litegrip-python`, workflow
# `release.yml` and no environment. Without it the upload is refused and this
# run goes red.
publish:
needs: release
# Nothing released (no feat/fix/perf in the merge) means nothing to publish.
if: needs.release.outputs.new_tag != ''
runs-on: ubuntu-22.04
permissions:
contents: read
id-token: write # the OIDC token PyPI exchanges for an upload credential
env:
VERSION: ${{ needs.release.outputs.new_tag }}
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

# The tag is the only source of truth: semantic-release never commits the
# version back (AGENTS.md §3), so pyproject.toml is rewritten here, in the
# workspace, and nowhere else. `litegrip.__version__` needs no equivalent
# step — it reads the installed distribution metadata (see
# `_detect_version` in src/litegrip/__init__.py), so it follows this number
# by construction instead of being a second copy of it.
- name: Set the release version
run: |
set -euo pipefail
V="${VERSION#v}"
sed -i "s/^version = .*/version = \"${V}\"/" pyproject.toml
# A sed that matched nothing would build 0.0.0 — the placeholder — so
# assert the rewrite rather than trusting it.
grep -qx "version = \"${V}\"" pyproject.toml

- name: Build sdist and wheel
run: |
python -m pip install --upgrade build
python -m build

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

# Attach the same artifacts to the GitHub release, so the files that reached
# PyPI are also downloadable from this repository without going through PyPI.
#
# The version rewrite and the build repeat what `publish` does. That is
# deliberate: the two jobs are independent siblings, and coupling them so the
# build happens once would mean deleting one breaks the other.
package:
needs: release
if: needs.release.outputs.new_tag != ''
runs-on: ubuntu-22.04
permissions:
contents: write # attach artifacts to the GitHub release
env:
VERSION: ${{ needs.release.outputs.new_tag }}
steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Set the release version
run: |
set -euo pipefail
V="${VERSION#v}"
sed -i "s/^version = .*/version = \"${V}\"/" pyproject.toml
grep -qx "version = \"${V}\"" pyproject.toml

- name: Build artifacts
run: |
python -m pip install --upgrade build
python -m build

- name: Attach artifacts to the release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload "$VERSION" dist/* --clobber
Loading