From 442d49e4e1f6e41b2e8fd0af4b42e5af24115348 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 11:50:35 +0100 Subject: [PATCH] chore: drop the scheduled campaign job --- automation/jobs.yml | 29 ----------------------------- scripts/campaign.py | 13 ++----------- tests/test_autorun.py | 1 - tests/test_campaign.py | 37 +++++++------------------------------ wiki/tools.md | 14 ++++---------- 5 files changed, 13 insertions(+), 81 deletions(-) diff --git a/automation/jobs.yml b/automation/jobs.yml index 1edfe158c..34da6f385 100644 --- a/automation/jobs.yml +++ b/automation/jobs.yml @@ -94,35 +94,6 @@ jobs: stdout: 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: schedule: "*-*-* 04:30" network: true diff --git a/scripts/campaign.py b/scripts/campaign.py index 2c85fe621..fd014113a 100644 --- a/scripts/campaign.py +++ b/scripts/campaign.py @@ -53,11 +53,9 @@ Usage: 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 gate - python scripts/campaign.py install-timer [--enable] -install-timer is opt-in: a contributor who wants a schedule on their own -machine installs the timer. It writes the units and enables nothing unless ---enable is passed. +A maintainer starts each pass for a question that names its profiles: no +job runs it on a schedule. """ 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") 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") - 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) paths = Paths.default() if args.command == "gate": 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": updates: dict[str, object] = {} for item in args.set: diff --git a/tests/test_autorun.py b/tests/test_autorun.py index 354f64a7a..aa32b1d05 100644 --- a/tests/test_autorun.py +++ b/tests/test_autorun.py @@ -275,7 +275,6 @@ class GithubTokenPerStep(Scratch): def test_the_committed_registry(self): jobs = ar.load_jobs() 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): diff --git a/tests/test_campaign.py b/tests/test_campaign.py index 3ac9d6670..5966dd8d7 100644 --- a/tests/test_campaign.py +++ b/tests/test_campaign.py @@ -1019,24 +1019,6 @@ class ContentFile(Workspace): 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): def test_no_profile_takes_a_campaign_file_name(self) -> None: 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(item.frames, item.frame_hash_every) - def test_the_autorun_job(self) -> None: - jobs = campaign.autorun.load_jobs() - job = jobs["campaign"] - self.assertTrue(job.lock and job.worktree and job.schedule) - self.assertEqual(job.gate[0].argv, ("python", "scripts/campaign.py", "gate")) - self.assertEqual({p for rule in job.commits for p in rule.paths}, - {"measurements/*.jsonl", "emulators/*.yml"}) - 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") + def test_no_job_runs_a_standing_campaign(self) -> None: + """A daily pass over every profile contradicted the rule that probes + run on demand, for a question that names its profiles.""" + for name, job in campaign.autorun.load_jobs().items(): + for step in [*job.steps, *job.gate]: + with self.subTest(job=name): + self.assertNotIn("scripts/campaign.py", step.argv) @unittest.skipUnless(shutil.which("cc") and shutil.which("strace"), "no C compiler or no strace") diff --git a/wiki/tools.md b/wiki/tools.md index 1aa6aef87..e0c6997b6 100644 --- a/wiki/tools.md +++ b/wiki/tools.md @@ -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 | | `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 | | `cleanup` | 05:30 | `cleanup_disk.py --apply --hours 72` in the main checkout | | `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 \ --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 install-timer # opt-in: writes the autorun units, enables nothing ``` | Step | What happens | @@ -1108,10 +1106,10 @@ checks. The campaign's own `evidence:` writes do not count as a profile change. | 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` | | 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` | | Disk | Below `--min-free-gb` (default 30) the pass stops as `paused` | | 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 that had uncommitted changes before the pass is left alone. -A maintainer starts each pass. Scheduling is opt-in: a contributor who wants a -schedule on their own machine installs the timer with `campaign.py -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`. +A maintainer starts each pass, for a question that names its profiles. No +autorun job runs it on a schedule. `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