chore: v2.8.21 — update progress steps, clone in background, extra SSH keys inline, today's spend
- Plugin update reports its running step (plugin-update-progress.json → plugin_update_progress); a hidden update or clone leaves a progress chip. - Clone here can run in the background. - Git identity: extra SSH keys are added inline per host (no More keys tab). - Users: spend today (viewer's local day, ?tz_offset=). - Prettier on the files touched since v2.8.20.
This commit is contained in:
@@ -19,7 +19,12 @@ const os = require("os");
|
||||
const path = require("path");
|
||||
const { execFileSync } = require("child_process");
|
||||
|
||||
const { readLastPluginUpdate, schedulePluginUpdate, runtimeDir } = require("../lib/plugin-update");
|
||||
const {
|
||||
readLastPluginUpdate,
|
||||
readPluginUpdateProgress,
|
||||
schedulePluginUpdate,
|
||||
runtimeDir,
|
||||
} = require("../lib/plugin-update");
|
||||
|
||||
const HELPER = path.join(__dirname, "..", "..", "scripts", "plugin-update-helper.js");
|
||||
|
||||
@@ -84,6 +89,43 @@ describe("readLastPluginUpdate", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("readPluginUpdateProgress", () => {
|
||||
const write = (name, body) => {
|
||||
const rt = path.join(tmpDir, name);
|
||||
fs.mkdirSync(rt, { recursive: true });
|
||||
if (body !== undefined) fs.writeFileSync(path.join(rt, "plugin-update-progress.json"), body);
|
||||
return rt;
|
||||
};
|
||||
|
||||
it("is null with no file and for malformed JSON", () => {
|
||||
assert.equal(readPluginUpdateProgress(write("prog-none")), null);
|
||||
assert.equal(readPluginUpdateProgress(write("prog-bad", "{nope")), null);
|
||||
});
|
||||
|
||||
it("reads the running step", () => {
|
||||
const started_at = new Date().toISOString();
|
||||
const rt = write(
|
||||
"prog-ok",
|
||||
JSON.stringify({ step: "plugin_update", index: 2, total: 3, started_at })
|
||||
);
|
||||
assert.deepEqual(readPluginUpdateProgress(rt), {
|
||||
step: "plugin_update",
|
||||
index: 2,
|
||||
total: 3,
|
||||
started_at,
|
||||
});
|
||||
});
|
||||
|
||||
it("ignores a file left by a helper that died long ago", () => {
|
||||
const old = new Date(Date.now() - 60 * 60 * 1000).toISOString();
|
||||
const rt = write(
|
||||
"prog-stale",
|
||||
JSON.stringify({ step: "bootstrap", index: 3, total: 3, started_at: old })
|
||||
);
|
||||
assert.equal(readPluginUpdateProgress(rt), null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("schedulePluginUpdate", () => {
|
||||
it("clears a stale verdict before spawning, so an old success can't be read as this run's result", () => {
|
||||
// Redirect the runtime dir into the temp tree — this must not touch the
|
||||
@@ -152,6 +194,20 @@ describe("plugin-update-helper name guard", () => {
|
||||
assert.equal(v.step, "validate");
|
||||
});
|
||||
|
||||
it("removes a progress record with its final verdict", () => {
|
||||
const rt = path.join(tmpDir, "guard-progress");
|
||||
fs.mkdirSync(rt, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(rt, "plugin-update-progress.json"),
|
||||
JSON.stringify({ step: "plugin_update", started_at: new Date().toISOString() })
|
||||
);
|
||||
execFileSync(process.execPath, [HELPER, "../evil", "agenthub", "/nonexistent/root", rt], {
|
||||
stdio: "ignore",
|
||||
timeout: 30_000,
|
||||
});
|
||||
assert.equal(fs.existsSync(path.join(rt, "plugin-update-progress.json")), false);
|
||||
});
|
||||
|
||||
it("stamps every verdict with a finish time", () => {
|
||||
// The failure mode this file exists to prevent is a helper that dies
|
||||
// silently: the tab would poll a server that never returns and say nothing.
|
||||
|
||||
Reference in New Issue
Block a user