@@ -93,6 +93,7 @@ type Server struct {
9393 dagStore exec.DAGStore
9494 licenseManager * license.Manager
9595 remoteNodeResolver * remotenode.Resolver
96+ upgradeStore upgrade.CacheStore
9697}
9798
9899// ServerOption is a functional option for configuring the Server.
@@ -312,13 +313,6 @@ func NewServer(ctx context.Context, cfg *config.Config, dr exec.DAGStore, drs ex
312313 logger .Warn (ctx , "Failed to create upgrade check store" , tag .Error (err ))
313314 }
314315
315- // Check for updates asynchronously (populates cache for next startup)
316- if upgradeStore != nil {
317- go func () { _ , _ = upgrade .CheckAndUpdateCache (upgradeStore , config .Version ) }()
318- }
319-
320- updateAvailable , latestVersion := getUpdateInfo (upgradeStore )
321-
322316 // Note: SSO/OIDC gating is applied after opts are processed (see below)
323317
324318 srv := & Server {
@@ -333,6 +327,7 @@ func NewServer(ctx context.Context, cfg *config.Config, dr exec.DAGStore, drs ex
333327 metricsRegistry : mr ,
334328 dagStore : dr ,
335329 remoteNodeResolver : remoteNodeResolver ,
330+ upgradeStore : upgradeStore ,
336331 funcsConfig : funcsConfig {
337332 NavbarColor : cfg .UI .NavbarColor ,
338333 NavbarTitle : cfg .UI .NavbarTitle ,
@@ -350,8 +345,7 @@ func NewServer(ctx context.Context, cfg *config.Config, dr exec.DAGStore, drs ex
350345 TerminalEnabled : cfg .Server .Terminal .Enabled && authSvc != nil ,
351346 GitSyncEnabled : cfg .GitSync .Enabled ,
352347 SetupRequiredChecker : & setupChecker {authSvc : authSvc , fallback : setupRequired },
353- UpdateAvailable : updateAvailable ,
354- LatestVersion : latestVersion ,
348+ UpdateChecker : & updateChecker {store : upgradeStore },
355349 AgentEnabledChecker : agentConfigStore ,
356350 },
357351 }
@@ -420,12 +414,16 @@ func NewServer(ctx context.Context, cfg *config.Config, dr exec.DAGStore, drs ex
420414 return srv , nil
421415}
422416
423- // getUpdateInfo returns update availability and latest version from cache.
424- func getUpdateInfo (store upgrade.CacheStore ) (updateAvailable bool , latestVersion string ) {
425- if store == nil {
417+ // updateChecker implements UpdateChecker by reading from the upgrade cache store.
418+ type updateChecker struct {
419+ store upgrade.CacheStore
420+ }
421+
422+ func (u * updateChecker ) GetUpdateInfo () (bool , string ) {
423+ if u .store == nil {
426424 return false , ""
427425 }
428- cache := upgrade .GetCachedUpdateInfo (store )
426+ cache := upgrade .GetCachedUpdateInfo (u . store )
429427 if cache == nil {
430428 return false , ""
431429 }
@@ -842,12 +840,36 @@ func (srv *Server) Serve(ctx context.Context) error {
842840 metrics .StartUptime (ctx )
843841 logger .Info (ctx , "Server is starting" , tag .Addr (addr ))
844842
843+ srv .startPeriodicUpdateCheck (ctx )
844+
845845 go srv .startServer (ctx )
846846 srv .setupGracefulShutdown (ctx )
847847
848848 return nil
849849}
850850
851+ // startPeriodicUpdateCheck runs an initial update check and then repeats
852+ // every CacheTTL interval so that long-running servers pick up new releases.
853+ func (srv * Server ) startPeriodicUpdateCheck (ctx context.Context ) {
854+ if srv .upgradeStore == nil {
855+ return
856+ }
857+ go func () {
858+ _ , _ = upgrade .CheckAndUpdateCache (srv .upgradeStore , config .Version )
859+
860+ ticker := time .NewTicker (upgrade .CacheTTL )
861+ defer ticker .Stop ()
862+ for {
863+ select {
864+ case <- ctx .Done ():
865+ return
866+ case <- ticker .C :
867+ _ , _ = upgrade .CheckAndUpdateCache (srv .upgradeStore , config .Version )
868+ }
869+ }
870+ }()
871+ }
872+
851873func (srv * Server ) configureAPIPath () string {
852874 apiV1BasePath := path .Join (srv .config .Server .BasePath , "api/v1" )
853875 return ensureLeadingSlash (apiV1BasePath )
0 commit comments