Skip to content

Commit d5b9b93

Browse files
committed
ci: add release-publish workflow (PyPI OIDC + conda + GitHub release)
Triggered by merging a pull request labeled release. Reads the version from .release-please-manifest.json, creates + pushes the tag, builds the wheel + sdist + conda package, publishes to PyPI via OIDC trusted publishing, uploads the conda package to anaconda.org, and cuts the GitHub release with all three assets.
1 parent 8d225be commit d5b9b93

1 file changed

Lines changed: 156 additions & 0 deletions

File tree

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Publish an openprotein-python release.
2+
#
3+
# Triggered by merging a pull request labeled `release`. The version is read from
4+
# .release-please-manifest.json; on merge it creates + pushes the vX.Y.Z tag (so
5+
# hatch-vcs can derive the version), builds the wheel + sdist + conda package,
6+
# publishes to PyPI via OIDC trusted publishing (no token), uploads the conda
7+
# package to anaconda.org, and cuts the GitHub release with all three assets.
8+
#
9+
# Every build/release job checks out the merge commit with fetch-depth: 0 so
10+
# hatch-vcs and the changelog compare-link have full tag history.
11+
#
12+
# Required repo config:
13+
# - PyPI trusted publisher registered for OpenProteinAI/openprotein-python +
14+
# workflow release-publish.yml (no secret needed for PyPI).
15+
# - secret ANACONDA_TOKEN — anaconda.org API token for `anaconda upload`.
16+
# - var ANACONDA_OWNER — anaconda.org user/org (channel) to upload to.
17+
18+
name: release-publish
19+
20+
on:
21+
pull_request:
22+
types: [closed]
23+
24+
jobs:
25+
build:
26+
# Only when a PR labeled `release` is actually merged.
27+
if: >-
28+
github.event.pull_request.merged == true &&
29+
contains(github.event.pull_request.labels.*.name, 'release')
30+
runs-on: ubuntu-latest
31+
permissions:
32+
contents: write # push the release tag
33+
defaults:
34+
run:
35+
shell: bash -el {0}
36+
outputs:
37+
tag: ${{ steps.resolve.outputs.tag }}
38+
steps:
39+
- uses: actions/checkout@v4
40+
with:
41+
ref: ${{ github.event.pull_request.merge_commit_sha }}
42+
fetch-depth: 0
43+
44+
- name: Resolve version + create tag
45+
id: resolve
46+
run: |
47+
TAG="v$(jq -r '."."' .release-please-manifest.json)"
48+
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
49+
git tag -a "${TAG}" -m "${TAG}"
50+
git push origin "${TAG}"
51+
52+
- name: Set up conda (conda-build + anaconda-client)
53+
uses: conda-incubator/setup-miniconda@v3
54+
with:
55+
miniforge-version: latest
56+
channels: conda-forge
57+
python-version: "3.11"
58+
59+
- name: Install build tooling
60+
run: |
61+
conda install -y -c conda-forge conda-build anaconda-client
62+
pip install hatch
63+
64+
- name: Build wheel + sdist
65+
run: hatch build
66+
67+
- name: Stage python dists
68+
run: |
69+
mkdir -p pypi-dist
70+
cp dist/*.whl dist/*.tar.gz pypi-dist/
71+
72+
- name: Build conda package
73+
run: |
74+
hatch build -t conda
75+
mkdir -p conda-dist
76+
cp dist/conda/noarch/*.conda conda-dist/
77+
78+
- uses: actions/upload-artifact@v4
79+
with:
80+
name: pypi-dist
81+
path: pypi-dist/
82+
83+
- uses: actions/upload-artifact@v4
84+
with:
85+
name: conda-dist
86+
path: conda-dist/
87+
88+
pypi:
89+
needs: build
90+
runs-on: ubuntu-latest
91+
permissions:
92+
id-token: write # OIDC trusted publishing
93+
steps:
94+
- uses: actions/download-artifact@v4
95+
with:
96+
name: pypi-dist
97+
path: dist/
98+
- name: Publish to PyPI (OIDC)
99+
uses: pypa/gh-action-pypi-publish@release/v1
100+
101+
conda:
102+
needs: build
103+
runs-on: ubuntu-latest
104+
defaults:
105+
run:
106+
shell: bash -el {0}
107+
steps:
108+
- uses: actions/download-artifact@v4
109+
with:
110+
name: conda-dist
111+
path: conda-dist/
112+
- uses: conda-incubator/setup-miniconda@v3
113+
with:
114+
miniforge-version: latest
115+
channels: conda-forge
116+
- name: Upload to anaconda.org
117+
env:
118+
ANACONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }}
119+
ANACONDA_OWNER: ${{ vars.ANACONDA_OWNER }}
120+
run: |
121+
conda install -y -c conda-forge anaconda-client
122+
anaconda -t "$ANACONDA_TOKEN" upload --user "$ANACONDA_OWNER" --force conda-dist/*.conda
123+
124+
github-release:
125+
needs: [build, pypi, conda]
126+
runs-on: ubuntu-latest
127+
permissions:
128+
contents: write
129+
env:
130+
TAG: ${{ needs.build.outputs.tag }}
131+
steps:
132+
- uses: actions/checkout@v4
133+
with:
134+
fetch-depth: 0
135+
- uses: actions/download-artifact@v4
136+
with:
137+
name: pypi-dist
138+
path: dist/
139+
- uses: actions/download-artifact@v4
140+
with:
141+
name: conda-dist
142+
path: dist/
143+
- name: Create GitHub release
144+
env:
145+
GH_TOKEN: ${{ github.token }}
146+
run: |
147+
prev=$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || echo "")
148+
if [ -n "$prev" ]; then
149+
notes="**Full Changelog**: https://github.com/${GITHUB_REPOSITORY}/compare/${prev}...${TAG}"
150+
else
151+
notes="Release ${TAG}"
152+
fi
153+
gh release create "${TAG}" \
154+
--title "${TAG}" \
155+
--notes "${notes}" \
156+
dist/*.whl dist/*.tar.gz dist/*.conda

0 commit comments

Comments
 (0)