-
Notifications
You must be signed in to change notification settings - Fork 26
203 lines (177 loc) · 7.02 KB
/
python-package.yml
File metadata and controls
203 lines (177 loc) · 7.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
name: Build, validate & Release
# Usage:
# - For PRs: this workflow runs automatically to validate the package builds and installs correctly on multiple Python versions. No artifacts are published for PRs.
# - For releases: when you push a tag like v1.2.3, this workflow runs the full matrix validation, then builds the release artifacts, and finally publishes to PyPI if all checks pass.
# - For manual re-runs: use "Run workflow" and provide a tag-like value such as v2.0.0rc1.
on:
workflow_dispatch:
inputs:
release_tag:
description: 'Tag to build/publish (e.g. v1.2.3 or v2.0.0rc1)'
required: true
type: string
# Release pipeline: run only when pushing a version-like tag (e.g. v1.2.3)
# Test pipeline: run tests on main/master pushes & pull requests AND tags.
push:
tags:
- "v*.*.*"
branches:
- main
- master
# Validation pipeline: run on PRs targeting main/master (no publishing)
pull_request:
branches: [main, master]
types: [opened, edited, synchronize, reopened]
# This workflow only needs to read repo contents
permissions:
contents: read
jobs:
test_matrix:
# PR + tag validation: ensure the project builds and installs on multiple Pythons
name: Test install & smoke (Py ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
# Run all versions even if one fails (helps spot version-specific issues)
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Validate manual tag input
if: ${{ github.event_name == 'workflow_dispatch' }}
shell: bash
run: |
case "${{ inputs.release_tag }}" in
v*.*.*)
echo "Manual release tag accepted: ${{ inputs.release_tag }}"
;;
*)
echo "release_tag must look like v1.2.3 (or similar, e.g. v2.0.0rc1)"
exit 1
;;
esac
- name: Checkout sources
uses: actions/checkout@v6
with:
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.release_tag) || github.ref }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install Qt/OpenGL runtime deps (Ubuntu)
run: |
sudo apt-get update
sudo apt-get install -y \
libegl1 \
libgl1 \
libopengl0 \
libxkbcommon-x11-0 \
libxcb-cursor0
# Install packaging toolchain:
# - build: creates wheel + sdist
# - twine: validates metadata and can upload (upload only happens in publish job)
- name: Install build tools
run: python -m pip install -U pip build twine
# Build distributions just to verify packaging config works on this Python
- name: Build (for validation only)
run: python -m build
# Validate dist metadata (README rendering, required fields, etc.)
- name: Twine check
run: python -m twine check dist/*
# Smoke test: install the built wheel and verify the package imports
- name: Install from wheel & smoke test
run: |
WHEEL=$(ls -1 dist/*.whl | head -n 1)
echo "Using wheel: $WHEEL"
python -m pip install \
--extra-index-url https://download.pytorch.org/whl/cpu \
"deeplabcut-live-gui[pytorch] @ file://$(pwd)/${WHEEL}"
python -c "import dlclivegui; print('Imported dlclivegui OK')"
QT_QPA_PLATFORM=offscreen dlclivegui --help
build_release:
# Tag-only build: produce the "official" release artifacts once matrix passed
name: Build release artifacts
runs-on: ubuntu-latest
needs: test_matrix
# Safety gate: only run for version tags, never for PRs/branches
if: ${{ startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
steps:
- name: Validate manual tag input
if: ${{ github.event_name == 'workflow_dispatch' }}
shell: bash
run: |
case "${{ inputs.release_tag }}" in
v*.*.*)
echo "Manual release tag accepted: ${{ inputs.release_tag }}"
;;
*)
echo "release_tag must look like v1.2.3 (or similar, e.g. v2.0.0rc1)"
exit 1
;;
esac
# Fetch sources for the tagged revision
- name: Checkout sources
uses: actions/checkout@v6
with:
# For a manual run, we want to check out the commit at the provided tag
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.release_tag) || github.ref }}
# Use a single, modern Python for the canonical release build
- name: Set up Python (release build)
uses: actions/setup-python@v6
with:
python-version: "3.12"
# Install build + validation tooling
- name: Install build tools
run: python -m pip install -U pip build twine
# Produce both sdist and wheel in dist/
- name: Build distributions
run: python -m build
# Re-check metadata on the final artifacts we intend to publish
- name: Twine check
run: python -m twine check dist/*
# Store dist/ outputs so the publish job uploads exactly what we built here
- name: Upload dist artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/*
publish:
# Tag-only publish: download built artifacts and upload them to PyPI
name: Publish to PyPI (API token)
runs-on: ubuntu-latest
needs: build_release
# Safety gate: only run for version tags
if: ${{ startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
steps:
# Retrieve the exact distributions produced in build_release
- name: Download dist artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist
# Set up Python (only needed to run Twine)
- name: Set up Python (publish)
uses: actions/setup-python@v6
with:
python-version: "3.12"
# Install twine for uploading
- name: Install Twine
run: python -m pip install -U twine
# Check that the PyPI API token is present before attempting upload (fails fast if not set)
- name: Check PyPI credential presence
shell: bash
env:
TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }}
run: |
if [ -z "$TWINE_PASSWORD" ]; then
echo "TWINE_PASSWORD is empty"
exit 1
else
echo "TWINE_PASSWORD is present"
fi
# Upload to PyPI using an API token stored in repo secrets.
# --skip-existing avoids failing if you re-run a workflow for the same version.
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }}
run: python -m twine upload --non-interactive --skip-existing dist/*