Files
claude-code-usage-bubble/plans/260516-1730-github-release-auto-update/plan.md
T
tiennm99 5fc9d18c67 docs: link install to releases page, add release-process guide
Replaces the "build from source only" Install section with a
Releases-page download path plus a SmartScreen note, and adds
docs/release-process.md so the tag-bump-build flow is captured for
future maintainers.
2026-05-16 12:34:38 +07:00

6.0 KiB

title, description, status, priority, created
title description status priority created
GitHub release CI + auto-update wiring Publish Windows binaries to GitHub Releases via Actions so the existing in-app updater can self-update. pending P2 2026-05-16

GitHub release CI + auto-update wiring

Overview

The self-update subsystem already exists end-to-end in src/update/: release::fetch_latest polls releases/latest on GitHub, parses the tag_name into a Version, picks the asset whose name matches claude-code-usage-bubble.exe (or the first .exe as fallback), and install::begin downloads it + spawns an inline cmd /c handoff that swaps the running exe and relaunches. app.rs wires this to a 24-hour timer (UPDATE_CHECK_INTERVAL_SECS) and the right-click menu ("Check for updates" / "Update available" / "Applying update…").

What is missing is the producer side: no .github/workflows/ directory exists, the repo has no tags, and the README explicitly says "Until packaged binaries are published, build from source". The updater therefore has nothing to pull from. Closing that loop is the whole job.

This plan ships three things: (1) a tag-triggered GitHub Actions workflow that builds release on windows-latest and attaches the exe to a GitHub Release, (2) an end-to-end test that proves a running v0.1.0 actually self-updates to v0.1.1 in the wild, and (3) the docs and release-cutting checklist so future versions ship by pushing a tag.

Out of scope: code signing, winget channel (Channel::Winget stays stubbed), SHA256 sidecar verification (HTTPS + cert pinning by WinHTTP is the security floor; checksum is a nice-to-have for later), and any new updater code paths beyond what the existing code already supports.

Phases

Phase Name Status
1 Release CI workflow Files written, awaiting commit + push
2 End-to-end update test Pending (user-driven: requires tag pushes + Windows runs)
3 Docs and release process Files written, awaiting commit; link verification gated on Phase 2

Key contracts (must not break)

The updater is already shipped logic — these constants are the contract the CI workflow has to satisfy:

Contract Source Value
Asset filename (primary match) src/update/release.rs:7 claude-code-usage-bubble.exe
Asset filename (fallback) src/update/release.rs:67-69 any *.exe
Endpoint src/update/release.rs:45 https://api.github.com/repos/tiennm99/claude-code-usage-bubble/releases/latest
Tag → version parse src/update/release.rs:33-41 strips leading v, splits on -, takes major.minor.patch
Current version source Cargo.toml version bumped per release

A tag like v0.1.1 → parses as Version { 0, 1, 1 }. The workflow MUST upload an asset named exactly claude-code-usage-bubble.exe.

Dependencies

No cross-plan dependencies. The prior plan 260516-0707-cleanroom-rewrite/phase-06-updater-and-remove-notice.md delivered the consumer side and is complete in code (whether its own phase row is checked is independent of this plan).

Validation Log

Session 1 — 2026-05-16

Verification Results

  • Tier: Standard (3 phases → Fact Checker + Contract Verifier)
  • Claims checked: 9
  • Verified: 9 | Failed: 0 | Unverified: 0
  • Claims verified: ASSET_NAME constant at src/update/release.rs:7, matcher at src/update/release.rs:64, fallback at src/update/release.rs:67-69, URL endpoint at src/update/release.rs:45, version parse at src/update/release.rs:33-41, version_action apply branch at src/app.rs:1037-1066, 24-hour interval at src/app.rs:48, Cargo name = "claude-code-usage-bubble" and version = "0.1.0" at Cargo.toml:2-3, README "Until packaged binaries are published" at README.md:58.

Decisions

  1. Version-tag match enforcement: YES, fail-fast in CI. The release workflow must parse Cargo.toml and abort if the tag (e.g. v0.1.1) does not equal the Cargo version. Prevents silent mismatch where a binary self-reports a different version than the release tag — which would in turn break the updater's Version::current() vs Version::parse(tag_name) comparison and either skip a real update or loop on the same one. → Propagated to phase-01-release-ci-workflow.md as a new step + extra success criterion.

  2. Initial release strategy: tag v0.1.0 first, then bump to v0.1.1 for the E2E test. Phase 2 stays as written. Cut v0.1.0 from current main (no Cargo bump needed since Cargo.toml is already 0.1.0), download the asset, bump Cargo to 0.1.1, tag v0.1.1, watch the v0.1.0 binary self-update to v0.1.1. Two real releases, clean test. → No changes needed in Phase 2 — already aligned.

  3. Asset matcher *.exe fallback: defer. Today only one asset ships so the fallback at src/update/release.rs:67-69 is dead code. Once multi-arch lands (x86_64/arm64), the fallback could pick the wrong binary. Tracked as a future cleanup, NOT in scope for this plan. → Documented in Phase 1 success criteria as a non-action.

Whole-Plan Consistency Sweep

  • Files reread: plan.md, phase-01-release-ci-workflow.md, phase-02-end-to-end-update-test.md, phase-03-docs-and-release-process.md
  • Decision deltas checked: 3 (version-match enforcement, initial release version, asset fallback)
  • Reconciled stale references: 4
    • Phase 1 architecture flow + todo list + success criteria updated to include version-tag check
    • Phase 1 non-functional line-count budget bumped 80 → ~100 lines to match the actual YAML after adding the check step
    • Phase 1 flow diagram cleaned up (removed phantom rustup default stable step; added cache + dispatch-draft notation)
    • Phase 3 release-process.md sketch now calls out that CI enforces tag-vs-Cargo match, explaining why step ordering matters
  • Unresolved contradictions: 0