Media build part 1 (unreleased): real restart (R-1026), media apps on every PRESENT drive via an owned compose override (R-1023), dead-drive check from mountinfo, removal never deletes shared/:ro binds (R-1027), safety list knows userdata (R-1032), no drive question for media apps (R-1031)
gates / gates (push) Successful in 46s
gates / gates (push) Successful in 46s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
1 parent
331be17e1e
commit
78c894234c
13 files changed
+984
-9
No files matched your search
@@ -552,3 +552,76 @@ func observationCoversTemplate(observed map[string]InstalledImage, tpl map[strin
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// observeStartTimes maps each compose service of the stack to its container's .State.StartedAt (R-1026). Read through
|
||||
// the recorder's own process boundary (runInstalled: a context and a timeout). The format carries a literal "started"
|
||||
// field so an inspect answer of another shape is ignored rather than misread as a time.
|
||||
func (m *Manager) observeStartTimes(stackDir string, env []string) (map[string]string, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), installedRecordTimeout)
|
||||
defer cancel()
|
||||
bin, argv := m.composeArgv("ps", "-a", "--format", "json")
|
||||
psOut, err := m.runInstalled(ctx, stackDir, env, bin, argv...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing containers: %w", err)
|
||||
}
|
||||
entries, err := parseComposePS(psOut)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
svcOf := map[string]string{}
|
||||
var ids []string
|
||||
for _, e := range entries {
|
||||
if e.Service == "" || e.ID == "" {
|
||||
continue
|
||||
}
|
||||
svcOf[e.ID] = e.Service
|
||||
ids = append(ids, e.ID)
|
||||
}
|
||||
if len(ids) == 0 {
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
sort.Strings(ids)
|
||||
args := append([]string{"inspect", "--type", "container",
|
||||
"--format", "{{.Id}}" + inspectSep + "started" + inspectSep + "{{.State.StartedAt}}"}, ids...)
|
||||
out, err := m.runInstalled(ctx, "", nil, "docker", args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inspecting containers: %w", err)
|
||||
}
|
||||
res := map[string]string{}
|
||||
for _, line := range strings.Split(strings.TrimSpace(out), "\n") {
|
||||
parts := strings.Split(strings.TrimSpace(line), inspectSep)
|
||||
if len(parts) != 3 || parts[1] != "started" {
|
||||
continue
|
||||
}
|
||||
for id, svc := range svcOf {
|
||||
if parts[0] == id || (len(parts[0]) > 12 && parts[0][:12] == id) {
|
||||
res[svc] = parts[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// diffStartTimes: the services whose start time moved ("svc old → new"), and the ones that kept it.
|
||||
func diffStartTimes(before, after map[string]string) (changed, same []string) {
|
||||
svcs := make([]string, 0, len(after))
|
||||
for s := range after {
|
||||
svcs = append(svcs, s)
|
||||
}
|
||||
sort.Strings(svcs)
|
||||
for _, s := range svcs {
|
||||
if b, ok := before[s]; ok && b == after[s] {
|
||||
same = append(same, s)
|
||||
} else {
|
||||
changed = append(changed, fmt.Sprintf("%s %s → %s", s, orDash(before[s]), after[s]))
|
||||
}
|
||||
}
|
||||
return changed, same
|
||||
}
|
||||
|
||||
func orDash(s string) string {
|
||||
if s == "" {
|
||||
return "-"
|
||||
}
|
||||
return s
|
||||
}
|
||||
Reference in new issue
Block a user