Skip to content

Commit 3807c62

Browse files
authored
Merge pull request #55 from DeepLabCut/cy/final-deploy-workflow-2.0.0rc1
Deploy workflow candidate for 2.0.0rc1
2 parents fd605f8 + 398b7c6 commit 3807c62

File tree

4 files changed

+117
-72
lines changed

4 files changed

+117
-72
lines changed
Lines changed: 94 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,42 @@
11
name: Build, validate & Release
22

3+
# Usage:
4+
# - For PRs: this workflow runs automatically to validate the package builds and installs correctly on multiple Python versions. No artifacts are published for PRs.
5+
# - 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.
6+
37
on:
8+
# Release pipeline: run only when pushing a version-like tag (e.g. v1.2.3)
49
push:
5-
tags: [ 'v*.*.*' ]
10+
tags:
11+
- "v*.*.*"
12+
13+
# Validation pipeline: run on PRs targeting main/master (no publishing)
614
pull_request:
7-
branches: [ main, master ]
8-
types: [ labeled, opened, edited, synchronize, reopened ]
15+
branches: [main, master]
16+
types: [opened, edited, synchronize, reopened]
17+
18+
# This workflow only needs to read repo contents
19+
permissions:
20+
contents: read
921

1022
jobs:
11-
test:
12-
name: Test / smoke (matrix)
23+
test_matrix:
24+
# PR + tag validation: ensure the project builds and installs on multiple Pythons
25+
name: Test install & smoke (Py ${{ matrix.python-version }})
1326
runs-on: ubuntu-latest
1427
strategy:
28+
# Run all versions even if one fails (helps spot version-specific issues)
1529
fail-fast: false
1630
matrix:
17-
python-version: [ "3.10", "3.11", "3.12" ]
31+
python-version: ["3.10", "3.11", "3.12"]
32+
1833
steps:
19-
- uses: actions/checkout@v6
20-
- uses: actions/setup-python@v6
34+
# Fetch repository sources so we can build/test
35+
- name: Checkout sources
36+
uses: actions/checkout@v6
37+
38+
- name: Set up Python
39+
uses: actions/setup-python@v6
2140
with:
2241
python-version: ${{ matrix.python-version }}
2342

@@ -31,82 +50,99 @@ jobs:
3150
libxkbcommon-x11-0 \
3251
libxcb-cursor0
3352
34-
- name: Install tools
35-
run: |
36-
python -m pip install --upgrade pip
37-
python -m pip install build twine wheel "packaging>=24.2"
53+
# Install packaging toolchain:
54+
# - build: creates wheel + sdist
55+
# - twine: validates metadata and can upload (upload only happens in publish job)
56+
- name: Install build tools
57+
run: python -m pip install -U pip build twine
3858

39-
- name: Build distributions (sdist + wheel)
59+
# Build distributions just to verify packaging config works on this Python
60+
- name: Build (for validation only)
4061
run: python -m build
4162

42-
- name: Inspect dist
43-
run: |
44-
ls -lah dist/
45-
echo "sdist contents (first ~200 entries):"
46-
tar -tf dist/*.tar.gz | sed -n '1,200p'
47-
48-
- name: Twine metadata & README check
63+
# Validate dist metadata (README rendering, required fields, etc.)
64+
- name: Twine check
4965
run: python -m twine check dist/*
5066

67+
# Smoke test: install the built wheel and verify the package imports
5168
- name: Install from wheel & smoke test
5269
run: |
53-
python -m pip install dist/*.whl
54-
python - <<'PY'
55-
import importlib
56-
pkg_name = "dlclivegui"
57-
m = importlib.import_module(pkg_name)
58-
print("Imported:", m.__name__, "version:", getattr(m, "__version__", "n/a"))
59-
PY
60-
61-
if ! command -v dlclivegui >/dev/null 2>&1; then
62-
echo "CLI entry point 'dlclivegui' not found in PATH; skipping CLI smoke test."
63-
else
64-
if command -v dlclivegui >/dev/null 2>&1; then
65-
echo "Running 'dlclivegui --help' smoke test..."
66-
if ! dlclivegui --help >/dev/null 2>&1; then
67-
echo "::error::'dlclivegui --help' failed; this indicates a problem with the installed CLI package."
68-
exit 1
69-
fi
70-
71-
build:
72-
name: Build release artifacts (single)
70+
WHEEL=$(ls -1 dist/*.whl | head -n 1)
71+
echo "Using wheel: $WHEEL"
72+
python -m pip install \
73+
--extra-index-url https://download.pytorch.org/whl/cpu \
74+
"deeplabcut-live-gui[pytorch] @ file://$(pwd)/${WHEEL}"
75+
python -c "import dlclivegui; print('Imported dlclivegui OK')"
76+
QT_QPA_PLATFORM=offscreen dlclivegui --help
77+
78+
build_release:
79+
# Tag-only build: produce the "official" release artifacts once matrix passed
80+
name: Build release artifacts
7381
runs-on: ubuntu-latest
74-
needs: test
75-
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
82+
needs: test_matrix
83+
# Safety gate: only run for version tags, never for PRs/branches
84+
if: startsWith(github.ref, 'refs/tags/v')
85+
7686
steps:
77-
- uses: actions/checkout@v6
78-
- uses: actions/setup-python@v6
87+
# Fetch sources for the tagged revision
88+
- name: Checkout sources
89+
uses: actions/checkout@v6
90+
91+
# Use a single, modern Python for the canonical release build
92+
- name: Set up Python (release build)
93+
uses: actions/setup-python@v6
7994
with:
8095
python-version: "3.12"
8196

82-
- name: Build distributions (sdist + wheel)
83-
run: |
84-
python -m pip install --upgrade pip
85-
python -m pip install build twine wheel "packaging>=24.2"
86-
python -m build
87-
python -m twine check dist/*
97+
# Install build + validation tooling
98+
- name: Install build tools
99+
run: python -m pip install -U pip build twine
100+
101+
# Produce both sdist and wheel in dist/
102+
- name: Build distributions
103+
run: python -m build
104+
105+
# Re-check metadata on the final artifacts we intend to publish
106+
- name: Twine check
107+
run: python -m twine check dist/*
88108

109+
# Store dist/ outputs so the publish job uploads exactly what we built here
89110
- name: Upload dist artifacts
90111
uses: actions/upload-artifact@v4
91112
with:
92113
name: dist
93114
path: dist/*
94-
if-no-files-found: error
95115

96116
publish:
97-
name: Publish to PyPI (OIDC)
117+
# Tag-only publish: download built artifacts and upload them to PyPI
118+
name: Publish to PyPI (API token)
98119
runs-on: ubuntu-latest
99-
needs: build
100-
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
101-
environment: pypi
102-
permissions:
103-
id-token: write
120+
needs: build_release
121+
# Safety gate: only run for version tags
122+
if: startsWith(github.ref, 'refs/tags/v')
123+
104124
steps:
125+
# Retrieve the exact distributions produced in build_release
105126
- name: Download dist artifacts
106127
uses: actions/download-artifact@v4
107128
with:
108129
name: dist
109130
path: dist
110131

132+
# Set up Python (only needed to run Twine)
133+
- name: Set up Python (publish)
134+
uses: actions/setup-python@v6
135+
with:
136+
python-version: "3.x"
137+
138+
# Install twine for uploading
139+
- name: Install Twine
140+
run: python -m pip install -U twine
141+
142+
# Upload to PyPI using an API token stored in repo secrets.
143+
# --skip-existing avoids failing if you re-run a workflow for the same version.
111144
- name: Publish to PyPI
112-
uses: pypa/gh-action-pypi-publish@release/v1
145+
env:
146+
TWINE_USERNAME: __token__
147+
TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }}
148+
run: python -m twine upload --non-interactive --verbose --skip-existing dist/*

.github/workflows/testing-ci.yml

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,26 @@ jobs:
5555
5656
- name: Run tests (exclude hardware) with coverage via tox
5757
run: |
58-
tox -q
58+
tox -q | tee tox-output.log
59+
5960
6061
- name: Append Coverage Summary to Job
61-
if: always()
62+
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12'
6263
shell: bash
6364
run: |
64-
python -m pip install -U coverage
6565
echo "## Coverage Summary" >> "$GITHUB_STEP_SUMMARY"
66-
echo "" >> "$GITHUB_STEP_SUMMARY"
6766
echo '```text' >> "$GITHUB_STEP_SUMMARY"
68-
python -m coverage report -m >> "$GITHUB_STEP_SUMMARY" || true
67+
awk '
68+
/^Name[[:space:]]+Stmts[[:space:]]+Miss/ {p=1}
69+
p==1 {print}
70+
/^Coverage XML written to file/ {exit}
71+
' tox-output.log >> "$GITHUB_STEP_SUMMARY" || true
6972
echo '```' >> "$GITHUB_STEP_SUMMARY"
7073
71-
- name: Upload coverage to Codecov # NOTE : may need to disable this if token is not setup
72-
if: github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'master')
74+
- name: Upload coverage to Codecov
75+
if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12' && github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'master')
7376
uses: codecov/codecov-action@v5
7477
with:
7578
token: ${{ secrets.CODECOV_TOKEN }}
76-
files: ./coverage.xml
77-
fail_ci_if_error: false
79+
files: ./.coverage.py312.xml
80+
fail_ci_if_error: true

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ omit = [
139139
branch = true
140140
omit = [
141141
"*/__pycache__/*",
142-
"*/site-packages/*",
142+
# "*/site-packages/*",
143+
# breaks CI coverage reporting as it excludes our own installed package
143144
]
144145
source = [ "dlclivegui", "tests" ]

tox.ini

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ description = Unit + smoke tests (exclude hardware) with coverage
1111
package = wheel
1212
extras = test
1313

14-
# Keep behavior aligned with your GitHub Actions job:
15-
commands =
16-
pytest -m "not hardware" --maxfail=1 --disable-warnings \
17-
--cov=dlclivegui --cov-report=xml --cov-report=term-missing {posargs}
1814

1915
# Helpful defaults for headless CI runs (Qt/OpenCV):
2016
setenv =
@@ -23,6 +19,15 @@ setenv =
2319
QT_OPENGL = software
2420
# Can help avoid some Windows/OpenCV capture backend flakiness when tests touch video I/O:
2521
OPENCV_VIDEOIO_PRIORITY_MSMF = 0
22+
COVERAGE_FILE = {toxinidir}/.coverage.{envname}
23+
24+
# Keep behavior aligned with your GitHub Actions job:
25+
commands =
26+
pytest -m "not hardware" --maxfail=1 --disable-warnings \
27+
--cov={envsitepackagesdir}/dlclivegui \
28+
--cov-report=xml:{toxinidir}/.coverage.{envname}.xml \
29+
--cov-report=term-missing \
30+
{posargs}
2631

2732
# Let CI variables pass through (useful for debugging and some GUI/headless setups):
2833
passenv =

0 commit comments

Comments
 (0)