Skip to content

Commit 972bcf0

Browse files
committed
[build] Add job to publish package to PyPI
1 parent fd652a0 commit 972bcf0

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
name: Publish PyPI Package
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: 'Tag To Publish'
8+
required: true
9+
environment:
10+
description: 'PyPI Environment'
11+
required: true
12+
type: choice
13+
options:
14+
- test
15+
- release
16+
default: 'test'
17+
18+
jobs:
19+
verify:
20+
name: Verify Build
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Check workflows
24+
uses: actions/github-script@v6
25+
with:
26+
script: |
27+
const { owner, repo } = context.repo;
28+
const tag = "${{ github.event.inputs.tag }}";
29+
const requiredWorkflows = ['Windows Distribution', 'Python Distribution'];
30+
let workflowConclusions = {};
31+
32+
console.log(`Checking for successful workflow runs for tag: ${tag}`);
33+
34+
const { data: response } = await github.rest.actions.listWorkflowRunsForRepo({
35+
owner,
36+
repo,
37+
event: 'push',
38+
});
39+
40+
const runsForTag = response.workflow_runs.filter(run => run.head_branch === tag);
41+
42+
for (const run of runsForTag) {
43+
if (requiredWorkflows.includes(run.name)) {
44+
if (!workflowConclusions[run.name] || new Date(run.created_at) > new Date(workflowConclusions[run.name].created_at)) {
45+
workflowConclusions[run.name] = {
46+
conclusion: run.conclusion,
47+
created_at: run.created_at,
48+
html_url: run.html_url,
49+
};
50+
}
51+
}
52+
}
53+
54+
let allSuccess = true;
55+
for (const workflowName of requiredWorkflows) {
56+
if (!workflowConclusions[workflowName]) {
57+
core.setFailed(`Workflow "${workflowName}" was not found for tag ${tag}.`);
58+
allSuccess = false;
59+
} else if (workflowConclusions[workflowName].conclusion !== 'success') {
60+
core.setFailed(`Workflow "${workflowName}" did not succeed for tag ${tag}. Conclusion was "${workflowConclusions[workflowName].conclusion}". See: ${workflowConclusions[workflowName].html_url}`);
61+
allSuccess = false;
62+
} else {
63+
console.log(`✅ Workflow "${workflowName}" succeeded for tag ${tag}.`);
64+
}
65+
}
66+
67+
if (!allSuccess) {
68+
throw new Error("One or more required build workflows did not succeed.");
69+
}
70+
71+
publish:
72+
name: Publishing DVR-Scan to ${{ github.event.inputs.environment }} PyPI
73+
runs-on: ubuntu-latest
74+
needs: verify
75+
76+
environment:
77+
name: ${{ github.event.inputs.environment == 'test' && 'test' || 'release' }}
78+
url: ${{ github.event.inputs.environment == 'test' && 'https://test.pypi.org/p/dvr-scan' || 'https://pypi.org/p/dvr-scan' }}
79+
80+
permissions:
81+
id-token: write # IMPORTANT: mandatory for trusted publishing
82+
83+
steps:
84+
- name: Checkout ${{ github.event.inputs.tag }}
85+
uses: actions/checkout@v3
86+
with:
87+
ref: ${{ github.event.inputs.tag }}
88+
89+
- name: Set up Python
90+
uses: actions/setup-python@v3
91+
with:
92+
python-version: "3.x"
93+
94+
- name: Install Dependencies
95+
run: |
96+
python -m pip install --upgrade pip
97+
pip install build twine
98+
99+
- name: Build Package
100+
run: |
101+
python dist/pre_release.py
102+
python -m build
103+
mkdir pkg
104+
mv dist/*.tar.gz pkg/
105+
mv dist/*.whl pkg/
106+
107+
- name: Upload Package
108+
uses: actions/upload-artifact@v4
109+
with:
110+
name: dvr-scan-dist
111+
path: |
112+
pkg/*.tar.gz
113+
pkg/*.whl
114+
115+
- name: Publish Package
116+
uses: pypa/gh-action-pypi-publish@release/v1
117+
with:
118+
repository-url: ${{ github.event.inputs.environment == 'test' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }}
119+
packages-dir: pkg/
120+
print-hash: true
121+
122+
123+
publish-headless:
124+
name: Publishing DVR-Scan-Headless to ${{ github.event.inputs.environment }} PyPI
125+
runs-on: ubuntu-latest
126+
needs: verify
127+
128+
environment:
129+
name: ${{ github.event.inputs.environment == 'test' && 'test' || 'release' }}
130+
url: ${{ github.event.inputs.environment == 'test' && 'https://test.pypi.org/p/dvr-scan-headless' || 'https://pypi.org/p/dvr-scan-headless' }}
131+
132+
permissions:
133+
id-token: write # IMPORTANT: mandatory for trusted publishing
134+
135+
steps:
136+
- name: Checkout ${{ github.event.inputs.tag }}
137+
uses: actions/checkout@v3
138+
with:
139+
ref: ${{ github.event.inputs.tag }}
140+
141+
- name: Set up Python
142+
uses: actions/setup-python@v5
143+
with:
144+
python-version: 3.13
145+
cache: 'pip'
146+
147+
- name: Install uv and set the python version
148+
uses: astral-sh/setup-uv@v4
149+
with:
150+
version: "0.5.11"
151+
python-version: 3.13
152+
153+
154+
- name: Install Dependencies
155+
run: |
156+
uv pip install --upgrade wheel virtualenv build twine
157+
uv pip install opencv-python-headless opencv-contrib-python-headless --only-binary :all:
158+
uv pip install -r requirements_headless.txt -r docs/requirements.txt
159+
160+
- name: Build Package
161+
run: |
162+
python -m build
163+
mkdir pkg_headless
164+
mv dist/*.tar.gz pkg_headless/
165+
mv dist/*.whl pkg_headless/
166+
167+
- name: Upload Package
168+
uses: actions/upload-artifact@v4
169+
with:
170+
name: dvr-scan-headless-dist
171+
path: |
172+
pkg_headless/*.tar.gz
173+
pkg_headless/*.whl
174+
175+
- name: Publish Package
176+
uses: pypa/gh-action-pypi-publish@release/v1
177+
with:
178+
repository-url: ${{ github.event.inputs.environment == 'test' && 'https://test.pypi.org/legacy/' || 'https://upload.pypi.org/legacy/' }}
179+
packages-dir: pkg_headless/
180+
print-hash: true

0 commit comments

Comments
 (0)