mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-10 12:47:04 +00:00
WordPress#0 runs 18 Playwright suites one after another and exceeds its CI time budget. A role that opts in through playwright.parallel in its meta/tests.yml (WordPress: [3, 1, 1] per variant) now hands its suites to an async scheduler, run_parallel.py, after staging: - a suite starts only when its dependencies finished here (Keycloak first) and the host's CPU pressure, avg10 and avg60, stayed at or below 36 for 30 s; the first container always starts and running ones are never killed - onion suites share their own cap of 3 and a Tor lock with a recreate-pending marker; a probe that times out on the lock re-probes the current circuits and, failing that, leaves only that app to the serial path - an app whose dependency was dropped is deferred, and every deferred app gets the serial first pass before the existing retry, in round order - each job gets --oom-score-adj=1000 instead of a memory limit; crashed pages, closed browsers and killed workers are counted and named in the per-app timing line - each job's result.json, stdout and stderr land in run-parallel/ and are archived with the reports - the async ceiling is the serial worst case per app and reachable network, curl's per-attempt max time included (now TEST_E2E_PLAYWRIGHT_ONION_PROBE_MAX_TIME); it stays above the CI step timeout and never raises a budget Roles without the opt-in run exactly as before; the onion probe script with an empty lock behaves byte for byte like the previous one. Tests: 126 role unit tests and 30 negative controls cover the gate window, admission order, onion cap and lock, marker cleanup, the bounded wait, dependency deferral, crash counting, artifact collection, the ceiling for the real WordPress#0 app list, and serial equivalence. No CI run has exercised it yet; the next push is its live proof. 🎯 Fix: 88 % 💥 Break: 6 % 🌱 Efficiency: – ⚡ Speed: – 🛡️ Security: ~100 % 🏆 Score: 83 🚦 Gate: ⛔ fix 88 % < 90, break 6 % > 5; committed and merged on the operator's word, CI is the live proof Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
31 lines
1001 B
Python
31 lines
1001 B
Python
from pathlib import Path
|
|
|
|
from utils.cache.yaml import load_yaml_any
|
|
|
|
PROJECT_ROOT: Path = Path(__file__).resolve().parents[5]
|
|
ROLE: Path = PROJECT_ROOT / "roles" / "test-e2e-playwright"
|
|
|
|
|
|
def role_task(task_file: str, prefix: str) -> dict:
|
|
"""Find a task of the Playwright role by the start of its name.
|
|
|
|
Args:
|
|
task_file: path of the task file below the role's ``tasks`` directory.
|
|
prefix: the start of the task name, usually its emoji.
|
|
|
|
Returns:
|
|
The first matching task, searched depth first through ``block``,
|
|
``rescue`` and ``always``.
|
|
"""
|
|
pending = list(
|
|
load_yaml_any(str(ROLE / "tasks" / task_file), default_if_missing=[])
|
|
)
|
|
while pending:
|
|
task = pending.pop(0)
|
|
if task.get("name", "").startswith(prefix):
|
|
return task
|
|
pending[:0] = (
|
|
task.get("block", []) + task.get("rescue", []) + task.get("always", [])
|
|
)
|
|
raise LookupError(f"{task_file}: no task named {prefix}")
|