Skip to content

Commit 2a84fd8

Browse files
Merge issue-143-engineer: Harden scheduled regression evidence capture (#143)
2 parents dfbfbb3 + dad3ea8 commit 2a84fd8

2 files changed

Lines changed: 97 additions & 4 deletions

File tree

.github/workflows/scheduled-full-regression.yml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ jobs:
5656
--summary "$GITHUB_STEP_SUMMARY"
5757
uv run --frozen python -m ci.verification environment \
5858
--plan .tmp/scheduled/verification-plan.json --component selector \
59+
--allow-hosted-runner-drift \
5960
--output .tmp/scheduled/selector-environment.json
6061
uv run --frozen python -m ci.verification record \
6162
--plan .tmp/scheduled/verification-plan.json --component selector \
@@ -67,6 +68,7 @@ jobs:
6768
--artifact-id "ci-selection-$GITHUB_RUN_ID-attempt-$GITHUB_RUN_ATTEMPT" \
6869
--artifact-root .tmp/scheduled \
6970
--execution-environment .tmp/scheduled/selector-environment.json \
71+
--allow-hosted-runner-drift \
7072
--machine-output .tmp/scheduled/verification-plan.json \
7173
--output .tmp/scheduled/selector-evidence.json
7274
- name: Preserve the scheduled decision and shared plan
@@ -85,6 +87,7 @@ jobs:
8587
- uses: actions/checkout@v4
8688
with:
8789
ref: ${{ github.sha }}
90+
fetch-depth: 0
8891
- uses: astral-sh/setup-uv@v6
8992
with:
9093
version: "0.10.11"
@@ -114,6 +117,7 @@ jobs:
114117
environment=.tmp/evidence/$component-environment.json
115118
uv run --frozen python -m ci.verification environment \
116119
--plan .tmp/input/verification-plan.json --component "$component" \
120+
--allow-hosted-runner-drift \
117121
--output "$environment"
118122
uv run --frozen python -m ci.verification record \
119123
--plan .tmp/input/verification-plan.json --component "$component" \
@@ -124,6 +128,7 @@ jobs:
124128
--job-id quality \
125129
--artifact-id "verification-component-quality-$GITHUB_RUN_ID-attempt-$GITHUB_RUN_ATTEMPT" \
126130
--artifact-root .tmp/evidence --execution-environment "$environment" \
131+
--allow-hosted-runner-drift \
127132
--machine-output .tmp/evidence/quality-output.log \
128133
--output ".tmp/evidence/$component-evidence.json"
129134
done
@@ -143,6 +148,7 @@ jobs:
143148
- uses: actions/checkout@v4
144149
with:
145150
ref: ${{ github.sha }}
151+
fetch-depth: 0
146152
- uses: astral-sh/setup-uv@v6
147153
with:
148154
version: "0.10.11"
@@ -159,6 +165,7 @@ jobs:
159165
- uses: actions/checkout@v4
160166
with:
161167
ref: ${{ github.sha }}
168+
fetch-depth: 0
162169
- uses: astral-sh/setup-uv@v6
163170
with:
164171
version: "0.10.11"
@@ -177,6 +184,7 @@ jobs:
177184
- uses: actions/checkout@v4
178185
with:
179186
ref: ${{ github.sha }}
187+
fetch-depth: 0
180188
- uses: astral-sh/setup-uv@v6
181189
with:
182190
version: "0.10.11"
@@ -202,6 +210,7 @@ jobs:
202210
mkdir -p .tmp/evidence
203211
uv run --frozen python -m ci.verification environment \
204212
--plan .tmp/input/verification-plan.json --component django \
213+
--allow-hosted-runner-drift \
205214
--output .tmp/evidence/django-environment.json
206215
uv run --frozen python -m ci.verification record \
207216
--plan .tmp/input/verification-plan.json --component django \
@@ -213,6 +222,7 @@ jobs:
213222
--artifact-id "verification-component-django-$GITHUB_RUN_ID-attempt-$GITHUB_RUN_ATTEMPT" \
214223
--artifact-root .tmp/evidence \
215224
--execution-environment .tmp/evidence/django-environment.json \
225+
--allow-hosted-runner-drift \
216226
--machine-output .tmp/evidence/django-output.log \
217227
--output .tmp/evidence/django-evidence.json
218228
- uses: actions/upload-artifact@v4
@@ -234,6 +244,7 @@ jobs:
234244
- uses: actions/checkout@v4
235245
with:
236246
ref: ${{ github.sha }}
247+
fetch-depth: 0
237248
- uses: astral-sh/setup-uv@v6
238249
with:
239250
version: "0.10.11"
@@ -249,7 +260,7 @@ jobs:
249260
run: |
250261
set -euo pipefail
251262
mkdir -p .tmp/evidence
252-
make test-playwright 2>&1 | tee .tmp/evidence/playwright-output.log
263+
make test-playwright-core 2>&1 | tee .tmp/evidence/playwright-output.log
253264
- name: Record shared Playwright envelope
254265
if: always()
255266
env:
@@ -259,17 +270,19 @@ jobs:
259270
mkdir -p .tmp/evidence
260271
uv run --frozen python -m ci.verification environment \
261272
--plan .tmp/input/verification-plan.json --component playwright \
273+
--allow-hosted-runner-drift \
262274
--output .tmp/evidence/playwright-environment.json
263275
uv run --frozen python -m ci.verification record \
264276
--plan .tmp/input/verification-plan.json --component playwright \
265-
--result "$JOB_STATUS" --command "make test-playwright" \
277+
--result "$JOB_STATUS" --command "make test-playwright-core" \
266278
--origin-kind github_actions --repository-id "$GITHUB_REPOSITORY" \
267279
--workflow .github/workflows/scheduled-full-regression.yml --ref "$GITHUB_REF" \
268280
--run-id "$GITHUB_RUN_ID" --run-attempt "$GITHUB_RUN_ATTEMPT" \
269281
--job-id playwright \
270282
--artifact-id "verification-component-playwright-$GITHUB_RUN_ID-attempt-$GITHUB_RUN_ATTEMPT" \
271283
--artifact-root .tmp/evidence \
272284
--execution-environment .tmp/evidence/playwright-environment.json \
285+
--allow-hosted-runner-drift \
273286
--machine-output .tmp/evidence/playwright-output.log \
274287
--output .tmp/evidence/playwright-evidence.json
275288
- uses: actions/upload-artifact@v4
@@ -288,6 +301,7 @@ jobs:
288301
- uses: actions/checkout@v4
289302
with:
290303
ref: ${{ github.sha }}
304+
fetch-depth: 0
291305
- uses: astral-sh/setup-uv@v6
292306
with:
293307
version: "0.10.11"
@@ -420,6 +434,7 @@ jobs:
420434
}' < .tmp/evidence/container-assertions.txt > .tmp/evidence/container-check.json
421435
uv run --frozen python -m ci.verification environment \
422436
--plan .tmp/input/verification-plan.json --component container \
437+
--allow-hosted-runner-drift \
423438
--output .tmp/evidence/container-environment.json
424439
uv run --frozen python -m ci.verification record \
425440
--plan .tmp/input/verification-plan.json --component container \
@@ -431,6 +446,7 @@ jobs:
431446
--artifact-id "verification-component-container-$GITHUB_RUN_ID-attempt-$GITHUB_RUN_ATTEMPT" \
432447
--artifact-root .tmp/evidence \
433448
--execution-environment .tmp/evidence/container-environment.json \
449+
--allow-hosted-runner-drift \
434450
--machine-output .tmp/evidence/container-check.json \
435451
--output .tmp/evidence/container-evidence.json
436452
- uses: actions/upload-artifact@v4
@@ -453,6 +469,7 @@ jobs:
453469
- uses: actions/checkout@v4
454470
with:
455471
ref: ${{ github.sha }}
472+
fetch-depth: 0
456473
- uses: astral-sh/setup-uv@v6
457474
with:
458475
version: "0.10.11"
@@ -522,6 +539,7 @@ jobs:
522539
- uses: actions/checkout@v4
523540
with:
524541
ref: ${{ github.sha }}
542+
fetch-depth: 0
525543
- uses: astral-sh/setup-uv@v6
526544
with:
527545
version: "0.10.11"

ci/tests/test_workflows.py

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from __future__ import annotations
22

3+
import json
4+
import re
35
from pathlib import Path
46
from typing import Any
57

@@ -21,6 +23,27 @@ def runs(job: dict[str, Any]) -> str:
2123
)
2224

2325

26+
def logical_shell_commands(job: dict[str, Any]) -> list[str]:
27+
commands: list[str] = []
28+
continuation: list[str] = []
29+
for raw_line in runs(job).splitlines():
30+
line = raw_line.strip()
31+
if not line:
32+
continue
33+
continued = line.endswith("\\")
34+
continuation.append(line.removesuffix("\\").rstrip())
35+
if not continued:
36+
commands.append(" ".join(continuation))
37+
continuation = []
38+
assert not continuation
39+
return commands
40+
41+
42+
def verification_invocations(job: dict[str, Any], subcommand: str) -> list[str]:
43+
marker = f"python -m ci.verification {subcommand}"
44+
return [command for command in logical_shell_commands(job) if marker in command]
45+
46+
2447
def test_normal_workflow_keeps_release_concurrency_and_exact_base_contract() -> None:
2548
data = workflow("ci.yml")
2649
concurrency = data["concurrency"]
@@ -294,7 +317,59 @@ def test_scheduled_workflow_has_no_mutation_or_aws_jobs_and_checks_exact_sha() -
294317
step for step in job["steps"] if step.get("uses") == "actions/checkout@v4"
295318
]
296319
assert checkout_steps
297-
assert all(step["with"]["ref"] == "${{ github.sha }}" for step in checkout_steps)
320+
assert all(
321+
step["with"]
322+
== {
323+
"ref": "${{ github.sha }}",
324+
"fetch-depth": "0",
325+
}
326+
for step in checkout_steps
327+
)
328+
329+
330+
def test_scheduled_evidence_capture_and_record_symmetrically_allow_runner_drift() -> None:
331+
jobs = workflow("scheduled-full-regression.yml")["jobs"]
332+
evidence_jobs = {"selector", "quality", "django", "playwright", "container"}
333+
assert {
334+
name
335+
for name, job in jobs.items()
336+
if verification_invocations(job, "environment") or verification_invocations(job, "record")
337+
} == evidence_jobs
338+
339+
for name in evidence_jobs:
340+
environment_calls = verification_invocations(jobs[name], "environment")
341+
record_calls = verification_invocations(jobs[name], "record")
342+
assert len(environment_calls) == 1, name
343+
assert len(record_calls) == 1, name
344+
assert environment_calls[0].split().count("--allow-hosted-runner-drift") == 1, name
345+
assert record_calls[0].split().count("--allow-hosted-runner-drift") == 1, name
346+
347+
assert "for component in quality evidence_validation; do" in runs(jobs["quality"])
348+
for name in ("selector", "django", "playwright", "container"):
349+
assert f"--component {name}" in verification_invocations(jobs[name], "environment")[0]
350+
351+
352+
def test_scheduled_playwright_executes_and_records_the_planner_core_command() -> None:
353+
playwright = workflow("scheduled-full-regression.yml")["jobs"]["playwright"]
354+
planner_command = json.loads((ROOT / "ci" / "ownership.json").read_text(encoding="utf-8"))[
355+
"components"
356+
]["playwright"]["command"]
357+
assert planner_command == "make test-playwright-core"
358+
command_pattern = re.compile(r"\bmake test-playwright(?:-core)?\b")
359+
360+
execution = next(
361+
step
362+
for step in playwright["steps"]
363+
if step.get("name") == "Run and retain the complete Playwright output"
364+
)
365+
recording = next(
366+
step
367+
for step in playwright["steps"]
368+
if step.get("name") == "Record shared Playwright envelope"
369+
)
370+
assert command_pattern.findall(execution["run"]) == [planner_command]
371+
assert command_pattern.findall(recording["run"]) == [planner_command]
372+
assert f'--command "{planner_command}"' in recording["run"]
298373

299374

300375
def test_scheduled_full_marker_and_gate_cover_every_component_or_exact_skip() -> None:
@@ -316,7 +391,7 @@ def test_scheduled_full_marker_and_gate_cover_every_component_or_exact_skip() ->
316391
assert "make test-factories" in runs(jobs["factories"])
317392
assert "make test-migrations" in runs(jobs["migrations"])
318393
assert "make test" in runs(jobs["django"])
319-
assert "make test-playwright" in runs(jobs["playwright"])
394+
assert "make test-playwright-core" in runs(jobs["playwright"])
320395
assert "ci.quality_contract" in runs(jobs["quality"])
321396
assert "make verification-quality" not in runs(jobs["quality"])
322397
container = runs(jobs["container"])

0 commit comments

Comments
 (0)