Skip to content

Commit 8a504f1

Browse files
committed
Publish palace-util and palace-opds to PyPI on release
Add a GitHub Actions workflow that builds and uploads both workspace packages to PyPI when a GitHub Release is published, using PyPI's Trusted Publishing (OIDC) flow — no API tokens stored. The workflow: - Runs a matrix job per package on parallel runners. - Computes version / commit / branch from the release tag via dunamai. - Overwrites the package's _version.py with the computed values so the wheel metadata picks up the release version (see the previous commit for why _version.py is the source of truth). - Pins the intra-workspace dependency before building palace-opds: uv build leaves workspace deps as bare `palace-util` in the wheel's Requires-Dist, which would fail to resolve on PyPI; we sed in `palace-util==<version>` so consumers installing palace-opds get the matching palace-util. - Uploads via pypa/gh-action-pypi-publish@release/v1 with OIDC. One-time setup required on PyPI (see comment in the workflow file): create the palace-util and palace-opds projects and register this workflow + repo + `pypi` environment as a trusted publisher for each. palace-manager is intentionally NOT published here — it needs more work first (alembic etc. prevent a clean wheel build).
1 parent ed19271 commit 8a504f1

1 file changed

Lines changed: 99 additions & 0 deletions

File tree

.github/workflows/publish-pypi.yml

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: Publish to PyPI
2+
3+
# Publishes `palace-util` and `palace-opds` to PyPI when a GitHub Release is
4+
# published. Each package is built and uploaded independently via PyPI's
5+
# Trusted Publishing (OIDC) flow — no API tokens are stored.
6+
#
7+
# One-time setup on PyPI (for each of the two projects, palace-util and
8+
# palace-opds):
9+
#
10+
# 1. Create the project on PyPI.
11+
# 2. In the project's "Publishing" settings, add a trusted publisher:
12+
# - Owner: ThePalaceProject
13+
# - Repository: circulation
14+
# - Workflow filename: publish-pypi.yml
15+
# - Environment name: pypi
16+
# 3. (Optional) Create the `pypi` environment on this repo under
17+
# Settings → Environments, and restrict it to protected branches /
18+
# required reviewers for an approval gate before release.
19+
#
20+
# No action is needed on new releases beyond publishing a GitHub Release with
21+
# a tag that dunamai can parse as semver (e.g. `v1.0.0` or `1.0.0`).
22+
23+
on:
24+
release:
25+
types: [published]
26+
27+
env:
28+
PYTHON_VERSION: "3.12"
29+
30+
jobs:
31+
publish:
32+
name: Publish ${{ matrix.package }} to PyPI
33+
runs-on: ubuntu-24.04
34+
strategy:
35+
fail-fast: false
36+
matrix:
37+
include:
38+
- package: palace-util
39+
source_dir: packages/palace-util/src/palace/util
40+
- package: palace-opds
41+
source_dir: packages/palace-opds/src/palace/opds
42+
environment:
43+
name: pypi
44+
url: https://pypi.org/p/${{ matrix.package }}
45+
permissions:
46+
# Required for PyPI Trusted Publishing (OIDC).
47+
id-token: write
48+
contents: read
49+
50+
steps:
51+
- uses: actions/checkout@v6
52+
with:
53+
persist-credentials: false
54+
# dunamai needs the full tag history.
55+
fetch-depth: 0
56+
57+
- name: Install uv
58+
uses: astral-sh/setup-uv@v8.0.0
59+
with:
60+
enable-cache: true
61+
cache-dependency-glob: uv.lock
62+
python-version: ${{ env.PYTHON_VERSION }}
63+
activate-environment: true
64+
65+
- name: Compute version metadata
66+
id: version
67+
run: |
68+
echo "version=$(uv run --only-group ci dunamai from git --style semver)" >> "$GITHUB_OUTPUT"
69+
echo "commit=$(uv run --only-group ci dunamai from git --format '{commit}' --full-commit)" >> "$GITHUB_OUTPUT"
70+
echo "branch=$(uv run --only-group ci dunamai from git --format '{branch}')" >> "$GITHUB_OUTPUT"
71+
72+
- name: Write _version.py
73+
# hatch reads __version__ from this file via [tool.hatch.version]
74+
# regex source; overwriting it here is how the release version lands
75+
# in the wheel metadata.
76+
run: |
77+
cat > ${{ matrix.source_dir }}/_version.py <<EOF
78+
__version__: str = "${{ steps.version.outputs.version }}"
79+
__commit__: str | None = "${{ steps.version.outputs.commit }}"
80+
__branch__: str | None = "${{ steps.version.outputs.branch }}"
81+
EOF
82+
83+
- name: Pin intra-workspace dependency
84+
# uv build preserves workspace deps as bare names in Requires-Dist
85+
# (e.g. `palace-util` with no version). For PyPI we need an exact pin
86+
# so a consumer installing palace-opds resolves the matching
87+
# palace-util version.
88+
if: matrix.package == 'palace-opds'
89+
run: |
90+
sed -i 's|"palace-util"|"palace-util==${{ steps.version.outputs.version }}"|' \
91+
packages/palace-opds/pyproject.toml
92+
93+
- name: Build ${{ matrix.package }}
94+
run: uv build --package ${{ matrix.package }}
95+
96+
- name: Publish to PyPI
97+
uses: pypa/gh-action-pypi-publish@release/v1
98+
with:
99+
packages-dir: dist

0 commit comments

Comments
 (0)