Visitar URL original
Add CI for package tests and release builds (#4) · DeepLabCut/dlccore@26e1241 · GitHub
Skip to content

Commit 26e1241

Browse files
authored
Add CI for package tests and release builds (#4)
Run tests across platforms and Python versions, check minimum dependencies, and validate built wheels and installed version metadata.
1 parent ee0b329 commit 26e1241

5 files changed

Lines changed: 312 additions & 0 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: build and test release
2+
3+
on:
4+
workflow_call:
5+
workflow_dispatch:
6+
inputs:
7+
ref:
8+
description: "Commit, tag or branch to build"
9+
required: false
10+
default: ""
11+
type: string
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
build-and-test-wheel:
18+
name: wheel install py3.12
19+
runs-on: ubuntu-latest
20+
21+
steps:
22+
- uses: actions/checkout@v6
23+
with:
24+
ref: ${{ inputs.ref }}
25+
# Needed for setuptools-scm
26+
fetch-depth: 0
27+
28+
- uses: actions/setup-python@v6
29+
with:
30+
python-version: "3.12"
31+
32+
- name: Install build tooling
33+
run: python -m pip install --upgrade pip build twine setuptools-scm
34+
35+
- name: Show SCM-derived version
36+
run: python -m setuptools_scm
37+
38+
- name: Build and check metadata
39+
run: |
40+
python -m build
41+
python -m twine check --strict dist/*
42+
ls dist
43+
44+
- name: Install the wheel
45+
run: python -m pip install "$(ls dist/*.whl)" "pytest>=8"
46+
47+
- name: Test the wheel outside the source tree
48+
run: |
49+
cp -r tests "$RUNNER_TEMP/wheeltest"
50+
cd "$RUNNER_TEMP"
51+
pytest wheeltest
52+
53+
- uses: actions/upload-artifact@v4
54+
with:
55+
name: dist
56+
path: dist/
57+
if-no-files-found: error

‎.github/workflows/format.yml‎

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: pre-commit (PR only on changed files)
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
permissions:
8+
contents: read
9+
10+
jobs:
11+
detect_changes:
12+
runs-on: ubuntu-latest
13+
outputs:
14+
changed: ${{ steps.changed_files.outputs.changed }}
15+
16+
steps:
17+
- name: Checkout full history
18+
uses: actions/checkout@v6
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Detect changed files
23+
id: changed_files
24+
env:
25+
BASE_REF: ${{ github.base_ref }}
26+
run: |
27+
git fetch origin "$BASE_REF"
28+
CHANGED_FILES=$(git diff --name-only --diff-filter=d "origin/$BASE_REF...HEAD")
29+
30+
delimiter="changed_$(openssl rand -hex 16)"
31+
{
32+
echo "changed<<$delimiter"
33+
echo "$CHANGED_FILES"
34+
echo "$delimiter"
35+
} >> "$GITHUB_OUTPUT"
36+
37+
- name: Show changed files
38+
env:
39+
CHANGED_FILES: ${{ steps.changed_files.outputs.changed }}
40+
run: |
41+
echo "Changed files:"
42+
echo "$CHANGED_FILES"
43+
44+
precommit:
45+
needs: detect_changes
46+
runs-on: ubuntu-latest
47+
if: ${{ needs.detect_changes.outputs.changed != '' }}
48+
49+
steps:
50+
- name: Checkout PR branch
51+
uses: actions/checkout@v6
52+
with:
53+
repository: ${{ github.event.pull_request.head.repo.full_name }}
54+
ref: ${{ github.event.pull_request.head.sha }}
55+
56+
- name: Set up Python
57+
uses: actions/setup-python@v6
58+
with:
59+
python-version: "3.12"
60+
61+
- name: Install pre-commit
62+
run: pip install pre-commit
63+
64+
- name: Run pre-commit (CI check-only stage) on changed files
65+
env:
66+
CHANGED_FILES: ${{ needs.detect_changes.outputs.changed }}
67+
run: |
68+
mapfile -t files <<< "$CHANGED_FILES"
69+
pre-commit run --hook-stage manual --files "${files[@]}" --show-diff-on-failure

‎.github/workflows/test_package.yml‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Test suite
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
workflow_call:
10+
11+
concurrency:
12+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
13+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
test:
20+
name: ${{ matrix.os }} py${{ matrix.python-version }}
21+
runs-on: ${{ matrix.os }}
22+
strategy:
23+
fail-fast: false
24+
matrix:
25+
os: [ubuntu-latest, windows-latest, macos-latest]
26+
# Keep in sync w/ DeepLabCut
27+
python-version: ["3.10", "3.11", "3.12"]
28+
29+
steps:
30+
- uses: actions/checkout@v6
31+
32+
- uses: astral-sh/setup-uv@v7
33+
with:
34+
python-version: ${{ matrix.python-version }}
35+
activate-environment: true
36+
37+
- name: Install
38+
run: uv pip install . "pytest>=8"
39+
40+
- name: Test the installed package
41+
run: pytest tests
42+
43+
test-dependency-floor:
44+
name: dependency floor py3.10
45+
runs-on: ubuntu-latest
46+
47+
steps:
48+
- uses: actions/checkout@v6
49+
50+
- uses: astral-sh/setup-uv@v7
51+
with:
52+
python-version: "3.10"
53+
activate-environment: true
54+
55+
- name: Install the lowest version each dependency declares
56+
run: uv pip install --resolution lowest-direct . "pytest>=8"
57+
58+
- name: Test
59+
run: pytest tests
60+
61+
build:
62+
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
63+
uses: ./.github/workflows/build_release.yml
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: weekly checks
2+
3+
on:
4+
schedule:
5+
- cron: "0 6 * * 1"
6+
workflow_dispatch:
7+
inputs:
8+
simulate_failure:
9+
description: "Skip the checks, fail on purpose, and open a test tracking issue"
10+
required: false
11+
default: false
12+
type: boolean
13+
14+
permissions:
15+
contents: read
16+
17+
jobs:
18+
dependency-prereleases:
19+
if: ${{ !inputs.simulate_failure }}
20+
name: dependency pre-releases py3.12
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- uses: actions/checkout@v6
25+
26+
- uses: astral-sh/setup-uv@v7
27+
with:
28+
python-version: "3.12"
29+
activate-environment: true
30+
31+
- name: Install with pre-releases allowed
32+
run: uv pip install --prerelease allow . "pytest>=8"
33+
34+
# `-o filterwarnings`, not `-W`: `-W` matches the module name exactly and would miss every submodule
35+
- name: Test
36+
run: |
37+
pytest tests -o $'filterwarnings=error::DeprecationWarning:dlccore\nerror::FutureWarning:dlccore'
38+
39+
build-release:
40+
if: ${{ !inputs.simulate_failure }}
41+
uses: ./.github/workflows/build_release.yml
42+
43+
simulate-failure:
44+
if: ${{ inputs.simulate_failure }}
45+
runs-on: ubuntu-latest
46+
steps:
47+
- run: exit 1
48+
49+
notify:
50+
# A job of its own: a step-level `if: failure()` never runs when its job dies before reaching it
51+
needs: [dependency-prereleases, build-release, simulate-failure]
52+
if: failure() && (github.event_name == 'schedule' || inputs.simulate_failure)
53+
runs-on: ubuntu-latest
54+
permissions:
55+
contents: read
56+
issues: write
57+
58+
steps:
59+
- name: Open or update the tracking issue
60+
env:
61+
GH_TOKEN: ${{ github.token }}
62+
REPO: ${{ github.repository }}
63+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
64+
PRERELEASE_RESULT: ${{ needs['dependency-prereleases'].result }}
65+
BUILD_RESULT: ${{ needs['build-release'].result }}
66+
SIMULATED: ${{ inputs.simulate_failure }}
67+
run: |
68+
set -euo pipefail
69+
if [ "$SIMULATED" = "true" ]; then
70+
title="Weekly checks failing (notification test)"
71+
label="weekly checks test"
72+
banner="> **Test.** Opened by a manual dispatch with *simulate_failure*; nothing is broken. Close it."
73+
else
74+
title="Weekly checks failing"
75+
label="weekly checks"
76+
banner=""
77+
fi
78+
gh label create "$label" --repo "$REPO" --color B60205 \
79+
--description "Tracking issue for the weekly CI checks" --force
80+
81+
summary=$(printf '%s\n' \
82+
"| Check | Result |" \
83+
"| --- | --- |" \
84+
"| dependency pre-releases | $PRERELEASE_RESULT |" \
85+
"| build and test release | $BUILD_RESULT |")
86+
87+
existing=$(gh issue list --repo "$REPO" --state open --label "$label" --json number --jq '.[0].number // empty')
88+
if [ -n "$existing" ]; then
89+
gh issue comment "$existing" --repo "$REPO" --body "$(printf '%s\n' "Still failing: [run $GITHUB_RUN_ID]($RUN_URL)" "" "$summary")"
90+
else
91+
body=$(printf '%s\n' \
92+
"Weekly checks failed: [run $GITHUB_RUN_ID]($RUN_URL)" \
93+
"" \
94+
"$summary" \
95+
"" \
96+
"These run against dependency pre-releases and the built wheel: a failure is usually an upstream change or a packaging problem, not a regression from a PR." \
97+
"" \
98+
"## Resolving" \
99+
"" \
100+
"1. Fix the failing job on a branch." \
101+
"2. Re-run the check on that branch: Actions -> build and test release -> Run workflow, or Actions -> weekly checks -> Run workflow." \
102+
"3. Merge the fix, dispatch Actions -> weekly checks on main, and close this issue. While it is open, later failures are added as comments.")
103+
if [ -n "$banner" ]; then
104+
body=$(printf '%s\n' "$banner" "" "$body")
105+
fi
106+
gh issue create --repo "$REPO" --title "$title" --body "$body" --label "$label"
107+
fi

‎tests/test_package.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# DeepLabCut Toolbox (deeplabcut.org)
3+
# © A. & M.W. Mathis Labs
4+
# https://github.com/DeepLabCut/dlccore
5+
#
6+
# Contributors:
7+
# https://github.com/DeepLabCut/dlccore/graphs/contributors
8+
#
9+
# Licensed under GNU Lesser General Public License v3.0
10+
#
11+
12+
import dlccore
13+
14+
15+
def test_version_comes_from_the_installed_distribution() -> None:
16+
assert dlccore.__version__ != "0+unknown"

0 commit comments

Comments
 (0)