Skip to content

Add store-versions: store releases against GitHub releases - #77

Merged
Bilb merged 11 commits into
mainfrom
feat/store-versions
Oct 15, 2026
Merged

Bilb merged 11 commits into
mainfrom
feat/store-versions

Conversation

@Bilb

@Bilb Bilb commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Moves session-divemo into session-ops, including the Flathub checks from session-foundation/session-divemo#1. It compares each store's Session release with the platform's GitHub release every 5 minutes, and posts to Discord as they diverge and come back.

The same seven checks as divemo (Play, App Store, APT, F-Droid merged/live, Flathub merged/live) and the same warning, reminder, all-clear and info rules, with three differences:

  • Every comparison uses the store's store_high watermark, not only the ℹ️ post: a store re-serving its previous version (iTunes, Flathub's CDN, raw.githubusercontent) no longer flips a tagged warning and its all-clear every run. Once a store has served a lower version for 6 hours, that version is compared, so a bad read clears itself, a warning out is updated to the new gap, and a rollback warns on the channels warned on either way; nothing needs editing by hand.
  • GitHub's version is the highest full release from one page of /releases, not /releases/latest, which skips prereleases and follows creation date. A store shipping a published prerelease above it is in sync, so a prerelease already on F-Droid or Play does not warn, and a hotfix on an older line does not look like GitHub going backwards.
  • APT takes the highest of a package's versions in the index, not the first listed. See docs/jobs/store-versions.md.

What changed in the move

divemo here
Always-on gateway bot (discord.py), polling loop session-ops@store-versions.timer, *:0/5, as sessionops
Bot token, channel id STORE_VERSIONS_DISCORD_WEBHOOK_URL, embeds over the webhook
config.yaml platforms + tag ids Platforms in code; tag ids in /etc/session-ops/store-versions.toml
SQLite /var/lib/session-ops/store-versions/state.json
requests direct shared.http (retries, timeouts, rate-limit handling)
Per-platform errors logged only A source failing for an hour is posted, then every 12 h, then ✅ when it recovers
/versions Ported in #71, reading state.json

Shared code this adds or reuses:

  • shared.discord.post_to_discord: a platform's state is kept only once its messages are posted, so a refused post is retried on the next run.
  • shared.github.releases (new).
  • shared.http.Session(deadline=…) (new): a wall-clock limit on a whole call, retries and body included; off by default. The runner's alert post uses 45 s for every job.
  • shared.state.write_json, split out of save_state.
  • The runner's failure alerts and the silence checker, via jobs.toml (max_age_hours = 1).
  • repeat_alert_hours (new, in jobs.toml): the runner reports the same failure again only that often until a run succeeds. store-versions sets 12, since a 5-minute timer would otherwise alert 288 times a day.

Each run fetches every source concurrently on short timeouts and saves state after each platform's message lands, and a platform's events go as one message. Every request has a wall-clock deadline, 60 s for a source and 20 s for a post (shared.http's new optional deadline, which also covers a server trickling its body), no post starts after 100 s, and the runner's alert gets 45 s, so a run and its alert end by about 165 s, inside the 4-minute timeout; unposted messages wait for the next run. A refused post fails with the same text however many wait, so the runner's 12-hour repeat limit holds. A version that doesn't start with a digit, or is over 64 characters, fails its source. A suffixed version (1.2.3-beta1) ranks below its release, as divemo's packaging comparison did.

/versions is in #71's interactions relay, which merges this branch in; merge this PR first.

Open PRs

Tested by merging this branch with #71 and #76, separately and together: no conflicts, and the deploy, registry and store-versions tests pass on the merged tree. To stay clear of #71's hunks:

Once #71 is in, that line can join the loop.

Deploying

  1. install.sh, then fill /etc/session-ops/store-versions.env with a webhook for the release-monitor channel and run install.sh again to enable the timer.
  2. Copy store-versions.toml.example to /etc/session-ops/store-versions.toml and fill in the tag ids from divemo's config.yaml on angus.
  3. session-ops run store-versions --dry-run to check every source reads.
  4. Stop divemo on angus (systemctl disable --now divemo).

State starts fresh: the first run records every platform quietly, unless one is already out of sync.

Tests: 862 pass, including 80 new (state machine ported from divemo's suite, parsers, fetchers, posting/retry/tagging, repeat alerts, flaps, prereleases, watermark decay, request deadlines). 975 pass on #71, which merges this branch in.

Bilb and others added 5 commits October 14, 2026 15:46
Port session-divemo, the Discord bot that watches each store's Session release
against the platform's GitHub release, into a session-ops job. Its seven checks
(Play, App Store, the APT repo, F-Droid merged and live, and Flathub merged and
live) and its warning, reminder, all-clear and info rules carry over unchanged.

It runs every 5 minutes from a timer instead of a long-running gateway bot, and
reuses what session-ops already shares:

- shared.http for every request, so retries, timeouts and rate limits behave
  like every other job's
- shared.discord's post_to_discord over a webhook, with embeds; a platform's
  state is kept only once its messages are posted, so a refused post is retried
- shared.github.latest_release, new, for the GitHub side
- shared.state.write_json, split out of save_state, for its atomic state file
- the runner's failure alerts and the silence checker, via jobs.toml

A source that keeps failing (Play's scraped listing is the fragile one) is
posted after an hour and every 12 hours until it reads again, rather than
alerting on every 5-minute run.

The /versions slash command is not ported: `session-ops run store-versions
--dry-run` prints the same versions.
…alerts

Sources are fetched concurrently on short timeouts, and each platform's
events go as one message saved as soon as it lands, so a host that stops
answering or a kill mid-run cannot outlast the unit or repost a step.
Messages still unposted 150 s in wait for the next run, and one refused
message no longer holds back the other platforms.

A version that does not start with a digit, or is over 64 characters,
fails its source rather than producing an embed Discord refuses. A
suffixed version (1.2.3-beta1, 1.2.3rc1) ranks below its release.

jobs.toml's repeat_alert_hours makes the runner report the same failure
again only that often until a run succeeds; store-versions sets 12, as a
5-minute timer would otherwise alert 288 times a day.
…releases

A store re-serving its previous version (iTunes, Flathub's CDN, raw.githubusercontent)
flipped a warning and its all-clear every run, tagging people each time: only the
info post used the store's high. Every comparison now does.

GitHub's /releases/latest skips prereleases, so a prerelease already on F-Droid or
Play warned as unpublished source, and it follows creation date, so a hotfix on an
older line looked like GitHub going backwards. One page of /releases now gives the
highest full release, and a store shipping a published prerelease above it is in sync.
Bilb and others added 6 commits October 15, 2026 16:29
store_high only ever rose, so a release pulled from APT or a reverted F-Droid or
Flathub manifest read as in sync for good, and one bad Play read warned every 12 hours
until state.json was edited. A store serving a lower version for six hours now has
that version compared: past the iTunes and CDN lag the watermark is there to smooth.
…aring

A post started up to 150 s in could run past the unit's 4 minutes; 100 s leaves room
for a request's worst case. A refused post fails with the same text however many
messages wait, so the runner's 12-hour repeat limit holds. A store that was ahead
and is now behind a newer GitHub release clears as "no longer ahead", not "back in
sync", and reminders no longer claim ~12h at every repeat.
…g the watermark changes

A read timeout applies per socket read, so a server trickling its body held a run past
the unit's 4-minute timeout, and a killed run reaches the OnFailure backstop on every
run. shared.http takes an optional deadline on a whole call, retries and body included;
store-versions gives fetches 60 s and posts 20 s, so a run and its alert end by about
165 s. The runner's alert post gets 45 s for every job, past which the backstop reports
the run; other requests keep no deadline.

A watermark coming down while a warning is out now posts the warning's new gap, rather
than turning "ahead" into "behind" unannounced until the next reminder.
… a growing gap

A lowered watermark can shrink the gap or flip its direction, so its update
now names the version the store came down to.
TestSession's NoSleep patches time.sleep globally; a Trickle thread still
sending into a later test landed in its recorded sleeps under load.
@Bilb
Bilb merged commit 350ec18 into main Oct 15, 2026
2 of 3 checks passed
@Bilb
Bilb deleted the feat/store-versions branch October 16, 2026 06:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants