Skip to content

feat: support beta channel for prerelease auto-upgrade #251

@mrcfps

Description

@mrcfps

Background

The release workflow already publishes prereleases correctly (release.yml:75-78 flags any tag containing - as a GitHub prerelease), and GitHub's /releases/latest endpoint excludes prereleases by design. As a result, looper upgrade on the default (stable) channel will never auto-upgrade users to a prerelease — which is the desired behavior for stable.

However, there is currently no way to opt into prereleases from the CLI. The spec at specs/2026-04-22-install-upgrade-strategy/spec.md:768-780 already describes a --channel beta flag, but the code has no channel concept (channel only appears as a metadata field today).

This issue tracks adding first-class beta channel support so internal users and early adopters can dogfood prereleases without breaking the stable auto-upgrade contract.

Goals

  • Let users opt into prereleases via an explicit, persistent channel setting.
  • Keep the stable channel completely unaffected — daily auto-upgrade must never pull a prerelease for stable users.
  • Make the channel visible in looper status / version output so users always know which track they are on.

Non-goals

  • Multiple parallel beta channels (e.g. alpha, nightly). Start with a single beta track.
  • Downgrade support. Switching from beta back to stable does not roll back the binary; it just stops pulling future prereleases until stable catches up.

Proposed design

CLI surface

  • New flag on looper upgrade and looper daemon install: --channel {stable,beta} (default stable).
  • Persisted preference in ~/.looper/config.json under a new upgrade.channel key so the daily auto-upgrade respects the choice without re-passing the flag.
  • looper status and --version --json include the active channel.

Resolver changes (internal/cliapp/upgrade.go, daemon_install.go)

  • fetchReleaseMetadata(ctx, tag) stays as-is for explicit tags.
  • Add fetchLatestRelease(ctx, channel):
    • stableGET /repos/{owner}/{repo}/releases/latest (current behavior).
    • betaGET /repos/{owner}/{repo}/releases?per_page=30, filter out drafts, then pick the highest semver among entries where prerelease == true || prerelease == false. (Beta users should also receive stable releases when stable > latest beta.)
  • isSemverUpgradeAvailable already orders prereleases correctly (semver_test.go:17-22), so no changes needed there.

Auto-upgrade behavior

  • Daily auto-upgrade reads upgrade.channel from config; defaults to stable.
  • Beta users get prereleases automatically once opted in. Stable users remain protected.

Telemetry / logging

  • Log the resolved channel and selected tag in upgrade output (both human and --json) so support can quickly tell which track a machine is on.

Acceptance criteria

  • looper upgrade --channel beta pulls the latest prerelease when one is newer than the installed version.
  • looper upgrade --channel beta --check --json reports the prerelease tag without installing.
  • looper upgrade (no flag, default config) never selects a prerelease, even if one is newer.
  • Channel preference persists across runs via ~/.looper/config.json.
  • Beta channel still upgrades to a stable release when stable > latest beta.
  • looper status shows the active channel.
  • Tests cover: stable ignores prereleases, beta picks prerelease, beta picks stable when newer, channel persistence, channel flag overrides config.

References

  • specs/2026-04-22-install-upgrade-strategy/spec.md:768-780 — original prerelease/channel spec
  • specs/2026-04-22-install-upgrade-strategy/checklist.md:142,178 — open checklist items for prerelease channel
  • internal/cliapp/upgrade.go:500-530 — current fetchLatestCLIVersion / fetchLatestDaemonRelease
  • internal/cliapp/semver.go:92-151 — prerelease-aware semver comparison (already correct)
  • .github/workflows/release.yml:75-84 — prerelease tag detection in release pipeline

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority:p3Lower priority: nice-to-have or longer-term work

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions