-
Notifications
You must be signed in to change notification settings - Fork 3
311 lines (302 loc) · 12.5 KB
/
Copy pathci.yml
File metadata and controls
311 lines (302 loc) · 12.5 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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
name: CI
# `develop` is listed under push because it is the integration branch every contributor
# branches from, and until #412 nothing ran the suite when a commit landed on it. The
# pull-request run tests the head merged into the base AS THE BASE STOOD WHEN IT RAN, so
# a green required check can describe a merge that no longer resembles the one being
# performed: #396's `test` check was seven hours stale when it merged, and the package
# move in #402 had landed in between. `security.yml` already covered `develop` on push,
# which is why the branch showed green marks after that merge while the suite was
# uncollectable. This step is DETECTION, not prevention -- `develop` still goes red, but
# within minutes and attributed to the merge commit that caused it, instead of surfacing
# later on an unrelated pull request.
#
# Prevention arrived separately: a merge queue is now enabled on `develop`, and it tests
# the exact merge commit BEFORE it lands. That makes the `develop` push run a re-test of
# a commit `merge_group` just passed -- verified identical, not merely equivalent: the
# queue's merge commit reappears verbatim as the push SHA on every merge, batched entries
# included. KEEP IT ANYWAY. The queue is a branch-protection setting that does not appear
# in the REST protection payload, is absent from rulesets, and is pinned by no test, so if
# it is ever switched off nothing else would notice and this run becomes the only verdict
# on `develop` again. Roughly seven runner-minutes per merge, on free hosted runners, buys
# insurance against a silent config change. `main` has NO queue, so its push run is not
# redundant at all -- it is the only test of the promotion merge commit, whose
# pull-request run tested a preview rather than what landed.
#
# A push to a branch that also has an open pull request runs this workflow twice, once
# per event. Feature branches are unaffected: `develop` and `main` are only pushed to by
# a merge, which closes the pull request rather than synchronizing it. The one case that
# does double is the `develop` -> `main` promotion pull request, which is rare and
# maintainer-initiated, so it is accepted rather than worked around.
#
# DO NOT add a `concurrency` group with `cancel-in-progress: true`. Cancelling an
# in-flight run means a merge commit finishes with no verdict, which is this issue's gap
# in a new form. `update-examples.yml` uses that setting and is the nearby example likely
# to be copied. `tests/test_ci_workflow.py` fails if either half of this is undone.
#
# `merge_group` reports the required `test` check for pull requests sitting in a merge
# queue (#416). The queue is ENABLED on `develop`, so this trigger is live and fires on
# every merge; it is not enabled on `main`, where the event never fires. The trigger
# landed BEFORE the queue was turned on, deliberately: enabling the queue while the
# workflows lack this trigger means the required checks are never reported, so nothing
# merges until someone works out why. `checks_requested` is the only
# activity type the event has; it is named rather than defaulted so the file says what it
# responds to. `security.yml` carries the same trigger for the other required check.
on:
pull_request:
push:
branches: [main, develop]
merge_group:
types: [checks_requested]
jobs:
# Interpreter-independent checks: lint and format. Run once, not per Python version.
lint-format:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
run: uv python install
- name: Install dependencies
run: uv sync
- name: Lint
if: ${{ !cancelled() }}
run: uv run ruff check .
- name: Check formatting
if: ${{ !cancelled() }}
run: uv run ruff format --check .
# Fast tests: run on every supported interpreter.
fast-tests:
runs-on: ubuntu-latest
permissions:
contents: read
env:
UV_PYTHON: ${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
python-version: ["3.12.0", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Run fast tests
if: ${{ !cancelled() }}
run: uv run pytest -m "not slow and not browser" -v
# Browser tests: run on every supported interpreter. Includes Playwright install.
browser-tests:
runs-on: ubuntu-latest
permissions:
contents: read
env:
UV_PYTHON: ${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
python-version: ["3.12.0", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Install Playwright browser
if: ${{ !cancelled() }}
run: uv run playwright install --with-deps chromium
- name: Run browser tests
if: ${{ !cancelled() }}
# --run-browser: this step runs on dedicated hardware where Chromium is
# guaranteed, so a launch failure must fail the run rather than skip every
# test into a green no-op (#599).
run: uv run pytest -m browser --run-browser -v
# External-validation suite: committee reports + legislative branch validation.
# Runs on every supported interpreter.
external-validation:
runs-on: ubuntu-latest
permissions:
contents: read
env:
UV_PYTHON: ${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
python-version: ["3.12.0", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Run external-validation suite
if: ${{ !cancelled() }}
run: >
uv run pytest -v -m slow
tests/test_committee_report.py
tests/test_validate_extraction.py
# Corpus correctness gates: parametrize over committed manifest.
# Runs on every supported interpreter.
corpus-gates:
runs-on: ubuntu-latest
permissions:
contents: read
env:
UV_PYTHON: ${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
python-version: ["3.12.0", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Run corpus correctness gates (committed manifest)
if: ${{ !cancelled() }}
run: >
uv run pytest -v -m slow
tests/test_corpus_properties.py
tests/test_corpus_tree_properties.py
tests/test_diff_validation.py
tests/test_node_join_corpus.py
tests/test_xml_subsection_nodes.py
tests/test_pdf_subsection_recall.py
tests/test_manifest_report_fixtures.py
tests/test_canonical_baseline.py
tests/test_pdf_canonical_baseline.py
tests/test_pdf_matching_boundary.py
tests/test_pdf_observation_emission.py
tests/test_pdf_observation_identity.py
tests/test_pdf_round1_retrieval.py
tests/test_pdf_round1_revocation.py
tests/test_pdf_round2_stages.py
tests/test_matching_contracts.py
tests/test_assignment_classification_boundary.py
tests/test_round1_stages.py
tests/test_round1_pairing_sentinel.py
# Packaging gate: build wheel, install in clean env, run diff from outside checkout.
# Runs on every supported interpreter.
packaging-gate:
runs-on: ubuntu-latest
permissions:
contents: read
env:
UV_PYTHON: ${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
python-version: ["3.12.0", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Run packaging gate (build, install, diff from outside the checkout)
if: ${{ !cancelled() }}
run: >
uv run pytest -v -m slow
tests/test_engine_installs.py
# Remaining slow suites: all other @slow modules against committed fixtures.
# Runs on every supported interpreter.
remaining-slow:
runs-on: ubuntu-latest
permissions:
contents: read
env:
UV_PYTHON: ${{ matrix.python-version }}
strategy:
fail-fast: false
matrix:
python-version: ["3.12.0", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v7
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Run remaining slow suites (committed fixtures)
if: ${{ !cancelled() }}
run: >
uv run pytest -v -m slow
tests/test_classify_bill.py
tests/test_pdf_corpus_smoke.py
tests/test_bill_tree.py
tests/test_structure_tree.py
tests/test_diff_bill.py
tests/test_pdf_compare.py
tests/test_financial_diff.py
tests/test_pipeline_parity.py
tests/test_pdf_xml_amount_recall.py
tests/test_pdf_xml_prose_recall.py
tests/test_front_matter_parity.py
tests/test_xml_compare.py
tests/test_toc_tree.py
tests/test_format_html.py
tests/test_manifest_report_fixtures.py
tests/test_canonical_tree.py
tests/test_reconcile.py
tests/test_pdf_watermark_recall.py
tests/test_formatters_text_serializer.py
tests/test_pdf_xml_withheld_recall.py
# The required status check on `develop` and `main` is a context named exactly
# `test`. The matrix jobs above report per-leg contexts (e.g., `fast-tests (3.12)`),
# and the non-matrix `lint-format` reports a single context. Without this aggregator
# the first matrix edit would silently strand branch protection: every following
# pull request would wait on a `test` context that no longer reports (#426 review).
# This job reports `test` forever, no matter how the matrix is renamed or resized,
# and no branch-protection edit is needed.
#
# `if: always()` is load-bearing: a skipped or failed dependency would otherwise
# skip this job too, and a skipped aggregator reports no verdict at all. The
# explicit result checks are the other half: each `needs.<job>.result` must be
# "success" -- a skipped or cancelled job surfaces as "skipped"/"cancelled", and
# without the comparison below those would count as a pass, making the aggregator
# a rubber stamp.
test:
needs: [lint-format, fast-tests, browser-tests, external-validation, corpus-gates, packaging-gate, remaining-slow]
if: always()
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Require every dependent job to have succeeded
env:
LINT_FORMAT_RESULT: ${{ needs.lint-format.result }}
FAST_TESTS_RESULT: ${{ needs.fast-tests.result }}
BROWSER_TESTS_RESULT: ${{ needs.browser-tests.result }}
EXTERNAL_VALIDATION_RESULT: ${{ needs.external-validation.result }}
CORPUS_GATES_RESULT: ${{ needs.corpus-gates.result }}
PACKAGING_GATE_RESULT: ${{ needs.packaging-gate.result }}
REMAINING_SLOW_RESULT: ${{ needs.remaining-slow.result }}
run: |
for result in \
"$LINT_FORMAT_RESULT" \
"$FAST_TESTS_RESULT" \
"$BROWSER_TESTS_RESULT" \
"$EXTERNAL_VALIDATION_RESULT" \
"$CORPUS_GATES_RESULT" \
"$PACKAGING_GATE_RESULT" \
"$REMAINING_SLOW_RESULT"; do
if [ "$result" != "success" ]; then
echo "::error::A required job result is '$result', not 'success'"
exit 1
fi
done
echo "All required jobs succeeded."