1+ name : release 
2+ run-name : Release ${{ inputs.working-directory }} by @${{ github.actor }} 
3+ on :
4+   workflow_call :
5+     inputs :
6+       working-directory :
7+         required : true 
8+         type : string 
9+         description : " From which folder this pipeline executes" 
10+   workflow_dispatch :
11+     inputs :
12+       working-directory :
13+         description : " From which folder this pipeline executes" 
14+         default : " ." 
15+       dangerous-nonmain-release :
16+         required : false 
17+         type : boolean 
18+         default : false 
19+         description : " Release from a non-main branch (danger!)" 
20+ 
21+ env :
22+   PYTHON_VERSION : " 3.11" 
23+   UV_FROZEN : " true" 
24+   UV_NO_SYNC : " true" 
25+ 
26+ jobs :
27+   build :
28+     if : github.ref == 'refs/heads/main' || inputs.dangerous-nonmain-release 
29+     environment : Scheduled testing 
30+     runs-on : ubuntu-latest 
31+ 
32+     outputs :
33+       pkg-name : ${{ steps.check-version.outputs.pkg-name }} 
34+       version : ${{ steps.check-version.outputs.version }} 
35+ 
36+     steps :
37+       - uses : actions/checkout@v4 
38+ 
39+       - name : Set up Python + uv 
40+         uses : " ./.github/actions/uv_setup" 
41+         with :
42+           python-version : ${{ env.PYTHON_VERSION }} 
43+ 
44+       #  We want to keep this build stage *separate* from the release stage,
45+       #  so that there's no sharing of permissions between them.
46+       #  The release stage has trusted publishing and GitHub repo contents write access,
47+       #  and we want to keep the scope of that access limited just to the release job.
48+       #  Otherwise, a malicious `build` step (e.g. via a compromised dependency)
49+       #  could get access to our GitHub or PyPI credentials.
50+       # 
51+       #  Per the trusted publishing GitHub Action:
52+       #  > It is strongly advised to separate jobs for building [...]
53+       #  > from the publish job.
54+       #  https://github.com/pypa/gh-action-pypi-publish#non-goals
55+       - name : Build project for distribution 
56+         run : uv build 
57+       - name : Upload build 
58+         uses : actions/upload-artifact@v4 
59+         with :
60+           name : dist 
61+           path : ${{ inputs.working-directory }}/dist/ 
62+ 
63+       - name : Check Version 
64+         id : check-version 
65+         shell : python 
66+         working-directory : ${{ inputs.working-directory }} 
67+         run : | 
68+           import os 
69+           import tomllib 
70+           with open("pyproject.toml", "rb") as f: 
71+               data = tomllib.load(f) 
72+           pkg_name = data["project"]["name"] 
73+           version = data["project"]["version"] 
74+           with open(os.environ["GITHUB_OUTPUT"], "a") as f: 
75+               f.write(f"pkg-name={pkg_name}\n") 
76+               f.write(f"version={version}\n") 
77+    publish :
78+     needs :
79+       - build 
80+     runs-on : ubuntu-latest 
81+     permissions :
82+       #  This permission is used for trusted publishing:
83+       #  https://blog.pypi.org/posts/2023-04-20-introducing-trusted-publishers/
84+       # 
85+       #  Trusted publishing has to also be configured on PyPI for each package:
86+       #  https://docs.pypi.org/trusted-publishers/adding-a-publisher/
87+       id-token : write 
88+ 
89+     defaults :
90+       run :
91+         working-directory : ${{ inputs.working-directory }} 
92+ 
93+     steps :
94+       - uses : actions/checkout@v4 
95+ 
96+       - name : Set up Python + uv 
97+         uses : " ./.github/actions/uv_setup" 
98+         with :
99+           python-version : ${{ env.PYTHON_VERSION }} 
100+ 
101+       - uses : actions/download-artifact@v4 
102+         with :
103+           name : dist 
104+           path : ${{ inputs.working-directory }}/dist/ 
105+ 
106+       - name : Publish package distributions to PyPI 
107+         uses : pypa/gh-action-pypi-publish@release/v1 
108+         with :
109+           packages-dir : ${{ inputs.working-directory }}/dist/ 
110+           verbose : true 
111+           print-hash : true 
112+           #  Temp workaround since attestations are on by default as of gh-action-pypi-publish v1.11.0
113+           attestations : false 
114+ 
115+   mark-release :
116+     needs :
117+       - build 
118+       - publish 
119+     runs-on : ubuntu-latest 
120+     permissions :
121+       #  This permission is needed by `ncipollo/release-action` to
122+       #  create the GitHub release.
123+       contents : write 
124+ 
125+     defaults :
126+       run :
127+         working-directory : ${{ inputs.working-directory }} 
128+ 
129+     steps :
130+       - uses : actions/checkout@v4 
131+ 
132+       - name : Set up Python + uv 
133+         uses : " ./.github/actions/uv_setup" 
134+         with :
135+           python-version : ${{ env.PYTHON_VERSION }} 
136+ 
137+       - uses : actions/download-artifact@v4 
138+         with :
139+           name : dist 
140+           path : ${{ inputs.working-directory }}/dist/ 
141+ 
142+       - name : Create Tag 
143+         uses : ncipollo/release-action@v1 
144+         with :
145+           artifacts : " dist/*" 
146+           token : ${{ secrets.GITHUB_TOKEN }} 
147+           generateReleaseNotes : true 
148+           tag : ${{needs.build.outputs.pkg-name}}==${{ needs.build.outputs.version }} 
149+           body : ${{ needs.release-notes.outputs.release-body }} 
150+           commit : main 
151+           makeLatest : true 
0 commit comments