github preston-peterson/aerodrome v3.0.0

latest releases: v3.4.136, v3.4.135, v3.4.134...
5 months ago

Activated the GitHub-Releases-based update channel — Aerodrome now knows when it's outdated. Adds a real implementation of /api/updates/github/check (previously a stub returning enabled: false), a background scheduler that polls GitHub on a configurable interval (default monthly), and a real "GitHub update" card on the /updates page replacing the v2.41.3 "coming soon" placeholder. Five distinct UI states render based on cached state: disabled (when the master toggle is off in config), never-checked (first run, no check has happened yet), up-to-date (running version is >= latest GitHub release), update-available (a strictly newer release exists), and error (last check failed — network timeout, rate limit, etc., with the last known good version still shown alongside). The major version bump is justified because this is the milestone the v3.0.0 slot was reserved for: transitioning from solo-dev private hosting to the public OSS update flow Aerodrome's repository-public state finally enables.

How it works for users: a new updates section in config.yaml controls the channel. The defaults — enabled: true, poll_interval: monthly, notify.banner: true, notify.gear_badge: true — are the values the launch experience optimizes for: a fresh install does its first check immediately on startup (or on first reaching the /updates page), then settles into monthly background polling. Manual "Check now" buttons on the card always work regardless of the polling interval, including when polling is set to "never" (which disables only the automatic checks, not the user's ability to ask). The gear-menu amber dot lights up across every admin page when an update is available — the same dot already used for sudoers drift and capacity issues, now with one additional contributor. v3.0.0 is intentionally check-only: the card shows "View on GitHub" for available updates, pointing at the Release page. Actually applying the update still goes through the existing local-update card above the GitHub card (download the zip, upload to update/, click Apply); the in-app "Apply update" button lands in v3.0.1, which is the fetch+verify+stage layer in front of the existing apply infrastructure.

Behind the scenes — the architecture choices that mattered. The single-row update_state table (SQLite migration v8, enforced by a CHECK (id = 1) constraint) carries two distinct timestamps: last_check_ts (any attempt, success or failure) drives the "last checked: N minutes ago" display, while last_known_latest_ts (only successful checks) is what the scheduler's interval-elapsed logic compares against. The separation matters because a transient network glitch shouldn't reset the "we know about v3.0.0" knowledge — only failed checks update the error fields; successful checks update everything. The scheduler is a dedicated daemon thread modeled on _daily_summary_scheduler (Aerodrome's established pattern — no central scheduler library, just one thread per periodic task), sleeping at most 1 hour between wakes so config changes propagate quickly without requiring service restart (this is also why updates is in LIVE_KEYS in config_validator.py). A _update_check_lock prevents the "Check now" button racing with a scheduled tick, so concurrent manual+automatic checks produce exactly one HTTP call and one state update. Page loads on /updates never hit GitHub directly — they read cached state from SQLite via the new endpoint shape, which is what makes the 60-req/hour anonymous rate limit irrelevant at scale (traffic scales with users, polling scales with the configured interval).

Behind the scenes — defensive bits worth flagging. Version comparison is strict semver-only (X.Y.Z parse exactly); non-semver tags including pre-release suffixes (-rc1, +build.42) are deliberately treated as "no actionable update" rather than risking a misorder comparison. This means pre-releases tagged on GitHub never appear as "available" to existing installs, regardless of whether releases/latest ever returns one — belt-and-braces, since releases/latest excludes pre-releases anyway. Downgrades are never suggested: when running > latest_known (a dev install ahead of the latest tagged release, or a yanked release scenario), the card shows "up to date" rather than offering to drop the user to an older version. Errors during _get_update_state() itself (e.g., DB locked momentarily) fall through to the "never checked" state with a logged warning rather than surfacing as a card error — those are infrastructure failures, not GitHub-channel failures. The endpoint accepts ?force=true for the manual "Check now" path, ignored silently when the master toggle is off (no point hitting GitHub when the feature is disabled). And the scheduler starts unconditionally on boot rather than gated on enabled: true, so flipping the toggle on at runtime via config edit takes effect within the next sleep interval (~1 hour worst case) without a service restart — the alternative ("don't start the thread when disabled, require restart to enable") would have made the LIVE_KEYS contract a lie.

The next two releases close out the v3.0.x arc: v3.0.1 adds the in-app "Apply update" button (fetch zip + sha256 from the Releases asset URLs, verify, stage into update/, hand off to the existing POST /api/updates/local/apply which already does the right thing source-agnostically), and v3.0.2 adds the optional ntfy push notification (update_available event type, opt-in like all ntfy events) plus a polish pass on whatever rough edges surface during dogfooding.

Don't miss a new aerodrome release

NewReleases is sending notifications on new releases.