detached 916a6f7

fix(update): re-check every launch when the cache says up-to-date

CheckForUpdate used to trust any cache entry less than 12h old
whose CurrentAt matched the running binary. Problem: if a user
installs v0.0.72 at 16:05 (cache: up-to-date) and a v0.0.73
release ships at 18:27, relaunching zot at 18:29 hits the
fresh cache and never calls the github api \u2014 the update
banner stays hidden until either 12h pass or the binary is
rebuilt. Noticed in the wild: v0.0.73 latest, zot 0.0.72
running, no banner.

Refined logic: only short-circuit on a cached entry when it
already reports Available=true. If the cache says "up to
date" we let the flow fall through to fetchLatestRelease and
reconcile. The api call is a single ~4s timeout request;
cheap enough to do on every up-to-date launch, and the common
"cache already shows available" path still skips the network
entirely.

Also: manually cleared /Users/pat/Library/Application
Support/zot/update-check.json on my local box so the next
launch sees the new v0.0.73 release without waiting; that's
user state, not something to commit.
commit
916a6f71d10089261a261af091dc84ad98618c2c
author
patriceckhart · · 2026-04-20 16:31 UTC
parent
13b8947
signature
unsigned
refs
tag: v0.0.74
1 file · +13 -1
M internal/agent/update.go +13 -1
@@ -62,8 +62,20 @@ func CheckForUpdate(ctx context.Context, zotHome, currentVersion string) UpdateI
6262 cachePath := filepath.Join(zotHome, updateCheckFile)
6363 if c, ok := readUpdateCache(cachePath); ok {
6464 // Cache is fresh and tracks the same binary version.
65+ // Additional guard: only trust the cache when it already
66+ // reports an available update. If the cache says "up to
67+ // date" (Latest <= Current) we re-check anyway so a
68+ // release published after the last launch is picked up
69+ // without waiting out the full TTL. The API call is a
70+ // single 4s request; cheap to do on every up-to-date
71+ // launch, and skips entirely for the common "installed a
72+ // new version, cache reports update available" path.
6573 if time.Since(c.CheckedAt) < updateCheckTTL && c.CurrentAt == currentVersion {
66- return buildInfo(currentVersion, c.Latest, c.URL)
74+ info := buildInfo(currentVersion, c.Latest, c.URL)
75+ if info.Available {
76+ return info
77+ }
78+ // fall through to re-check
6779 }
6880 }
6981
@@ -62,8 +62,20 @@ func CheckForUpdate(ctx context.Context, zotHome, currentVersion string) UpdateI
62 cachePath := filepath.Join(zotHome, updateCheckFile)62 cachePath := filepath.Join(zotHome, updateCheckFile)
63 if c, ok := readUpdateCache(cachePath); ok {63 if c, ok := readUpdateCache(cachePath); ok {
64 // Cache is fresh and tracks the same binary version.64 // Cache is fresh and tracks the same binary version.
65+ // Additional guard: only trust the cache when it already
66+ // reports an available update. If the cache says "up to
67+ // date" (Latest <= Current) we re-check anyway so a
68+ // release published after the last launch is picked up
69+ // without waiting out the full TTL. The API call is a
70+ // single 4s request; cheap to do on every up-to-date
71+ // launch, and skips entirely for the common "installed a
72+ // new version, cache reports update available" path.
65 if time.Since(c.CheckedAt) < updateCheckTTL && c.CurrentAt == currentVersion {73 if time.Since(c.CheckedAt) < updateCheckTTL && c.CurrentAt == currentVersion {
66- return buildInfo(currentVersion, c.Latest, c.URL)74+ info := buildInfo(currentVersion, c.Latest, c.URL)
75+ if info.Available {
76+ return info
77+ }
78+ // fall through to re-check
67 }79 }
68 }80 }
69 81

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close