feat(updates): tell the user in-app when a newer Hive is published - #18
Open
sreekar2403 wants to merge 1 commit into
Open
feat(updates): tell the user in-app when a newer Hive is published#18sreekar2403 wants to merge 1 commit into
sreekar2403 wants to merge 1 commit into
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 isprivate, sonpm outdatedis 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.tsasks GitHub two things about theoriginremote:package.jsonversionHEAD—/compare/<sha>...<branch>→ahead_byEither 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
originmeans a fork checks itself rather than upstream.Endpoints.
GET /api/updatesserves a cached answer (updates.checkIntervalHours, default 6h) and only reaches GitHub when stale — cheap on every page load.POST /api/updates/checkis the "check now" button and always goes out. A background watcher re-checks on the same interval and broadcastsupdate:availableon the existing SSE stream, which is what lets a window left open overnight notice a release without a reload.Client.
useUpdateCheckdrives a pill in theTopBar(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 withgit stashwhen the Hive checkout itself is dirty, and says so —git pullon 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
errorstring rather than throwing. An update check that breaks the app because GitHub rate-limited it is worse than no update check at all.updates.enabled: false→ the UI shows nothing at allGITHUB_TOKENis used for the rate limit when set. Concurrent checks share one in-flight request, so mashing "check again" cannot spend the whole limit.Config
Testing
19 new tests in
updates.test.tsdriving 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.jsonis still0.1.0while the latest release isv1.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