Skip to content

Commit b858b12

Browse files
committed
fixes
1 parent e5c5760 commit b858b12

File tree

7 files changed

+95
-15
lines changed

7 files changed

+95
-15
lines changed

cmd/push-validator/cmd_version.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ type updateChecker interface {
7171
}
7272

7373
// checkForUpdateBackground performs a non-blocking update check.
74-
// Uses cache to avoid checking more than once per 24 hours.
74+
// Uses cache to avoid checking more than once per 10 minutes.
7575
// Stores result in updateCheckResult global for use by PersistentPostRun.
7676
func checkForUpdateBackground() {
7777
cfg := loadCfg()
@@ -85,6 +85,21 @@ func checkForUpdateBackground() {
8585
}
8686
}
8787

88+
// checkForUpdateFresh performs a fresh update check, bypassing cache.
89+
// Used by status and dashboard commands for immediate notification.
90+
func checkForUpdateFresh() {
91+
cfg := loadCfg()
92+
result, err := update.ForceCheck(cfg.HomeDir, Version)
93+
if err != nil {
94+
return // Silently fail
95+
}
96+
if result != nil && result.UpdateAvailable {
97+
updateCheckMu.Lock()
98+
updateCheckResult = result
99+
updateCheckMu.Unlock()
100+
}
101+
}
102+
88103
// checkForUpdateWith is the testable core of checkForUpdateBackground.
89104
func checkForUpdateWith(
90105
homeDir string,
@@ -176,3 +191,10 @@ func shouldSkipUpdateCheck(cmd *cobra.Command) bool {
176191
}
177192
return false
178193
}
194+
195+
// shouldForceFreshUpdateCheck returns true for commands that need immediate update notification.
196+
// These commands bypass the cache and always make a fresh network call to GitHub.
197+
func shouldForceFreshUpdateCheck(cmd *cobra.Command) bool {
198+
cmdName := cmd.Name()
199+
return cmdName == "status" || cmdName == "dashboard"
200+
}

cmd/push-validator/cmd_version_extra_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (m *mockUpdateChecker) Check() (*update.CheckResult, error) {
2121
func TestCheckForUpdateWith_CacheValid_UpdateAvailable(t *testing.T) {
2222
loadCache := func(homeDir string) (*update.CacheEntry, error) {
2323
return &update.CacheEntry{
24-
CheckedAt: time.Now().Add(-1 * time.Hour),
24+
CheckedAt: time.Now().Add(-5 * time.Minute), // Within 10-minute cache TTL
2525
LatestVersion: "2.0.0",
2626
UpdateAvailable: true,
2727
}, nil
@@ -47,7 +47,7 @@ func TestCheckForUpdateWith_CacheValid_UpdateAvailable(t *testing.T) {
4747
func TestCheckForUpdateWith_CacheValid_NoUpdate(t *testing.T) {
4848
loadCache := func(homeDir string) (*update.CacheEntry, error) {
4949
return &update.CacheEntry{
50-
CheckedAt: time.Now().Add(-1 * time.Hour),
50+
CheckedAt: time.Now().Add(-5 * time.Minute), // Within 10-minute cache TTL
5151
LatestVersion: "1.0.0",
5252
UpdateAvailable: false,
5353
}, nil
@@ -153,7 +153,7 @@ func TestCheckForUpdateWith_CacheValid_SameVersion(t *testing.T) {
153153
// Cache says update available but we're now on the latest version
154154
loadCache := func(homeDir string) (*update.CacheEntry, error) {
155155
return &update.CacheEntry{
156-
CheckedAt: time.Now().Add(-1 * time.Hour),
156+
CheckedAt: time.Now().Add(-5 * time.Minute), // Within 10-minute cache TTL
157157
LatestVersion: "1.0.0",
158158
UpdateAvailable: true,
159159
}, nil

cmd/push-validator/root_cobra.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,13 @@ var rootCmd = &cobra.Command{
5858
// Start background update check (non-blocking)
5959
// Skip for installation-related commands where notifications are disruptive
6060
if !shouldSkipUpdateCheck(cmd) {
61-
go checkForUpdateBackground()
61+
// Use fresh check (bypass cache) for status/dashboard commands
62+
// to ensure immediate notification of new versions
63+
if shouldForceFreshUpdateCheck(cmd) {
64+
go checkForUpdateFresh()
65+
} else {
66+
go checkForUpdateBackground()
67+
}
6268
}
6369
},
6470
PersistentPostRun: func(cmd *cobra.Command, args []string) {

internal/dashboard/dashboard.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,23 @@ func (m *Dashboard) Init() tea.Cmd {
201201
return tea.Batch(
202202
m.spinner.Tick,
203203
m.fetchCmd(),
204+
m.updateCheckCmd(), // Fresh update check on startup
204205
tickCmd(m.opts.RefreshInterval),
205206
)
206207
}
207208

209+
// updateCheckCmd performs a fresh update check on dashboard startup.
210+
// This bypasses the cache to ensure immediate notification of new versions.
211+
func (m *Dashboard) updateCheckCmd() tea.Cmd {
212+
return func() tea.Msg {
213+
result, err := update.ForceCheck(m.opts.Config.HomeDir, m.opts.CLIVersion)
214+
if err != nil {
215+
return updateCheckResultMsg{result: nil}
216+
}
217+
return updateCheckResultMsg{result: result}
218+
}
219+
}
220+
208221
// Update handles messages (Bubble Tea lifecycle)
209222
func (m *Dashboard) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
210223
switch msg := msg.(type) {
@@ -274,6 +287,14 @@ func (m *Dashboard) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
274287
var cmd tea.Cmd
275288
m.spinner, cmd = m.spinner.Update(msg)
276289
return m, cmd
290+
291+
case updateCheckResultMsg:
292+
// Fresh update check completed on startup
293+
if msg.result != nil && msg.result.UpdateAvailable {
294+
m.data.UpdateInfo.Available = true
295+
m.data.UpdateInfo.LatestVersion = msg.result.LatestVersion
296+
}
297+
return m, nil
277298
}
278299

279300
return m, nil

internal/dashboard/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/pushchain/push-validator-cli/internal/config"
88
"github.com/pushchain/push-validator-cli/internal/metrics"
99
"github.com/pushchain/push-validator-cli/internal/process"
10+
"github.com/pushchain/push-validator-cli/internal/update"
1011
)
1112

1213
// Message types for Bubble Tea event loop - ensures deterministic control flow
@@ -34,6 +35,11 @@ type forceRefreshMsg struct{}
3435
// toggleHelpMsg is sent when user presses '?' to toggle help overlay
3536
type toggleHelpMsg struct{}
3637

38+
// updateCheckResultMsg is sent when the startup update check completes
39+
type updateCheckResultMsg struct {
40+
result *update.CheckResult
41+
}
42+
3743
// DashboardData aggregates all data shown in the dashboard
3844
type DashboardData struct {
3945
// Reuse existing metrics collector

internal/update/cache.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,27 @@ func SaveCache(homeDir string, entry *CacheEntry) error {
5555
func IsCacheValid(entry *CacheEntry) bool {
5656
return time.Since(entry.CheckedAt) < cacheDuration
5757
}
58+
59+
// ForceCheck performs a fresh update check, ignoring cache.
60+
// Used by status and dashboard commands for immediate notification.
61+
// Updates the cache after checking.
62+
func ForceCheck(homeDir, currentVersion string) (*CheckResult, error) {
63+
updater, err := New(currentVersion)
64+
if err != nil {
65+
return nil, err
66+
}
67+
68+
result, err := updater.Check()
69+
if err != nil {
70+
return nil, err
71+
}
72+
73+
// Update cache with fresh result
74+
_ = SaveCache(homeDir, &CacheEntry{
75+
CheckedAt: time.Now(),
76+
LatestVersion: result.LatestVersion,
77+
UpdateAvailable: result.UpdateAvailable,
78+
})
79+
80+
return result, nil
81+
}

internal/update/cache_test.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ func TestLoadCache_InvalidJSON(t *testing.T) {
111111
}
112112

113113
func TestIsCacheValid(t *testing.T) {
114+
// Note: cacheDuration is 10 minutes
114115
tests := []struct {
115116
name string
116117
checkedAt time.Time
@@ -122,28 +123,28 @@ func TestIsCacheValid(t *testing.T) {
122123
want: true,
123124
},
124125
{
125-
name: "fresh cache - 1 hour ago",
126-
checkedAt: time.Now().Add(-1 * time.Hour),
126+
name: "fresh cache - 5 minutes ago",
127+
checkedAt: time.Now().Add(-5 * time.Minute),
127128
want: true,
128129
},
129130
{
130-
name: "fresh cache - 23 hours ago",
131-
checkedAt: time.Now().Add(-23 * time.Hour),
131+
name: "fresh cache - 9 minutes ago",
132+
checkedAt: time.Now().Add(-9 * time.Minute),
132133
want: true,
133134
},
134135
{
135-
name: "stale cache - 25 hours ago",
136-
checkedAt: time.Now().Add(-25 * time.Hour),
136+
name: "stale cache - 11 minutes ago",
137+
checkedAt: time.Now().Add(-11 * time.Minute),
137138
want: false,
138139
},
139140
{
140-
name: "stale cache - 48 hours ago",
141-
checkedAt: time.Now().Add(-48 * time.Hour),
141+
name: "stale cache - 1 hour ago",
142+
checkedAt: time.Now().Add(-1 * time.Hour),
142143
want: false,
143144
},
144145
{
145-
name: "stale cache - 7 days ago",
146-
checkedAt: time.Now().Add(-7 * 24 * time.Hour),
146+
name: "stale cache - 24 hours ago",
147+
checkedAt: time.Now().Add(-24 * time.Hour),
147148
want: false,
148149
},
149150
}

0 commit comments

Comments
 (0)