Documentation
¶
Overview ¶
Package updatecheck implements the passive "new release available" check: it throttles GitHub release lookups to once per check interval, caches the result in the sprout state directory, and answers "is there a newer version than X" for the CLI notice and the WebUI banner.
All persistence is best-effort: any read/write failure degrades to "no cached state" rather than an error, because the check must never disrupt startup or command output.
Index ¶
- Constants
- func CachedNewer(current string, now time.Time) (latest string, ok bool)
- func FetchLatestTag(ctx context.Context) (string, error)
- func FetchLatestTagLongTimeout(ctx context.Context) (string, error)
- func IsNewer(current, latest string) bool
- func NeedsCheck(s State, now time.Time) bool
- func NormalizeVersion(v string) string
- func NoticeFor(current string, now time.Time) (latest string, ok bool)
- func RefreshMaybe(ctx context.Context, now time.Time)
- func Skipped(current string, disabled bool) bool
- type State
Constants ¶
const ( // CheckInterval is the minimum time between GitHub release lookups. // The unauthenticated GitHub API allows 60 req/hr per IP; one lookup // per process start at this throttle stays far below that even with // many sprout invocations, since only the first start in the window // hits the network. CheckInterval = 24 * time.Hour // NoticeInterval is how often the CLI re-shows the update notice for // the same newer version. Each new cached version notifies immediately. NoticeInterval = 24 * time.Hour )
Variables ¶
This section is empty.
Functions ¶
func CachedNewer ¶
CachedNewer is NoticeFor without the side effect, for read-only consumers like the WebUI bootstrap payload.
func FetchLatestTag ¶
FetchLatestTag returns the most recent non-draft, non-prerelease release tag from GitHub. Bounded by fetchTimeout.
func FetchLatestTagLongTimeout ¶
FetchLatestTagLongTimeout is FetchLatestTag with the upgrade command's generous 5-minute budget: the user explicitly asked for a version lookup, so a slow or proxied link gets the same patience the old inline implementation had.
func IsNewer ¶
IsNewer reports whether latest is a strict semver upgrade over current. Either side failing to parse as semver ("dev", git-hash builds, empty strings) yields false — no notice rather than a wrong notice.
func NeedsCheck ¶
NeedsCheck reports whether the cached state is stale enough to justify another release lookup. A zero LastCheck (first run) always needs one; a future LastCheck (clock moved back) counts as recently checked so a skewed clock can't trigger a lookup on every start.
func NormalizeVersion ¶
NormalizeVersion strips a leading 'v' (or 'V') so v1.2.3 / V1.2.3 / 1.2.3 compare equal, then re-adds the 'v' because upstream release tags carry it. "dev" passes through unchanged.
func NoticeFor ¶
NoticeFor returns the cached newer version when a notice is due: the cached LatestRelease must be a strict semver upgrade over current and not already shown for that version within NoticeInterval. When ok, the shown-marker is persisted so the caller can print unconditionally.
func RefreshMaybe ¶
RefreshMaybe runs a release lookup when the cached state is stale and persists the outcome. It is synchronous but bounded by fetchTimeout; callers invoke it from a goroutine so startup never waits on it.
The in-flight flag dedupes concurrent launches in one process (the interactive CLI and the in-process web server both call this). The lock is released during the network call so notice rendering and bootstrap reads never block on the fetch, and the merge re-reads state afterward so a marker persisted meanwhile survives.
func Skipped ¶
Skipped resolves every skip condition for both the check and its notice: non-semver builds ("dev", empty) can't be compared, the env var opts out, CI environments shouldn't make network calls, and the config knob disables the feature. Callers fold nil-config into disabled=true (fail closed: no config, no GitHub call).
Types ¶
type State ¶
type State struct {
// LastCheck is when the release lookup last ran (success or failure).
// Failures advance it too, so an unreachable GitHub doesn't turn every
// startup into a network attempt.
LastCheck time.Time `json:"last_check,omitempty"`
// LatestRelease is the newest stable release tag seen. Empty until the
// first successful lookup.
LatestRelease string `json:"latest_release,omitempty"`
// NoticeShownAt / NoticeShownFor record when the update notice was
// last printed and for which version, enforcing the once-per-notice-
// interval nag limit.
NoticeShownAt time.Time `json:"notice_shown_at,omitempty"`
NoticeShownFor string `json:"notice_shown_for,omitempty"`
}
State is the persisted update-check cache.