Show a progress bar and live log while Hammerhead, edges and sites update
CI / test (push) Successful in 5m21s
Release / release (push) Successful in 6m9s

The updater reports each step (checking, downloading with percent, verifying, installing, restarting).
Edges and sites send that progress and their log with their reports; Hammerhead serves it to a dialog
that opens when an update starts, and keeps its own last update log on disk across the restart.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
VeillaxandClaude Sonnet 5.5 committed 2026-10-26 18:05:42 -06:00
1 parent 9bd48917c7
commit 2b7cb5dd35
11 files changed
+306 -13

No files matched your search

+23 -1
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"net/http"
"os"
"strings"
"sync"
"time"
@@ -45,13 +46,32 @@ func (s *Server) applyUpdate(w http.ResponseWriter, r *http.Request, p *principa
fail(w, 400, errors.New("auto-update is off: set HAMMERHEAD_AUTO_UPDATE=1 and restart Hammerhead, or update it by hand"))
return
}
u := &s.selfUpd
u.mu.Lock()
if u.running {
u.mu.Unlock()
fail(w, 409, errors.New("an update is already running"))
return
}
u.running, u.stage, u.pct, u.lines = true, "starting", 0, nil
u.mu.Unlock()
os.WriteFile(s.updateLogPath(), nil, 0o600) // a new run starts a new log
cfg := s.updateConfig("hammerhead", s.opts.Version)
cfg.Restart = func(exe string) error { s.Close(); return selfupdate.Restart(exe) }
cfg.Progress = func(e selfupdate.Event) { s.selfNote(e.Stage, e.Pct, e.Line) }
cfg.Restart = func(exe string) error {
s.selfNote("restarting", 100, "Closing the database and restarting")
s.Close()
return selfupdate.Restart(exe)
}
s.selfNote("starting", 0, "Update requested by "+p.User.Email)
s.audit("", p.User.Email, "server.update", "")
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
selfupdate.CheckOnce(ctx, cfg) // on success the process is replaced and this never returns
u.mu.Lock()
u.running = false
u.mu.Unlock()
}()
writeJSON(w, 200, map[string]any{"started": true})
}
@@ -121,6 +141,7 @@ func (s *Server) updateEdge(w http.ResponseWriter, r *http.Request, p *principal
fail(w, 400, err)
return
}
delete(s.updRuns, e.ID) // a new request starts a new log
s.bump()
s.audit("", p.User.Email, "edge.update", e.Name)
ok(w)
@@ -142,6 +163,7 @@ func (s *Server) updateSite(w http.ResponseWriter, r *http.Request, c *orgCtx) {
fail(w, 400, err)
return
}
delete(s.updRuns, st.ID)
s.bump()
s.audit(c.Org.ID, c.Actor, "site.update", st.Name)
ok(w)