From 6e59a94e1192c6657b2d7dfe549274506cfcdf26 Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Tue, 24 Feb 2026 17:08:16 +0100 Subject: [PATCH 1/8] Revert "Update python-package.yml" This reverts commit 09a836442768430117531309fd57af90c1d5bea1. --- .github/workflows/python-package.yml | 75 ++++++++++++---------------- 1 file changed, 33 insertions(+), 42 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 5d8a312..7dd1965 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -2,22 +2,26 @@ name: Build, validate & Release on: push: - tags: [ 'v*.*.*' ] + tags: + - 'v*.*.*' pull_request: branches: [ main, master ] types: [ labeled, opened, edited, synchronize, reopened ] jobs: - test: - name: Test / smoke (matrix) + build_check: + name: Build & validate package runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: [ "3.10", "3.11", "3.12" ] + python-version: [ "3.10", "3.11", "3.12" ] # adjust to what you support steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -50,63 +54,50 @@ jobs: - name: Install from wheel & smoke test run: | + # Install from the built wheel (not from the source tree) python -m pip install dist/*.whl + python - <<'PY' import importlib - pkg_name = "dlclivegui" + pkg_name = "dlclivegui" # change if your top-level import differs m = importlib.import_module(pkg_name) print("Imported:", m.__name__, "version:", getattr(m, "__version__", "n/a")) PY if ! command -v dlclivegui >/dev/null 2>&1; then - echo "CLI entry point 'dlclivegui' not found in PATH; skipping CLI smoke test." + echo "CLI entry point 'dlclivegui' not found in PATH; skipping CLI smoke test." else - if command -v dlclivegui >/dev/null 2>&1; then echo "Running 'dlclivegui --help' smoke test..." if ! dlclivegui --help >/dev/null 2>&1; then echo "::error::'dlclivegui --help' failed; this indicates a problem with the installed CLI package." exit 1 fi + fi - build: - name: Build release artifacts (single) + publish: + name: Publish to PyPI runs-on: ubuntu-latest - needs: test - if: ${{ startsWith(github.ref, 'refs/tags/v') }} + needs: build_check + if: ${{ startsWith(github.ref, 'refs/tags/v') }} # only on tag pushes like v1.2.3 steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Python + uses: actions/setup-python@v6 with: - python-version: "3.12" + python-version: "3.x" - - name: Build distributions (sdist + wheel) + - name: Install build tools run: | python -m pip install --upgrade pip - python -m pip install build twine wheel "packaging>=24.2" - python -m build - python -m twine check dist/* - - - name: Upload dist artifacts - uses: actions/upload-artifact@v4 - with: - name: dist - path: dist/* - if-no-files-found: error + python -m pip install build twine - publish: - name: Publish to PyPI (OIDC) - runs-on: ubuntu-latest - needs: build - if: ${{ startsWith(github.ref, 'refs/tags/v') }} - environment: pypi - permissions: - id-token: write - steps: - - name: Download dist artifacts - uses: actions/download-artifact@v4 - with: - name: dist - path: dist + - name: Build distributions (sdist + wheel) + run: python -m build - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }} + run: python -m twine upload --verbose dist/* From 853075e2d42c3fd9453e8389820182c3d567f482 Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Tue, 24 Feb 2026 17:14:47 +0100 Subject: [PATCH 2/8] Refactor Python GitHub Actions workflow Simplify and reorganize the CI workflow: normalize tag pattern quoting and narrow pull_request branches/types. Add permissions.contents=read. Rename the main job to a test_matrix job that runs a Python matrix, streamline setup and pip installs, remove verbose cache steps (use setup-python cache option), and simplify build/twine checks and wheel smoke test (only imports the package; CLI smoke test removed). Add a separate build_release job (runs on tag pushes) to build distributions and upload them as artifacts. Update publish job to download artifacts, install twine, and upload to PyPI using non-interactive --skip-existing; make publish depend on build_release. --- .github/workflows/python-package.yml | 101 ++++++++++++++------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 7dd1965..ad4874e 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -3,25 +3,25 @@ name: Build, validate & Release on: push: tags: - - 'v*.*.*' + - "v*.*.*" pull_request: - branches: [ main, master ] - types: [ labeled, opened, edited, synchronize, reopened ] + branches: [main, master] + types: [opened, edited, synchronize, reopened] + +permissions: + contents: read jobs: - build_check: - name: Build & validate package + test_matrix: + name: Test install & smoke (Py ${{ matrix.python-version }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: - python-version: [ "3.10", "3.11", "3.12" ] # adjust to what you support + python-version: ["3.10", "3.11", "3.12"] steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v6 + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -39,65 +39,70 @@ jobs: run: | python -m pip install --upgrade pip python -m pip install build twine wheel "packaging>=24.2" + cache: pip - - name: Build distributions (sdist + wheel) + - name: Build (for validation only) run: python -m build - - name: Inspect dist - run: | - ls -lah dist/ - echo "sdist contents (first ~200 entries):" - tar -tf dist/*.tar.gz | sed -n '1,200p' - - - name: Twine metadata & README check + - name: Twine check run: python -m twine check dist/* - name: Install from wheel & smoke test run: | - # Install from the built wheel (not from the source tree) python -m pip install dist/*.whl - python - <<'PY' import importlib - pkg_name = "dlclivegui" # change if your top-level import differs - m = importlib.import_module(pkg_name) - print("Imported:", m.__name__, "version:", getattr(m, "__version__", "n/a")) + m = importlib.import_module("dlclivegui") + print("Imported:", m.__name__) PY - if ! command -v dlclivegui >/dev/null 2>&1; then - echo "CLI entry point 'dlclivegui' not found in PATH; skipping CLI smoke test." - else - echo "Running 'dlclivegui --help' smoke test..." - if ! dlclivegui --help >/dev/null 2>&1; then - echo "::error::'dlclivegui --help' failed; this indicates a problem with the installed CLI package." - exit 1 - fi - fi + build_release: + name: Build release artifacts + runs-on: ubuntu-latest + needs: test_matrix + if: startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install build tools + run: python -m pip install -U pip build twine + + - name: Build distributions + run: python -m build + + - name: Twine check + run: python -m twine check dist/* + + - name: Upload dist artifacts + uses: actions/upload-artifact@v4 + with: + name: dist + path: dist/* publish: - name: Publish to PyPI + name: Publish to PyPI (API token) runs-on: ubuntu-latest - needs: build_check - if: ${{ startsWith(github.ref, 'refs/tags/v') }} # only on tag pushes like v1.2.3 + needs: build_release + if: startsWith(github.ref, 'refs/tags/v') steps: - - name: Checkout - uses: actions/checkout@v6 + - name: Download dist artifacts + uses: actions/download-artifact@v4 + with: + name: dist + path: dist - - name: Setup Python - uses: actions/setup-python@v6 + - uses: actions/setup-python@v6 with: python-version: "3.x" - - name: Install build tools - run: | - python -m pip install --upgrade pip - python -m pip install build twine - - - name: Build distributions (sdist + wheel) - run: python -m build + - name: Install Twine + run: python -m pip install -U twine - name: Publish to PyPI env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.TWINE_API_KEY }} - run: python -m twine upload --verbose dist/* + run: python -m twine upload --non-interactive --verbose --skip-existing dist/* From ac3a35b4856a8fde6e90a8be2bf864d81d7b9414 Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Tue, 24 Feb 2026 17:18:04 +0100 Subject: [PATCH 3/8] Improve GitHub Actions Python release workflow Split the workflow into validation and release paths and add clearer step names and safeguards. PRs against main/master now run a validation matrix (3.10, 3.11, 3.12) that builds the package, runs twine check, and smoke-tests installing the wheel; fail-fast is disabled to surface version-specific issues. Tag pushes matching v*.*.* trigger a canonical release build (single Python 3.12) that produces dist artifacts, uploads them as workflow artifacts, and a separate publish job downloads those artifacts and uploads them to PyPI. Other improvements: minimal permissions (contents: read), explicit checkout/setup steps, pip caching enabled, comments for clarity, and use of --skip-existing when uploading to PyPI. --- .github/workflows/python-package.yml | 59 +++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index ad4874e..6de28b5 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -1,27 +1,42 @@ 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. + on: + # Release pipeline: run only when pushing a version-like tag (e.g. v1.2.3) push: tags: - "v*.*.*" + + # 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: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 + # Fetch repository sources so we can build/test + - name: Checkout sources + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -35,18 +50,21 @@ jobs: libxkbcommon-x11-0 \ libxcb-cursor0 - - name: Install tools - run: | - python -m pip install --upgrade pip - python -m pip install build twine wheel "packaging>=24.2" - cache: pip + # 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: | python -m pip install dist/*.whl @@ -57,25 +75,37 @@ jobs: PY 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') + steps: - - uses: actions/checkout@v6 - - uses: actions/setup-python@v6 + # Fetch sources for the tagged revision + - name: Checkout sources + uses: actions/checkout@v6 + + # 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: @@ -83,24 +113,33 @@ jobs: 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') + steps: + # Retrieve the exact distributions produced in build_release - name: Download dist artifacts uses: actions/download-artifact@v4 with: name: dist path: dist - - uses: actions/setup-python@v6 + # Set up Python (only needed to run Twine) + - name: Set up Python (publish) + uses: actions/setup-python@v6 with: python-version: "3.x" + # Install twine for uploading - name: Install Twine run: python -m pip install -U twine + # 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__ From 58794d574b5516960fe19ce314fe8bd05358f512 Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Wed, 25 Feb 2026 13:41:08 +0100 Subject: [PATCH 4/8] Adjust CI coverage reporting & tox coverage config Capture tox output to a file and append the coverage summary to the GitHub Actions job summary only for the ubuntu-latest / Python 3.12 matrix. Restrict Codecov uploads to that same matrix for PRs targeting main/master and point the upload at a per-environment coverage XML (coverage.py312.xml). Move coverage settings into tox.ini so tests generate env-specific .coverage files (COVERAGE_FILE) and XML reports ({toxinidir}/.coverage.{envname}.xml) for more reliable CI artifact handling. --- .github/workflows/testing-ci.yml | 17 ++++++++--------- tox.ini | 13 +++++++++---- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/.github/workflows/testing-ci.yml b/.github/workflows/testing-ci.yml index 07c8af0..df87342 100644 --- a/.github/workflows/testing-ci.yml +++ b/.github/workflows/testing-ci.yml @@ -55,23 +55,22 @@ jobs: - name: Run tests (exclude hardware) with coverage via tox run: | - tox -q + tox -q | tee tox-output.log - name: Append Coverage Summary to Job - if: always() + if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12' shell: bash run: | - python -m pip install -U coverage echo "## Coverage Summary" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" echo '```text' >> "$GITHUB_STEP_SUMMARY" - python -m coverage report -m >> "$GITHUB_STEP_SUMMARY" || true + # Pull the coverage table from tox output: + sed -n '/^Name\s\+Stmts\s\+Miss\s\+Cover/,$p' tox-output.txt >> "$GITHUB_STEP_SUMMARY" || true echo '```' >> "$GITHUB_STEP_SUMMARY" - - name: Upload coverage to Codecov # NOTE : may need to disable this if token is not setup - if: github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'master') + - name: Upload coverage to Codecov + if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12' && github.event_name == 'pull_request' && (github.base_ref == 'main' || github.base_ref == 'master') uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.xml - fail_ci_if_error: false + files: ./coverage.py312.xml + fail_ci_if_error: true diff --git a/tox.ini b/tox.ini index 3344818..e7c04dd 100644 --- a/tox.ini +++ b/tox.ini @@ -11,10 +11,6 @@ description = Unit + smoke tests (exclude hardware) with coverage package = wheel extras = test -# Keep behavior aligned with your GitHub Actions job: -commands = - pytest -m "not hardware" --maxfail=1 --disable-warnings \ - --cov=dlclivegui --cov-report=xml --cov-report=term-missing {posargs} # Helpful defaults for headless CI runs (Qt/OpenCV): setenv = @@ -23,6 +19,15 @@ setenv = QT_OPENGL = software # Can help avoid some Windows/OpenCV capture backend flakiness when tests touch video I/O: OPENCV_VIDEOIO_PRIORITY_MSMF = 0 + COVERAGE_FILE = {toxinidir}/.coverage.{envname} + +# Keep behavior aligned with your GitHub Actions job: +commands = + pytest -m "not hardware" --maxfail=1 --disable-warnings \ + --cov=dlclivegui + --cov-report=xml:{toxinidir}/.coverage.{envname}.xml + --cov-report=term-missing + {posargs} # Let CI variables pass through (useful for debugging and some GUI/headless setups): passenv = From a7ef2740b38df44227fcf49f635b6e01fa50f734 Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Wed, 25 Feb 2026 13:49:19 +0100 Subject: [PATCH 5/8] Fix CI coverage reporting and cov config Update GitHub Actions and tox/pyproject coverage settings so coverage output is picked up reliably. Read the tox output from tox-output.log and point the codecov action at ./.coverage.py312.xml. Stop excluding site-packages in pyproject (commented out) because it caused our installed package to be omitted from reports. Also tidy tox.ini coverage command formatting and ensure XML/term reports are emitted per envname. --- .github/workflows/testing-ci.yml | 4 ++-- pyproject.toml | 3 ++- tox.ini | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/testing-ci.yml b/.github/workflows/testing-ci.yml index df87342..3a6a3db 100644 --- a/.github/workflows/testing-ci.yml +++ b/.github/workflows/testing-ci.yml @@ -64,7 +64,7 @@ jobs: echo "## Coverage Summary" >> "$GITHUB_STEP_SUMMARY" echo '```text' >> "$GITHUB_STEP_SUMMARY" # Pull the coverage table from tox output: - sed -n '/^Name\s\+Stmts\s\+Miss\s\+Cover/,$p' tox-output.txt >> "$GITHUB_STEP_SUMMARY" || true + sed -n '/^Name\s\+Stmts\s\+Miss\s\+Cover/,$p' tox-output.log >> "$GITHUB_STEP_SUMMARY" || true echo '```' >> "$GITHUB_STEP_SUMMARY" - name: Upload coverage to Codecov @@ -72,5 +72,5 @@ jobs: uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.py312.xml + files: ./.coverage.py312.xml fail_ci_if_error: true diff --git a/pyproject.toml b/pyproject.toml index bb84eac..02e8a96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,6 +139,7 @@ omit = [ branch = true omit = [ "*/__pycache__/*", - "*/site-packages/*", + # "*/site-packages/*", + # breaks CI coverage reporting as it excludes our own installed package ] source = [ "dlclivegui", "tests" ] diff --git a/tox.ini b/tox.ini index e7c04dd..7a9eaca 100644 --- a/tox.ini +++ b/tox.ini @@ -24,9 +24,9 @@ setenv = # Keep behavior aligned with your GitHub Actions job: commands = pytest -m "not hardware" --maxfail=1 --disable-warnings \ - --cov=dlclivegui - --cov-report=xml:{toxinidir}/.coverage.{envname}.xml - --cov-report=term-missing + --cov=dlclivegui \ + --cov-report=xml:{toxinidir}/.coverage.{envname}.xml \ + --cov-report=term-missing \ {posargs} # Let CI variables pass through (useful for debugging and some GUI/headless setups): From 02d385eb4304578381b0b714b205fa2673a644cc Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Wed, 25 Feb 2026 13:57:08 +0100 Subject: [PATCH 6/8] Use envsitepackagesdir in coverage path Update tox.ini to set pytest --cov to {envsitepackagesdir}/dlclivegui instead of the project module name. This ensures coverage collects from the package installed into the tox environment (matching the GitHub Actions job) and avoids missing coverage when the package is not imported from the project root. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7a9eaca..d6f8d86 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ setenv = # Keep behavior aligned with your GitHub Actions job: commands = pytest -m "not hardware" --maxfail=1 --disable-warnings \ - --cov=dlclivegui \ + --cov={envsitepackagesdir}/dlclivegui \ --cov-report=xml:{toxinidir}/.coverage.{envname}.xml \ --cov-report=term-missing \ {posargs} From 7a2152173b82f2815675388e37b4918a31f82fca Mon Sep 17 00:00:00 2001 From: Cyril Achard Date: Wed, 25 Feb 2026 14:03:21 +0100 Subject: [PATCH 7/8] Use awk to extract coverage table in CI Replace the sed extraction with an awk script that starts printing at the coverage header and stops when the "Coverage XML written to file" marker is seen, preventing extraneous trailing output from being appended to the GitHub job summary. Retains the code block wrapping and || true to avoid step failures, and includes minor whitespace cleanup in the workflow file. --- .github/workflows/testing-ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing-ci.yml b/.github/workflows/testing-ci.yml index 3a6a3db..5a06fbb 100644 --- a/.github/workflows/testing-ci.yml +++ b/.github/workflows/testing-ci.yml @@ -57,14 +57,18 @@ jobs: run: | tox -q | tee tox-output.log + - name: Append Coverage Summary to Job if: matrix.os == 'ubuntu-latest' && matrix.python == '3.12' shell: bash run: | echo "## Coverage Summary" >> "$GITHUB_STEP_SUMMARY" echo '```text' >> "$GITHUB_STEP_SUMMARY" - # Pull the coverage table from tox output: - sed -n '/^Name\s\+Stmts\s\+Miss\s\+Cover/,$p' tox-output.log >> "$GITHUB_STEP_SUMMARY" || true + awk ' + /^Name[[:space:]]+Stmts[[:space:]]+Miss/ {p=1} + p==1 {print} + /^Coverage XML written to file/ {exit} + ' tox-output.log >> "$GITHUB_STEP_SUMMARY" || true echo '```' >> "$GITHUB_STEP_SUMMARY" - name: Upload coverage to Codecov From 398b7c61d444739389ab741439c9207e41a3b25a Mon Sep 17 00:00:00 2001 From: C-Achard Date: Wed, 25 Feb 2026 18:48:05 +0100 Subject: [PATCH 8/8] Improve wheel install & smoke test in CI Select the built wheel dynamically and echo its path, install the package via a file:// wheel URL including the [pytorch] extras and add PyTorch CPU index to pip, replace the multi-line import check with a single python -c import verification, and run dlclivegui --help in offscreen Qt mode. These changes make the smoke test more robust by ensuring PyTorch dependencies are resolved and exercising the CLI in a headless environment. --- .github/workflows/python-package.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index 6de28b5..81b9a41 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -67,12 +67,13 @@ jobs: # Smoke test: install the built wheel and verify the package imports - name: Install from wheel & smoke test run: | - python -m pip install dist/*.whl - python - <<'PY' - import importlib - m = importlib.import_module("dlclivegui") - print("Imported:", m.__name__) - PY + 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