ci(R-1297 A): heavy suites by path on a branch push, all on a tag and nightly; no second full gate run
gates / gates (push) Successful in 3m23s
gates / gates (push) Successful in 3m23s
- script_tests_gate.py: test_gate_decoys / test_due_checks_gate / dooplex-offsite run on a CI branch push only when one of their paths changed (FELHOM_CHANGED_FILES); no list, an empty or unreadable list = every suite - gates.yml: nightly schedule 03:20 UTC; the list is set only for push:refs/heads/*; a trimmed commits array (total_commits > listed) is an unknown list - test_repo_gates.py and test_due_checks_gate.py group G drive the real runner with GATES narrowed to their labels, instead of the whole gate set a second time (130 s each in CI) - red-proved: filter cases, workflow pins (old gates.yml), inert run_gate, unregistered due-checks
This commit is contained in:
1 parent
8143a2c872
commit
ecd82abab8
5 files changed
+194
-10
No files matched your search
@@ -45,6 +45,51 @@ def timeout_for(rel):
|
||||
return SUITE_TIMEOUT_S.get(rel.replace(os.sep, "/"), TIMEOUT_S)
|
||||
SKIP_DIRS = {"__pycache__", ".git", "node_modules"}
|
||||
|
||||
# R-1297 (A, 2026-10-26): THREE HEAVY SUITES RUN ON A CI PUSH ONLY WHEN ONE OF THEIR OWN PATHS CHANGED. Measured in CI run
|
||||
# 2366: the decoy suite 232 s, the due-checks suite 133 s, the DooPlex off-site suite 69 s — of a 13-minute run, on every
|
||||
# push, documents included. The decoys test the GATE SCRIPTS (they plant a fixture and expect a conviction), so a push
|
||||
# that touches no script cannot change their verdict; the other two test their own scripts only.
|
||||
#
|
||||
# A RUN WITHOUT A LIST RUNS EVERY SUITE. CI sets FELHOM_CHANGED_FILES only for a branch push whose file list is known
|
||||
# (gates.yml); a release tag, the nightly schedule, a manual dispatch and the pre-push hook set nothing, so they run all
|
||||
# of them. An unreadable or empty list is unknown, and unknown runs everything (fail-closed). What the filter may cost:
|
||||
# a document change that breaks a decoy's FIXTURE (not a gate) is seen by the nightly run, within a day — said here so
|
||||
# nobody reads the filter as free. Pinned by scripts/test_script_tests_gate.py (FILTER_CASES).
|
||||
CHANGED_ENV = "FELHOM_CHANGED_FILES"
|
||||
PATH_FILTERED = {
|
||||
"scripts/test_gate_decoys.py": ("scripts/", ".gitea/", ".githooks/"),
|
||||
"scripts/test_due_checks_gate.py": ("scripts/due_checks_gate.py", "scripts/test_due_checks_gate.py",
|
||||
"scripts/repo_gates.py", "scripts/script_tests_gate.py"),
|
||||
"scripts/dooplex-offsite/test_dooplex_offsite.py": ("scripts/dooplex-offsite/",),
|
||||
}
|
||||
|
||||
|
||||
def changed_paths():
|
||||
"""The pushed paths, or None when no usable list exists (then every suite runs)."""
|
||||
p = os.environ.get(CHANGED_ENV, "")
|
||||
if not p:
|
||||
return None
|
||||
try:
|
||||
with open(p, encoding="utf-8") as fh:
|
||||
paths = [l.strip() for l in fh if l.strip()]
|
||||
except OSError as e:
|
||||
print("script-tests: %s=%s unreadable (%s) — every suite runs" % (CHANGED_ENV, p, e))
|
||||
return None
|
||||
if not paths:
|
||||
print("script-tests: %s=%s is empty — every suite runs" % (CHANGED_ENV, p))
|
||||
return None
|
||||
return paths
|
||||
|
||||
|
||||
def skip_reason(rel, changed):
|
||||
"""Why a suite is skipped on this push, or "" when it runs."""
|
||||
prefixes = PATH_FILTERED.get(rel.replace(os.sep, "/"))
|
||||
if changed is None or not prefixes:
|
||||
return ""
|
||||
if any(c.startswith(pfx) for c in changed for pfx in prefixes):
|
||||
return ""
|
||||
return "none of its paths (%s) changed in this push — it runs on a release tag and nightly" % ", ".join(prefixes)
|
||||
|
||||
|
||||
def find_suites(scripts_dir):
|
||||
out = []
|
||||
@@ -66,9 +111,16 @@ def main(argv):
|
||||
print("script-tests: FAILED — no test_*.py found under %s/scripts (a gate that checked nothing is not green)" % root)
|
||||
return 1
|
||||
env = dict(os.environ, **{MARK: "1"})
|
||||
changed = changed_paths()
|
||||
failed = []
|
||||
skipped = 0
|
||||
for s in suites:
|
||||
rel = os.path.relpath(s, root)
|
||||
why = skip_reason(rel, changed)
|
||||
if why:
|
||||
print(" SKIP %-55s %s" % (rel, why))
|
||||
skipped += 1
|
||||
continue
|
||||
t0 = time.time()
|
||||
limit = timeout_for(rel)
|
||||
try:
|
||||
@@ -86,7 +138,8 @@ def main(argv):
|
||||
if failed:
|
||||
print("script-tests: FAILED — %d of %d suite(s): %s" % (len(failed), len(suites), ", ".join(failed)))
|
||||
return 1
|
||||
print("script-tests: OK — %d suite(s), every one exited 0" % len(suites))
|
||||
print("script-tests: OK — %d suite(s) ran, every one exited 0%s" % (
|
||||
len(suites) - skipped, (", %d skipped by path (R-1297)" % skipped) if skipped else ""))
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user