Drafts rebase onto the live settings; the review speaks in words
A draft started before someone else's Apply used to carry that older config and would have put the newer changes back. Now every read of the draft replays only the draft's own changes onto the live settings (base config from history), so the review lists exactly what you changed. Values read in words (windows as days and hours), long ones fold open with was/now, commands are no longer red, and warnings the live config already had are not repeated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
+30
-5
@@ -645,7 +645,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
changes = [applying.words_for(p, a, b, cfg, live_cfg) for p, a, b in applying.changed_paths(cfg, live_cfg)]
|
||||
return self.send(200, {"config": schema.mask_secrets(cfg), "changes_to_now": changes})
|
||||
if path == "/api/draft":
|
||||
d = site.draft.read()
|
||||
d = self.draft_now()
|
||||
return self.send(200, {"draft": self.masked_draft(d) if d else None, "live_sha": site.cfg_sha,
|
||||
"stale": bool(d and d.get("base_sha") != site.cfg_sha),
|
||||
"atem_pending": os.path.exists(os.path.join(site.data_dir, "draft-atem.xml"))})
|
||||
@@ -667,7 +667,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
snap = site.read_json("snapshot.json", {}) or {}
|
||||
return self.send(200, {"local": snap.get("midi"), "remote": snap.get("remote_midi")})
|
||||
if path == "/api/site/names":
|
||||
return self.send(200, self.names_check((site.draft.read() or {}).get("config") or site.load()))
|
||||
return self.send(200, self.names_check((self.draft_now() or {}).get("config") or site.load()))
|
||||
if path == "/api/outlets":
|
||||
d = outlets.load()
|
||||
t = d.get("tuya") or {}
|
||||
@@ -795,7 +795,7 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
self.discard_atem()
|
||||
return self.send(200, {"ok": True})
|
||||
if path in ("/api/build", "/api/build-check"):
|
||||
cfg = (site.draft.read() or {}).get("config") or site.load()
|
||||
cfg = (self.draft_now() or {}).get("config") or site.load()
|
||||
try:
|
||||
if path == "/api/build":
|
||||
fix = builders.build(cfg, body.get("fix") or {})
|
||||
@@ -804,10 +804,10 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
except ValueError as e:
|
||||
return self.send(200, {"ok": False, "error": str(e)})
|
||||
if path == "/api/validate":
|
||||
cfg = schema.unmask(body.get("config") or (site.draft.read() or {}).get("config") or site.load(), site.load())
|
||||
cfg = schema.unmask(body.get("config") or (self.draft_now() or {}).get("config") or site.load(), site.load())
|
||||
return self.send(200, self.check(cfg))
|
||||
if path == "/api/apply":
|
||||
draft = site.draft.read()
|
||||
draft = self.draft_now()
|
||||
if not draft:
|
||||
return self.send(400, {"error": "there is no draft to apply"})
|
||||
if draft.get("base_sha") != site.cfg_sha:
|
||||
@@ -1071,6 +1071,29 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
raise ValueError("no keys in it")
|
||||
return cfg
|
||||
|
||||
def draft_now(self):
|
||||
"""The draft, rebased onto the live settings when they moved on since it was started."""
|
||||
site = self.site
|
||||
d = site.draft.read()
|
||||
if not d or d.get("base_sha") == site.cfg_sha:
|
||||
return d
|
||||
base = None
|
||||
for e in applying.history_entries(site.data_dir):
|
||||
meta = site.read_json(os.path.join("history", e["id"] + ".meta.json"), {}) or {}
|
||||
if meta.get("sha_before") == d.get("base_sha"):
|
||||
base = applying.history_config(site.data_dir, e["id"])
|
||||
break
|
||||
if base is None:
|
||||
return d # stale: the page says so
|
||||
merged, changes = applying.rebase(d, site.load(), base)
|
||||
nd, conflict = site.draft.update(merged, d.get("by", "?"), site.cfg_sha, expect=d.get("version"))
|
||||
if nd is None:
|
||||
return conflict
|
||||
nd["started"], nd["started_by"] = d.get("started", nd["started"]), d.get("started_by", nd["started_by"])
|
||||
sp.write_json(site.draft.path, nd)
|
||||
log.info("draft by %s rebased onto the live settings (%d of its own changes kept)", d.get("by"), len(changes))
|
||||
return nd
|
||||
|
||||
def masked_draft(self, draft):
|
||||
return dict(draft, config=schema.mask_secrets(draft["config"]))
|
||||
|
||||
@@ -1083,6 +1106,8 @@ class Handler(http.server.BaseHTTPRequestHandler):
|
||||
except Exception:
|
||||
conns = None
|
||||
errors, warnings = schema.validate(cfg, {"connections": conns} if conns else None)
|
||||
already = set(schema.validate(live_cfg)[1])
|
||||
warnings = [w for w in warnings if w not in already] # only what this change adds
|
||||
tmp = tempfile.NamedTemporaryFile("wb", suffix=".json", delete=False)
|
||||
tmp.write(applying.config_text(cfg)); tmp.close()
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user