Skip to content

Self-host update channels: nightly Beta, Stable promoted after a day - #479

Merged
nambok merged 2 commits into
mainfrom
selfhost-update-channels
Nov 5, 2026
Merged

nambok merged 2 commits into
mainfrom
selfhost-update-channels

Conversation

@nambok

@nambok nambok commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Self-hosted installs choose an update channel: Stable or Beta (nightly). Betas are cut nightly from main's deployed head when anything that ships changed; the newest beta that has been out for 24 hours with no reported problem is promoted to stable, on the same commit. Urgent fixes still ship the same day, by hand. Installs verify every tag exactly as they do today.

Changes

  • Release streams (.github/workflows/auto-release.yml, .github/scripts/auto_release.py):
    • 06:17 UTC: vX.Y.Z-beta.YYYYMMDD of main's head once its hosted deploy succeeded (waits up to 45 min), skipped when nothing that ships changed since the last release. GitHub prerelease, signed and checked like stable; launch/beta.json moves.
    • Every beta gets its own X.Y.Z (one above the last release of either kind), so a promoted beta always sorts below later betas and never undercuts newer code.
    • The beta's run also signs the statement for its stable tag vX.Y.Z (same commit) and stores it in the beta tag's message (CODERHELM STABLE ATTESTATION block, never matched by the install's release attestation parser).
    • 10:17 UTC: the newest beta whose GitHub release is 24h old, with no open release-blocker issue naming it, whose stable number doesn't exist and sorts above every stable release, is promoted: the stable tag is created with the pre-signed statement (verified first with cosign, as installs do), and release.yml builds and signs that tag's own files. latest.json and beta.json move.
    • By hand: mode: promote (optional version, ignores the age, still honours blockers), mode: beta, mode: stable.
  • Deploy (deploy.yml): scheduled deploys no longer cut releases. workflow_dispatch input stable_release (default off) releases the deployed commit straight to stable in the same run. The hosted deploy itself is unchanged.
  • Release (release.yml): a tag with a suffix is published as a GitHub prerelease.
  • IAM (infra/lib/worker-stack.ts): release-latest may also write launch/beta.json (same conditional writes).
  • Updater (lambda/updater/handler.py): channels stable and beta, preview accepted as beta (stored settings, config, calls). Each channel reads its own pointer (latest.json, beta.json), cached apart; the pointer is read at least every 5 minutes, so a release shows within minutes even though the list is read hourly. Check for updates re-reads unless read in the last 5 seconds (was 30). Beta to stable never downgrades.
  • Config and installer: updates.channel / --channel take beta (preview kept as the old name).
  • Docs: a Channels section in docs/self-host/updates.md; updates-security.md and config-reference.md updated.

Verification

  • python3 -m unittest discover -s lambda/tests: 320 OK (new: beta channel, preview alias, per-channel hints, a nightly visible within minutes, no downgrade).
  • python3 -m unittest discover -s .github/scripts: 55 OK (new: beta planning, promotion rules, blockers, by-hand promotion, workflow wiring, a real-git beta and promotion).
  • python3 .github/scripts/check_workflows.py and actionlint (with shellcheck): clean.
  • infra: npm test 145 pass. installer: npm test 171 pass.
  • selfhost/versions.json: dashboard pinned to ad28b8e28c4a59829d807b1dc8a056a9d37e24b8 (Settings, System: choose Stable or Beta (nightly) updates coderhelm#317, the Settings, System channel choice); node scripts/check-dashboard-ref.mjs passes.

Releases:
- Nightly (auto-release.yml schedule): when anything that ships changed,
  a beta vX.Y.Z-beta.YYYYMMDD of main's deployed head, a GitHub prerelease
  signed and checked like every release; launch/beta.json names it. Each
  beta has its own X.Y.Z, so a promoted one always sorts below later betas.
- The beta's run also signs the statement for its stable tag vX.Y.Z (same
  commit) and keeps it in the beta's tag message. A second daily schedule
  (10:17 UTC, hours after the 06:17 nightly) promotes the newest beta out
  for 24 hours with no open release-blocker issue naming it: it checks
  that signature and creates the stable tag with it,
  and release.yml builds and signs that tag's own files. launch/latest.json
  (and beta.json) move. Installs keep checking exactly what they check now.
- By hand: promote now (optionally a given beta, whatever its age), a beta
  now, or a stable release now; deploy.yml by hand with "stable release"
  releases the commit it deployed straight to stable. Scheduled deploys no
  longer cut releases; the hosted deploy itself is unchanged.
- The release-latest role may also write launch/beta.json.

Updater: the channels are stable and beta ("preview" still accepted, read
as beta). Each channel reads its own pointer (latest.json, beta.json),
cached apart, at least every 5 minutes, so a new release shows within
minutes; Check for updates reads again unless read seconds ago. Moving from
beta to stable never goes back: the install waits for a newer stable.

Config and installer take updates.channel / --channel beta (preview kept as
its old name). Docs: Channels in docs/self-host/updates.md.

Dashboard pinned to ad28b8e (CoderHelm/coderhelm#317): Settings, System
chooses Stable or Beta (nightly).
@nambok
nambok merged commit 88fee61 into main Nov 5, 2026
10 checks passed
@nambok
nambok deleted the selfhost-update-channels branch November 5, 2026 04:51
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.

1 participant