Hotfix: Auto update capability.

This commit is contained in:
2025-12-15 01:16:30 +00:00
parent 637412e8bb
commit bbd6c3f1ff
4 changed files with 93 additions and 6 deletions
+39
View File
@@ -28,6 +28,7 @@ const (
ReleasesAPI = "https://api.github.com/repos/" + GitHubRepo + "/releases/latest"
CheckInterval = 24 * time.Hour
MaxExtractedSize = 100 * 1024 * 1024 // 100MB max per extracted file
RestartDelay = 500 * time.Millisecond
)
// Release represents a GitHub release.
@@ -281,6 +282,12 @@ func (u *Updater) ApplyUpdate(ctx context.Context, info *UpdateInfo) error {
return fmt.Errorf("failed to replace binaries: %w", err)
}
// Clear cache so next check shows no update available
u.mu.Lock()
u.cachedUpdate = nil
u.lastCheck = time.Time{}
u.mu.Unlock()
u.setStatus("done", 1.0, fmt.Sprintf("Updated to v%s. Restart required.", info.LatestVersion))
log.Info().Str("version", info.LatestVersion).Msg("Update applied successfully")
@@ -553,3 +560,35 @@ func isNewerVersion(latest, current string) bool {
return len(latestParts) > len(currentParts)
}
// Restart spawns the new worker binary and exits the current process.
// This should be called after a successful update to apply the new version.
func (u *Updater) Restart() error {
workerPath := filepath.Join(u.installDir, "worker")
// Verify the new binary exists
if _, err := os.Stat(workerPath); err != nil {
return fmt.Errorf("new worker binary not found: %w", err)
}
log.Info().Str("path", workerPath).Msg("Restarting worker with new binary")
// Start the new process
cmd := exec.Command(workerPath) // #nosec G204 -- workerPath is from internal installDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = os.Environ()
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start new worker: %w", err)
}
// Give the new process time to start
time.Sleep(RestartDelay)
// Exit current process - the new one is now running
log.Info().Int("new_pid", cmd.Process.Pid).Msg("New worker started, exiting old process")
os.Exit(0)
return nil // Never reached
}
+27
View File
@@ -750,3 +750,30 @@ func (s *Service) handleUpdateStatus(w http.ResponseWriter, r *http.Request) {
status := s.updater.GetStatus()
writeJSON(w, status)
}
// handleUpdateRestart restarts the worker with the new binary.
func (s *Service) handleUpdateRestart(w http.ResponseWriter, r *http.Request) {
status := s.updater.GetStatus()
if status.State != "done" {
http.Error(w, "no update has been applied", http.StatusBadRequest)
return
}
// Send response before restarting
writeJSON(w, map[string]interface{}{
"success": true,
"message": "Restarting worker...",
})
// Flush the response
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
// Restart in background after response is sent
go func() {
if err := s.updater.Restart(); err != nil {
log.Error().Err(err).Msg("Failed to restart worker")
}
}()
}
+1
View File
@@ -600,6 +600,7 @@ func (s *Service) setupRoutes() {
s.router.Get("/api/update/check", s.handleUpdateCheck)
s.router.Post("/api/update/apply", s.handleUpdateApply)
s.router.Get("/api/update/status", s.handleUpdateStatus)
s.router.Post("/api/update/restart", s.handleUpdateRestart)
// SSE endpoint (works before DB is ready)
s.router.Get("/api/events", s.sseBroadcaster.HandleSSE)