chore: drop the scheduled campaign job

This commit is contained in:
Abdessamad Derraz committed 2026-11-05 11:50:35 +01:00
1 parent 6876fe7ec3
commit 442d49e4e1
5 files changed
+13 -81

No files matched your search

-29
View File
@@ -94,35 +94,6 @@ jobs:
stdout: tmp/autorun/bios-integrity.json stdout: tmp/autorun/bios-integrity.json
report: tmp/autorun/bios-integrity.json report: tmp/autorun/bios-integrity.json
campaign:
schedule: "*-*-* 06:00"
network: true
max_load_wait: 7200
timeout: 12600
steps:
- run: [python, scripts/campaign.py, run, --budget, 150m]
ok_codes: [0, 75]
stdout: tmp/autorun/campaign.json
env: [RETROBIOS_ROMFETCH, RETROBIOS_AUTO_LOCK, ROMFETCH_CONTENT_DIR, ROMFETCH_CACHE_DIR,
ROMFETCH_DATS_DIR, ROMFETCH_RETOOL_DIR, ROMFETCH_MIN_INTERVAL, ROMFETCH_MAX_SIZE,
ROMFETCH_DISABLED_SOURCES, XDG_CONFIG_HOME]
gate:
- [python, scripts/campaign.py, gate]
commits:
- subject: "chore: add campaign measurements"
body: >-
Core probe records, findings and the pass summary written by
campaign.py, behind the measurement tests and the schemas.
paths: [measurements/*.jsonl]
- subject: "chore: apply fields the campaign proved"
body: >-
Fields the campaign's rule table settled from real runs, each with
the record ids in evidence:, behind verify and an emulator pack of
every changed profile.
paths: [emulators/*.yml]
report: tmp/autorun/campaign.json
scratch: [tmp/autorun, tmp/core_probe]
assets: assets:
schedule: "*-*-* 04:30" schedule: "*-*-* 04:30"
network: true network: true
+2 -11
View File
@@ -53,11 +53,9 @@ Usage:
python scripts/campaign.py findings [--all] [--json] python scripts/campaign.py findings [--all] [--json]
python scripts/campaign.py review KEY --status applied|rejected --reason TEXT [--set FIELD=YAML] python scripts/campaign.py review KEY --status applied|rejected --reason TEXT [--set FIELD=YAML]
python scripts/campaign.py gate python scripts/campaign.py gate
python scripts/campaign.py install-timer [--enable]
install-timer is opt-in: a contributor who wants a schedule on their own A maintainer starts each pass for a question that names its profiles: no
machine installs the timer. It writes the units and enables nothing unless job runs it on a schedule.
--enable is passed.
""" """
from __future__ import annotations from __future__ import annotations
@@ -1941,18 +1939,11 @@ def main(argv: Sequence[str] | None = None) -> int:
help="with --status applied, a field to write into the entry") help="with --status applied, a field to write into the entry")
review_p.add_argument("--index", type=int, help="the entry's position when several share its name") review_p.add_argument("--index", type=int, help="the entry's position when several share its name")
sub.add_parser("gate", help="tests, schemas, verify and pack of the changed profiles") sub.add_parser("gate", help="tests, schemas, verify and pack of the changed profiles")
timer_p = sub.add_parser("install-timer",
help="opt-in: write the autorun units, the campaign job's timer among them")
timer_p.add_argument("--enable", action="store_true",
help="also reload systemd and enable the timers (never by default)")
args = parser.parse_args(argv) args = parser.parse_args(argv)
paths = Paths.default() paths = Paths.default()
if args.command == "gate": if args.command == "gate":
return gate(paths.root, changed_profiles(paths.root)) return gate(paths.root, changed_profiles(paths.root))
if args.command == "install-timer":
command = [sys.executable, str(ROOT / "scripts" / "autorun.py"), "install-timers"]
return subprocess.run(command + (["--enable"] if args.enable else []), check=False).returncode
if args.command == "review": if args.command == "review":
updates: dict[str, object] = {} updates: dict[str, object] = {}
for item in args.set: for item in args.set:
-1
View File
@@ -275,7 +275,6 @@ class GithubTokenPerStep(Scratch):
def test_the_committed_registry(self): def test_the_committed_registry(self):
jobs = ar.load_jobs() jobs = ar.load_jobs()
self.assertTrue(all(step.github_token for step in jobs["nightly"].steps)) self.assertTrue(all(step.github_token for step in jobs["nightly"].steps))
self.assertFalse(any(step.github_token for step in jobs["campaign"].steps + jobs["campaign"].gate))
class ReportAlert(Scratch): class ReportAlert(Scratch):
+7 -30
View File
@@ -1019,24 +1019,6 @@ class ContentFile(Workspace):
self.assertIn("Other Game (Japan)", out.getvalue()) self.assertIn("Other Game (Japan)", out.getvalue())
class InstallTimer(unittest.TestCase):
def test_writes_the_units_and_enables_only_when_asked(self) -> None:
calls = []
real_run = subprocess.run
def fake_run(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess:
if "install-timers" not in argv:
return real_run(argv, **kwargs)
calls.append(argv)
return subprocess.CompletedProcess(argv, 0)
with unittest.mock.patch.object(campaign.subprocess, "run", fake_run):
self.assertEqual(campaign.main(["install-timer"]), 0)
self.assertEqual(campaign.main(["install-timer", "--enable"]), 0)
self.assertEqual([argv[2:] for argv in calls],
[["install-timers"], ["install-timers", "--enable"]])
self.assertTrue(calls[0][1].endswith("scripts/autorun.py"))
class Registry(unittest.TestCase): class Registry(unittest.TestCase):
def test_no_profile_takes_a_campaign_file_name(self) -> None: def test_no_profile_takes_a_campaign_file_name(self) -> None:
stems = {p.stem for p in (REPO_ROOT / "emulators").glob("*.yml")} stems = {p.stem for p in (REPO_ROOT / "emulators").glob("*.yml")}
@@ -1054,18 +1036,13 @@ class Registry(unittest.TestCase):
self.assertGreater(len(item.reason), 40) self.assertGreater(len(item.reason), 40)
self.assertGreater(item.frames, item.frame_hash_every) self.assertGreater(item.frames, item.frame_hash_every)
def test_the_autorun_job(self) -> None: def test_no_job_runs_a_standing_campaign(self) -> None:
jobs = campaign.autorun.load_jobs() """A daily pass over every profile contradicted the rule that probes
job = jobs["campaign"] run on demand, for a question that names its profiles."""
self.assertTrue(job.lock and job.worktree and job.schedule) for name, job in campaign.autorun.load_jobs().items():
self.assertEqual(job.gate[0].argv, ("python", "scripts/campaign.py", "gate")) for step in [*job.steps, *job.gate]:
self.assertEqual({p for rule in job.commits for p in rule.paths}, with self.subTest(job=name):
{"measurements/*.jsonl", "emulators/*.yml"}) self.assertNotIn("scripts/campaign.py", step.argv)
self.assertGreater(job.schedule, jobs["nightly"].schedule)
def test_autorun_tells_its_steps_the_lock_is_held(self) -> None:
job = campaign.autorun.load_jobs()["campaign"]
self.assertEqual(campaign.autorun.job_env(job)[campaign.LOCK_HELD_ENV], "1")
@unittest.skipUnless(shutil.which("cc") and shutil.which("strace"), "no C compiler or no strace") @unittest.skipUnless(shutil.which("cc") and shutil.which("strace"), "no C compiler or no strace")
+4 -10
View File
@@ -772,7 +772,6 @@ until a report without one. Exit codes: 0 for `ok`,
|-----|----------|--------------| |-----|----------|--------------|
| `nightly` | 02:00 | `check_freshness.py --json` saved to a file, `refresh_stale.py --findings` on its `data` and `catalogs` rows, the MAME and FBNeo hash scrapers, then `pipeline.py --offline` as the gate. `database.json` and `install/*.json` are computed a second time and compared. Commits the hash profiles, the recipes and the regenerated records | | `nightly` | 02:00 | `check_freshness.py --json` saved to a file, `refresh_stale.py --findings` on its `data` and `catalogs` rows, the MAME and FBNeo hash scrapers, then `pipeline.py --offline` as the gate. `database.json` and `install/*.json` are computed a second time and compared. Commits the hash profiles, the recipes and the regenerated records |
| `assets` | 04:30 | `sync_release_assets.py`, as a dry run | | `assets` | 04:30 | `sync_release_assets.py`, as a dry run |
| `campaign` | 06:00 | `campaign.py run --budget 150m` once the host is idle (up to two hours of waiting), gated by `campaign.py gate`. Commits the records, findings and pass line, then the profile fields the runs proved |
| `bios-integrity` | Sunday 08:30 | `bios_integrity.py --apply --json` in the main checkout, once the host is idle (up to four hours of waiting). Its report is kept in the job's status | | `bios-integrity` | Sunday 08:30 | `bios_integrity.py --apply --json` in the main checkout, once the host is idle (up to four hours of waiting). Its report is kept in the job's status |
| `cleanup` | 05:30 | `cleanup_disk.py --apply --hours 72` in the main checkout | | `cleanup` | 05:30 | `cleanup_disk.py --apply --hours 72` in the main checkout |
| `disk-check` | hourly | `cleanup_disk.py --check`, without the lock | | `disk-check` | hourly | `cleanup_disk.py --check`, without the lock |
@@ -1052,7 +1051,6 @@ python scripts/campaign.py findings # what waits for review
python scripts/campaign.py review beetle_psx/sony-playstation/scph5501.bin/validation \ python scripts/campaign.py review beetle_psx/sony-playstation/scph5501.bin/validation \
--status applied --set on_mismatch=warn --reason "logs the SHA1 and loads (libretro.c:853-860)" --status applied --set on_mismatch=warn --reason "logs the SHA1 and loads (libretro.c:853-860)"
python scripts/campaign.py gate # the checks behind a commit python scripts/campaign.py gate # the checks behind a commit
python scripts/campaign.py install-timer # opt-in: writes the autorun units, enables nothing
``` ```
| Step | What happens | | Step | What happens |
@@ -1108,10 +1106,10 @@ checks. The campaign's own `evidence:` writes do not count as a profile change.
| Guard | Rule | | Guard | Rule |
|-------|------| |-------|------|
| Lock | The host-wide autorun lock. Under the `campaign` job, autorun holds it and says so with `RETROBIOS_AUTO_LOCK_HELD` | | Lock | The host-wide autorun lock. When a caller already holds it, `RETROBIOS_AUTO_LOCK_HELD` says so |
| Probe lock | Not held by the pass: each core_probe it starts takes the host probe lock for its own run, so the pass and other measurements (pack_probe, a core_probe loop) interleave. Two campaign passes still exclude each other through the automation lock, since they share `.cache/campaign/state.json`. Do not wrap `campaign.py run` in `flock` | | Probe lock | Not held by the pass: each core_probe it starts takes the host probe lock for its own run, so the pass and other measurements (pack_probe, a core_probe loop) interleave. Two campaign passes still exclude each other through the automation lock, since they share `.cache/campaign/state.json`. Do not wrap `campaign.py run` in `flock` |
| Environment | `core_probe.py`, git and `make` get autorun's allowlist, romfetch the same plus its `ROMFETCH_*` settings. None sees a token or a password | | Environment | `core_probe.py`, git and `make` get autorun's allowlist, romfetch the same plus its `ROMFETCH_*` settings. None sees a token or a password |
| Sandbox | Fails closed: without a working bubblewrap the pass is `refused` (exit 3) and nothing third-party runs, every probe is started with `--require-sandbox`, and a build is refused. `--allow-unsandboxed` overrides. Under autorun the refusal raises the `job:campaign` alert | | Sandbox | Fails closed: without a working bubblewrap the pass is `refused` (exit 3) and nothing third-party runs, every probe is started with `--require-sandbox`, and a build is refused. `--allow-unsandboxed` overrides |
| Load | Before each run, waits while the 1-minute load is above a quarter of the CPUs, up to `--max-load-wait` seconds, then the pass stops as `paused` | | Load | Before each run, waits while the 1-minute load is above a quarter of the CPUs, up to `--max-load-wait` seconds, then the pass stops as `paused` |
| Disk | Below `--min-free-gb` (default 30) the pass stops as `paused` | | Disk | Below `--min-free-gb` (default 30) the pass stops as `paused` |
| Budget | No check starts after `--budget` | | Budget | No check starts after `--budget` |
@@ -1129,12 +1127,8 @@ and campaign tests, `validate_schemas.py --source-only`, then `verify.py
--emulator` and `generate_pack.py --emulator` for each changed profile. A file --emulator` and `generate_pack.py --emulator` for each changed profile. A file
that had uncommitted changes before the pass is left alone. that had uncommitted changes before the pass is left alone.
A maintainer starts each pass. Scheduling is opt-in: a contributor who wants a A maintainer starts each pass, for a question that names its profiles. No
schedule on their own machine installs the timer with `campaign.py autorun job runs it on a schedule.
install-timer`, which writes the autorun units and enables nothing unless
`--enable` is passed. The `campaign` autorun job then runs a 150-minute pass
in the automation worktree, gates it with `campaign.py gate` and commits with
its own rules, pushing only with `--push`.
`review` closes an open finding once someone has read the core's code at the `review` closes an open finding once someone has read the core's code at the
profile's pin. `--status applied` writes each `--set FIELD=YAML` into the profile's pin. `--status applied` writes each `--set FIELD=YAML` into the