Commit challs.txt once a day at 04:00, separate from the HedgeDoc refresh

The 30-minute timer now only updates the HedgeDoc note; a new
ctf-challs.timer runs --challs daily at 04:00. Locks are per target so
the two runs can overlap without one skipping.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-20 17:47:57 +02:00
co-authored by Claude Opus 5.5
parent cb5184a55d
commit ec2f444ef3
6 changed files with 63 additions and 28 deletions
+23 -13
View File
@@ -74,7 +74,14 @@ deployment project. If the branch is protected, the token's user must be allowed
to push to it. Without `CHALLS_BRANCH`, the default branch is looked up, which
also needs **Project: Read** there.
## Every 30 minutes, on a server
## On a schedule, on a server
Two timers, independent of each other:
| timer | when | runs |
|---|---|---|
| `ctf-status.timer` | every 30 minutes | `--update` (HedgeDoc note) |
| `ctf-challs.timer` | daily at 04:00 (server local time) | `--challs` (challs.txt commit) |
Copy the repo to the server, create `ctf_status.env` or `.env` there (both are gitignored, so they
do not travel with the code), then:
@@ -84,27 +91,30 @@ do not travel with the code), then:
```
It writes systemd **user** units into `~/.config/systemd/user/`, substituting
whatever directory the repo actually sits in, enables the timer, turns on lingering
so it keeps firing when you are not logged in, and does one run to prove it works.
whatever directory the repo actually sits in, enables both timers, turns on lingering
so they keep firing when you are not logged in, and does one run of each to prove they work.
Re-run it any time; it just overwrites the units.
```sh
systemctl --user list-timers ctf-status.timer # when it next fires
journalctl --user -u ctf-status.service -n 50 # what it did
systemctl --user start ctf-status.service # run it now
systemctl --user disable --now ctf-status.timer # stop it
systemctl --user list-timers 'ctf-*' # when they next fire
journalctl --user -u ctf-status.service -n 50 # what the HedgeDoc run did
journalctl --user -u ctf-challs.service -n 50 # what the challs.txt run did
systemctl --user start ctf-challs.service # commit challs.txt now
systemctl --user disable --now ctf-challs.timer # stop the daily commit
```
The timer fires at `:00` and `:30` with up to a minute of jitter, and
`Persistent=true` catches up a run missed while the machine was down. Plain cron
works too, if you would rather:
The HedgeDoc timer fires at `:00` and `:30` with up to a minute of jitter; both
timers have `Persistent=true`, so a run missed while the machine was down is caught up.
Plain cron works too, if you would rather:
```
*/30 * * * * /path/to/planner/ctf_status.py --update --challs --quiet
*/30 * * * * /path/to/planner/ctf_status.py --update --quiet
0 4 * * * /path/to/planner/ctf_status.py --challs --quiet
```
`--quiet` keeps a successful run silent, so you only hear about it when the note
changed or something broke. Overlapping runs are prevented by a lock file, and the
`--quiet` keeps a successful run silent, so you only hear about it when something
changed or broke. Overlapping runs of the same target are prevented by a lock file
per target (the 04:00 challs run and the HedgeDoc run can overlap), and the
HedgeDoc write is retried (`--retries`, default 3) before the run gives up with a
one-line error and a non-zero exit.