-
-
Notifications
You must be signed in to change notification settings - Fork 7
231 lines (220 loc) · 8.52 KB
/
tests.yaml
File metadata and controls
231 lines (220 loc) · 8.52 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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
---
name: 🔬 Tests
"on":
workflow_dispatch:
push:
branches:
- main
paths:
- repomatic/**
- tests/**
- pyproject.toml
- uv.lock
- .github/workflows/tests.yaml
pull_request:
branches-ignore:
- prepare-release
paths:
- repomatic/**
- tests/**
- pyproject.toml
- uv.lock
- .github/workflows/tests.yaml
schedule:
# Run tests monthly to catch regressions from dependency or environment changes.
- cron: "17 9 1 * *"
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ !startsWith(github.event.head_commit.message, '[changelog] Release') }}
jobs:
tests:
strategy:
# We delegate the failure decision to each job, by the way of the "continue-on-error" flag.
fail-fast: false
matrix:
# Available OS: https://github.com/actions/runner-images#available-images
# Only targets 2 variants per platforms to keep the matrix small.
os:
- ubuntu-24.04-arm # arm64
- ubuntu-slim # x86
- macos-26 # arm64
- macos-15-intel # x86
- windows-11-arm # arm64
- windows-2025 # x86
python-version:
- "3.10"
# Ignore intermediate versions to reduce CI load.
# - "3.11"
# - "3.12"
# - "3.13"
- "3.14"
- "3.14t"
- "3.15"
- "3.15t"
exclude:
# Python 3.10 has no native ARM64 Windows build.
- os: windows-11-arm
python-version: "3.10"
include:
# Default all jobs as stable, unless marked otherwise below.
- state: stable
# XXX Python 3.15 is still in development.
- state: unstable
python-version: "3.15"
- state: unstable
python-version: "3.15t"
name: "${{ matrix.state == 'stable' && '✅' || '⁉️' }} ${{ matrix.os }} / py${{ matrix.python-version }}"
runs-on: ${{ matrix.os }}
# We keep going when a job flagged as not stable fails.
continue-on-error: ${{ matrix.state == 'unstable' }}
steps:
- uses: actions/checkout@v6.0.2
- uses: astral-sh/setup-uv@v7.3.1
- name: Force native ARM64 Python on Windows ARM64
# Workaround for https://github.com/astral-sh/uv/issues/12906: uv defaults to x86_64 Python on ARM64 Windows,
# relying on transparent emulation. Native extensions with Rust (e.g., test-results-parser from codecov-cli)
# fail to compile because the ARM64 Rust toolchain lacks the x86_64-pc-windows-msvc target. Setting UV_PYTHON
# to an architecture-qualified specifier forces uv (and uvx) to use native ARM64 Python for all steps.
if: runner.os == 'Windows' && runner.arch == 'ARM64'
shell: bash
run: echo "UV_PYTHON=cpython-${{ matrix.python-version }}-aarch64" >> "$GITHUB_ENV"
- name: Install Python ${{ matrix.python-version }}
# If UV cannot find the requested Python version, it exits with code 2, which let the job pass unnoticed on
# Windows. So we force the shell to bash, even on Windows, and "set -e" to ensure any error in the install
# process is caught and fails the job. It works because Windows runners too Git Bash available).
shell: bash
run: |
set -e
uv --no-progress venv --python "${UV_PYTHON:-${{ matrix.python-version }}}"
- name: Install project
run: uv --no-progress sync --frozen --all-extras --group test
- name: Unittests
# Produce XML artifacts for Codecov integration (coverage + test analytics).
run: >
uv --no-progress run --frozen -- pytest
--cov-report=xml
--junitxml=junit.xml
--override-ini=junit_family=legacy
- name: Upload coverage to Codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: >
uvx --no-progress 'codecov-cli==11.2.6'
--auto-load-params-from githubactions
upload-process
--git-service github
--report-type coverage
--token "${CODECOV_TOKEN}"
- name: Upload test results to Codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: >
uvx --no-progress 'codecov-cli==11.2.6'
--auto-load-params-from githubactions
upload-process
--git-service github
--report-type test_results
--token "${CODECOV_TOKEN}"
- name: Self-tests against test plan
# test-plan-file is auto-detected from [tool.repomatic] in pyproject.toml.
run: uv run --frozen --no-progress -- repomatic test-plan --command "uv run -- repomatic"
metadata:
name: 🧬 Project metadata
runs-on: ubuntu-slim
outputs:
metadata: ${{ steps.metadata.outputs.metadata }}
steps:
- uses: actions/checkout@v6.0.2
- uses: astral-sh/setup-uv@v7.3.1
- name: Run repomatic metadata
id: metadata
run: >
uvx --no-progress --from . repomatic metadata
--format github-json --output "$GITHUB_OUTPUT"
build_targets cli_scripts package_name
test-package-install:
name: 📦 Package install
needs:
- metadata
if: fromJSON(needs.metadata.outputs.metadata).cli_scripts
runs-on: ubuntu-slim
env:
PACKAGE: ${{ fromJSON(needs.metadata.outputs.metadata).package_name }}
CLI_SCRIPTS: ${{ fromJSON(needs.metadata.outputs.metadata).cli_scripts }}
GIT_URL: git+https://github.com/${{ github.repository }}
steps:
- uses: astral-sh/setup-uv@v7.3.1
- name: Install Python
run: uv --no-progress venv --python 3.14
- name: Run stable CLI
run: |
for script in $CLI_SCRIPTS; do
uvx "$script" --version
uvx --from "$PACKAGE" -- "$script" --version
uv run --with "$PACKAGE" -- "$script" --version
done
module=$(echo "$PACKAGE" | tr '-' '_')
uv run --with "$PACKAGE" -m "$module" --version
uv run --with "$PACKAGE" python -m "$module" --version
- name: Run dev CLI
run: |
for script in $CLI_SCRIPTS; do
uvx --from "$GIT_URL" -- "$script" --version
uv run --with "$GIT_URL" -- "$script" --version
done
module=$(echo "$PACKAGE" | tr '-' '_')
uv run --with "$GIT_URL" -m "$module" --version
uv run --with "$GIT_URL" python -m "$module" --version
- name: Run stable CLI via uv tool
run: |
uv --no-progress tool install "$PACKAGE"
for script in $CLI_SCRIPTS; do
"$script" --version
done
uv --no-progress tool uninstall "$PACKAGE"
- name: Run dev CLI via uv tool
run: |
uv --no-progress tool install "$GIT_URL"
for script in $CLI_SCRIPTS; do
"$script" --version
done
uv --no-progress tool uninstall "$PACKAGE"
- name: Run CLI via pipx
run: |
uv --no-progress tool install pipx
for script in $CLI_SCRIPTS; do
pipx run "$script" --version
pipx run --spec "$PACKAGE" "$script" --version
pipx run --spec "$GIT_URL" "$script" --version
done
uv --no-progress tool uninstall pipx
validate-arch:
# Check architecture matches the one expected from the runner image. This is to ensure that the OS does not rely on
# emulation to run the build. See:
# https://docs.astral.sh/uv/concepts/python-versions/#transparent-x86_64-emulation-on-aarch64
name: 🖥️ Validate ${{ matrix.os }} / ${{ matrix.arch }}
needs:
- metadata
if: fromJSON(needs.metadata.outputs.metadata).build_targets
strategy:
matrix:
include: ${{ fromJSON(fromJSON(needs.metadata.outputs.metadata).build_targets) }}
runs-on: ${{ matrix.os }}
steps:
- name: Check Python version
run: |
python --version
python -m pip --version
- name: Check architecture is ${{ matrix.arch }}
shell: python
run: |
import platform
arch = platform.machine()
print(f"Detected architecture: {arch}")
matrix_arch = "${{ matrix.arch }}"
if matrix_arch == "x64":
assert arch.lower() in ("x86_64", "amd64")
elif matrix_arch == "arm64":
assert arch.lower() in ("aarch64", "arm64")
else:
raise ValueError(f"Unrecognized architecture in the matrix: {matrix_arch}")