Check frontend build budgets before the full unit suite

PR2624 spent 8m45s on successful unit tests before its deterministic bundle-size rejection; PR2626 and PR2627 now fail at that same gate. Type-check, build the real embedded assets and apply the unchanged size budgets before spending that test time. Passing source still runs every existing check.

Contract-Neutral: Reorder existing frontend CI commands only; no product interface, dependency graph, bundle budget or required-check change.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-07 00:02:31 +01:00
parent f77070690e
commit a8271238e1
2 changed files with 78 additions and 4 deletions
+7 -4
View File
@@ -251,14 +251,13 @@ jobs:
working-directory: frontend-modern
run: npm run lint:cpd
- name: Frontend unit tests
working-directory: frontend-modern
run: npm run test
- name: Type-check frontend
working-directory: frontend-modern
run: npm run type-check
# Reject an unshippable bundle before the whole-tree unit suite. On
# PR2624 that suite took 8m45s before the size gate rejected the bundle.
# Passing builds still require the same tests, audit and size budgets.
- name: Build frontend bundle (with embed copy)
run: make frontend
@@ -266,6 +265,10 @@ jobs:
working-directory: frontend-modern
run: npm run check:bundlesize
- name: Frontend unit tests
working-directory: frontend-modern
run: npm run test
# An audit service failure or real advisory stays gating, but it must not
# suppress formatting, tests, type-checking, or production-build proof.
- name: Require frontend dependency audit
@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""An unshippable frontend must fail before the expensive full unit suite."""
from pathlib import Path
import unittest
import yaml
ROOT = Path(__file__).resolve().parents[2]
WORKFLOW = ROOT / ".github/workflows/build-and-test.yml"
class FrontendWorkflowOrderTest(unittest.TestCase):
def setUp(self):
self.job = yaml.safe_load(WORKFLOW.read_text())["jobs"]["frontend"]
self.steps = self.job["steps"]
def step(self, name):
matches = [step for step in self.steps if step.get("name") == name]
self.assertEqual(len(matches), 1, f"expected one {name} step")
return matches[0]
def test_type_build_and_size_admit_the_unit_suite_in_order(self):
names = [step.get("name") for step in self.steps]
ordered = [
"Install frontend dependencies",
"Type-check frontend",
"Build frontend bundle (with embed copy)",
"Check frontend bundle size budget",
"Frontend unit tests",
"Require frontend dependency audit",
]
positions = [names.index(name) for name in ordered]
self.assertEqual(positions, sorted(positions))
def test_admission_keeps_real_embed_build_and_unchanged_commands(self):
commands = {
"Type-check frontend": "npm run type-check",
"Build frontend bundle (with embed copy)": "make frontend",
"Check frontend bundle size budget": "npm run check:bundlesize",
"Frontend unit tests": "npm run test",
}
for name, command in commands.items():
with self.subTest(step=name):
step = self.step(name)
self.assertEqual(step["run"], command)
# GitHub's default success() condition must stop here on a
# failed prerequisite; neither warnings nor conditional skips
# may turn a broken build into a successful Frontend check.
self.assertNotIn("if", step)
self.assertNotIn("continue-on-error", step)
if name != "Build frontend bundle (with embed copy)":
self.assertEqual(step["working-directory"], "frontend-modern")
self.assertNotIn("continue-on-error", self.job)
def test_audit_still_reports_failure_after_an_earlier_failed_gate(self):
audit = self.step("Audit complete frontend dependency graph")
required = self.step("Require frontend dependency audit")
self.assertEqual(audit["id"], "audit-complete")
self.assertIs(audit["continue-on-error"], True)
self.assertEqual(required["if"], "${{ !cancelled() }}")
self.assertEqual(required["env"]["COMPLETE_AUDIT_RESULT"],
"${{ steps.audit-complete.outcome }}")
self.assertNotIn("continue-on-error", required)
self.assertIn('if [ "${COMPLETE_AUDIT_RESULT}" != success ]; then', required["run"])
self.assertIn("exit 1", required["run"])
if __name__ == "__main__":
unittest.main()