Skip to content

feat(updates): tell the user in-app when a newer Hive is published - #18

Open
sreekar2403 wants to merge 1 commit into
mainfrom
feat/self-update-notice
Open

feat(updates): tell the user in-app when a newer Hive is published#18
sreekar2403 wants to merge 1 commit into
mainfrom
feat/self-update-notice

Conversation

@sreekar2403

Copy link
Copy Markdown
Owner

What / Why

Hive is installed by cloning the repo and linking bin/hive.js. Someone running it today has no way to learn that changes were pushed — and there is no registry to poll, since the root package is private, so npm outdated is not the answer either.

This adds a self-update check: Hive noticing that a newer Hive exists, and saying so in the app. (Not about the projects Hive works on.)

How it works

packages/server/src/updates.ts asks GitHub two things about the origin remote:

  • the newest published release, compared against the root package.json version
  • how many commits the upstream default branch is ahead of local HEAD/compare/<sha>...<branch>ahead_by

Either one being ahead flags an update. Both signals are needed: a repo that has never cut a release still ships changes, and someone sitting on an old tag is behind even with a clean branch. Reading the remote from origin means a fork checks itself rather than upstream.

Endpoints. GET /api/updates serves a cached answer (updates.checkIntervalHours, default 6h) and only reaches GitHub when stale — cheap on every page load. POST /api/updates/check is the "check now" button and always goes out. A background watcher re-checks on the same interval and broadcasts update:available on the existing SSE stream, which is what lets a window left open overnight notice a release without a reload.

Client. useUpdateCheck drives a pill in the TopBar (UpdateNotice.tsx) opening a dialog with the release notes and a copyable upgrade command.

Two deliberate calls

Hive never updates itself. It is the user's git checkout, with agents running against their working tree; pulling under them mid-task would be the most hostile thing this app could do. The notice hands over git pull && pnpm install, prefixed with git stash when the Hive checkout itself is dirty, and says so — git pull on top of local edits is how somebody loses work they did not know they had.

Dismissal is keyed to the upstream state, not to a boolean. Dismissing "0.2.0 is out" must not also silence "0.3.0 is out" a month later.

Failure behaviour

Every path degrades to "no update available" plus an error string rather than throwing. An update check that breaks the app because GitHub rate-limited it is worse than no update check at all.

  • offline / DNS failure / timeout → error surfaced, nothing claimed
  • 403/429 → "GitHub rate limit reached", explicitly not "you are up to date"
  • no releases yet (404) → normal, falls through to the commit signal
  • local commit unknown upstream (a local branch, a rebase) → not an error worth showing
  • no GitHub remote, or updates.enabled: false → the UI shows nothing at all

GITHUB_TOKEN is used for the rate limit when set. Concurrent checks share one in-flight request, so mashing "check again" cannot spend the whole limit.

Config

"updates": {
  "enabled": true,          // false = never reach GitHub, no notice ever
  "repo": "",               // empty = read it from the origin remote
  "checkIntervalHours": 6
}

Testing

19 new tests in updates.test.ts driving a fake GitHub — version ordering (including prereleases and short tags), both remote URL shapes, release vs. commit signals, draft releases, an older release than installed, rate limits, offline, unknown commits, TTL caching, forced refresh, and in-flight collapsing.

Full suite: 478 passing. Both packages typecheck; eslint and prettier clean. Also ran one live check against this repo end to end.

Note, not fixed here

The root package.json is still 0.1.0 while the latest release is v1.3.2, so the release signal will fire for everyone until the version is bumped in step with the tags. Left alone deliberately — changing the version number is a separate call.

🤖 Generated with Claude Code

Hive is installed by cloning the repo and linking bin/hive.js, so someone
running it has no way to learn that changes were pushed. There is no
registry to poll either — the root package is private — so `npm outdated`
is not the answer.

updates.ts asks GitHub two things about the `origin` remote: the newest
published release, and how many commits the upstream default branch is
ahead of local HEAD (/compare/<sha>...<branch> -> ahead_by). Either being
ahead flags an update. The commit signal is there because a repo that has
never cut a release still ships changes, and the release signal is there
because someone sitting on an old tag is behind even with a clean branch.

Every failure path degrades to "no update" plus an error string rather
than throwing: offline, rate-limited, no remote, or a local commit that
upstream has never seen (a local branch or a rebase — not an error worth
showing anybody). Answers are cached for updates.checkIntervalHours (6h),
and concurrent checks share one in-flight request so a mashed "check
again" cannot spend the whole GitHub rate limit.

GET /api/updates serves the cache, POST /api/updates/check forces a
refresh, and a background watcher broadcasts update:available over the
existing SSE stream — which is what lets a window left open overnight
notice a release without a reload. The client hook drives a pill in the
top bar with the release notes and the upgrade command.

Hive deliberately does not update itself. It is the user's git checkout
with agents running against their working tree; pulling under them
mid-task would be the most hostile thing this app could do. It hands over
`git pull && pnpm install`, prefixed with `git stash` when the Hive
checkout is dirty, and the notice is dismissible per upstream state so
dismissing 0.2.0 does not silence 0.3.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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