mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-19 22:17:42 +02:00
The console now knows what it is running: the status endpoint compares the binary identity captured at startup (size + nanosecond mtime) with the file on disk, so an update, a rollback or a CLI run that swapped the binary without a restart is reported as needsRestart (with the build time) - a state that used to live only in one job's result and vanished with the process. That state grows a persistent banner and POST /api/system/update/restart (409 when there is nothing to activate or a job owns the tree, 400 without a hook), so a forgotten tick is one click instead of a shell login. The restart tick defaults to on exactly where it is safe: launchd/systemd leave markers in the environment (XPC_SERVICE_NAME, INVOCATION_ID/JOURNAL_STREAM) and the box follows them; an operator's own choice is never second-guessed afterwards. While the process is down the console becomes a recovery view that probes the status endpoint every 2 seconds: 200 means the old process still answers, a dead socket means it is on its way out, and a 401 - only a new process can fail to know the session - reloads the page back to #system-update. A job poll that dies during a requested restart is the restart in flight, not a failure to toast. Also fixed: updateT now asks i18next not to escape interpolated values, because the console escapes at render time - the apply confirm dialog has been showing branch names with / ever since they contained a slash, and the new banner showed dates the same way. Tests: 7 new console contract tests (36 total: banner, tick default, restart confirm/refusal, watchdog 200-vs-401, timeout copy, the in-flight poll death) plus handler tests for the stamp and the endpoint, all mutation-probed; docs describe the pending-restart state and the self-recovery.
42 lines
1.4 KiB
Go
42 lines
1.4 KiB
Go
package update
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
// BinaryStamp identifies the executable on disk: a rebuild, a rename-swap from an update,
|
|
// or a rollback that puts the previous build back all change it. Size plus nanosecond
|
|
// mtime is enough to tell two builds apart, and it costs one stat() on a request path -
|
|
// hashing the file would not.
|
|
type BinaryStamp struct {
|
|
Exists bool `json:"exists"`
|
|
Size int64 `json:"size,omitempty"`
|
|
ModTime time.Time `json:"modTime,omitempty"`
|
|
}
|
|
|
|
// StampBinary reads the install's binary as it is on disk right now. A missing file is a
|
|
// legitimate stamp rather than an error: a source checkout has no binary until the first
|
|
// update builds one.
|
|
func StampBinary(root string, opts Options) BinaryStamp {
|
|
info, err := os.Stat(filepath.Join(root, opts.binaryName()))
|
|
if err != nil {
|
|
return BinaryStamp{}
|
|
}
|
|
return BinaryStamp{Exists: true, Size: info.Size(), ModTime: info.ModTime()}
|
|
}
|
|
|
|
// Same reports whether two stamps describe the same file. The comparison is deliberately
|
|
// stricter than "the disk file is newer": a rollback moves an older build back into place,
|
|
// and the process running the newer one still has to restart for the rollback to be real.
|
|
func (s BinaryStamp) Same(other BinaryStamp) bool {
|
|
if s.Exists != other.Exists {
|
|
return false
|
|
}
|
|
if !s.Exists {
|
|
return true
|
|
}
|
|
return s.Size == other.Size && s.ModTime.Equal(other.ModTime)
|
|
}
|