-
Notifications
You must be signed in to change notification settings - Fork 0
239 lines (197 loc) 路 7.3 KB
/
Copy pathci.yml
File metadata and controls
239 lines (197 loc) 路 7.3 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
232
233
234
235
236
237
238
239
name: Lint and Test
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
# The release workflow calls this one, so the gate a release passes is this exact job
# set rather than a copy of it that can drift.
workflow_call:
permissions:
contents: read
concurrency:
# github.workflow is the calling workflow's name under workflow_call, so a release and a
# push to main land in their own groups and do not cancel each other.
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
GITLEAKS_VERSION: 8.30.1
# From the release's checksums file. Dependabot cannot update a pinned download, so bump
# both together when moving to a new gitleaks release.
GITLEAKS_SHA256: 551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb
PIP_AUDIT_VERSION: 2.10.1
jobs:
secrets:
name: Secret scan (gitleaks)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout (full history)
uses: actions/checkout@v7
with:
# Every commit, so a secret added and later removed is still found.
fetch-depth: 0
- name: Install gitleaks
run: |
archive="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
curl -sSfL -o "$RUNNER_TEMP/$archive" \
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/$archive"
echo "${GITLEAKS_SHA256} $RUNNER_TEMP/$archive" | sha256sum -c -
tar -xzf "$RUNNER_TEMP/$archive" -C "$RUNNER_TEMP" gitleaks
- name: Scan every commit
run: '"$RUNNER_TEMP/gitleaks" git --config .gitleaks.toml --redact --verbose .'
lint:
name: Lint, format and type check
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
# --locked fails the job when uv.lock is out of step with pyproject.toml.
- name: Install
run: uv sync --locked
- name: Lint
run: uv run ruff check --output-format=github src tests scripts
# Check only: formatting is fixed locally with 'just fmt', never in CI.
- name: Format check
run: uv run ruff format --check src tests scripts
# Strict, on the package; the settings are in pyproject.toml.
- name: Type check
run: uv run mypy
audit:
name: Dependency audit (pip-audit)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
# The locked, hashed dependency tree, runtime and dev, exactly as CI installs it.
- name: Audit the locked dependencies for known vulnerabilities
run: |
uv export --frozen --all-extras --format requirements-txt --no-emit-project \
--output-file "$RUNNER_TEMP/requirements.txt"
uvx "pip-audit==${PIP_AUDIT_VERSION}" --strict --disable-pip --require-hashes \
--requirement "$RUNNER_TEMP/requirements.txt"
test:
name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.11", "3.12", "3.13", "3.14"]
# The config path and file permission handling differ on Windows and macOS,
# so run the suite there too, on one Python version.
include:
- os: windows-latest
python-version: "3.13"
- os: macos-latest
python-version: "3.13"
env:
UV_PYTHON: ${{ matrix.python-version }}
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
- name: Install
run: uv sync --locked
# Includes the rebrand test, which renames a copy of the repository and runs its
# whole suite, so 'just rebrand' is proven on every platform.
- name: Test
run: uv run pytest
# Line and branch coverage on one platform, so the number is the same on every run.
# The floor (fail_under) lives in pyproject.toml, beside the rest of the coverage config.
coverage:
name: Coverage
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
with:
enable-cache: true
- name: Install
run: uv sync --locked
- name: Test with coverage
run: uv run pytest --cov --cov-report=term-missing --cov-report=xml
- name: Summarise
if: always()
shell: bash
run: |
if [ -f .coverage ]; then
{
echo "## Coverage"
echo
uv run coverage report --format=markdown --fail-under=0
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: Keep the report
if: always()
uses: actions/upload-artifact@v7
with:
name: coverage
path: coverage.xml
if-no-files-found: ignore
retention-days: 14
# The total as a shields.io endpoint; badges.yml publishes it for the README badge
# after a push to main.
- name: Write the coverage badge
shell: bash
run: |
total="$(uv run coverage report --format=total --precision=1 --fail-under=0)"
python3 - "$total" > coverage-badge.json <<'PY'
import json
import sys
total = float(sys.argv[1])
colour = next(c for floor, c in ((90, "brightgreen"), (80, "green"), (70, "yellow"), (0, "red")) if total >= floor)
print(json.dumps({"schemaVersion": 1, "label": "coverage", "message": f"{total:.1f}%", "color": colour}))
PY
cat coverage-badge.json
- name: Keep the badge
uses: actions/upload-artifact@v7
with:
name: coverage-badge
path: coverage-badge.json
if-no-files-found: error
retention-days: 14
build:
name: Build
runs-on: ubuntu-latest
timeout-minutes: 10
needs: [secrets, lint, audit, test, coverage]
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
- name: Build the sdist and wheel
run: uv build
# Proves the wheel installs cleanly and the entry point resolves outside the source tree.
- name: Smoke test the wheel
shell: bash
run: |
uv venv "$RUNNER_TEMP/smoke"
uv pip install --python "$RUNNER_TEMP/smoke" dist/*.whl
"$RUNNER_TEMP/smoke/bin/ldo" --version
"$RUNNER_TEMP/smoke/bin/ldo" --help > /dev/null
# Built once here; a release publishes these exact files rather than rebuilding.
- name: Keep the build
uses: actions/upload-artifact@v7
with:
name: dist
path: dist/
if-no-files-found: error
retention-days: 14