chore: v2.8.12 — clone queue and progress, session defaults, chat agents, drops as @ mentions
Whole tree from parallel sessions, plus the version bump: - Clone: a queue with live progress (Add workspace and extra folders), clones run under nice/ionice so the machine stays responsive. - Settings → AI tools → Session defaults: a new session's default permission mode / bypass per account. - Chat lists the background subagents that have not reported back yet. - A file pasted or dropped into a claude pane goes in as an @ mention. - Changes panel: say why amend/undo is refused on a pushed commit. - openapi.yaml regenerated (Artifacts, Host, resumeById).
This commit is contained in:
@@ -64,6 +64,8 @@ before(async () => {
|
||||
"init",
|
||||
]);
|
||||
execFileSync("git", ["clone", "-q", "--bare", src, bare]);
|
||||
// A forge serves a partial clone only when it allows filters.
|
||||
execFileSync("git", ["-C", bare, "config", "uploadpack.allowFilter", "true"]);
|
||||
|
||||
remote = http.createServer((_req, res) => {
|
||||
res.writeHead(401, { "WWW-Authenticate": 'Basic realm="t"' });
|
||||
@@ -183,6 +185,61 @@ describe("POST /api/lanes/clone", () => {
|
||||
assert.equal(r.body.error.code, "ENOREPO");
|
||||
});
|
||||
|
||||
it("makes a blobless clone only when asked, and refuses a non-boolean", async () => {
|
||||
const r = await request("POST", "/api/lanes/clone", {
|
||||
url: "https://forge.test/o/r.git",
|
||||
parent: WORK,
|
||||
name: "r-partial",
|
||||
partial: true,
|
||||
});
|
||||
assert.equal(r.status, 201, JSON.stringify(r.body));
|
||||
const dir = path.join(WORK, "r-partial");
|
||||
assert.equal(fs.readFileSync(path.join(dir, "README.md"), "utf8"), "hi\n");
|
||||
const cfg = (k) =>
|
||||
execFileSync("git", ["-C", dir, "config", "--get", k], { encoding: "utf8" }).trim();
|
||||
assert.equal(cfg("remote.origin.partialclonefilter"), "blob:none");
|
||||
// The full clone above is not a promisor remote.
|
||||
assert.throws(() =>
|
||||
execFileSync("git", ["-C", path.join(WORK, "r"), "config", "--get", "remote.origin.promisor"])
|
||||
);
|
||||
const bad = await request("POST", "/api/lanes/clone", {
|
||||
url: "https://forge.test/o/r.git",
|
||||
parent: WORK,
|
||||
name: "r-bad",
|
||||
partial: "yes",
|
||||
});
|
||||
assert.equal(bad.status, 400);
|
||||
assert.equal(bad.body.error.code, "EBADFIELD");
|
||||
});
|
||||
|
||||
it("reports a clone's progress by its token, and refuses a bad token", async () => {
|
||||
const token = "progress-test-0000001";
|
||||
const r = await request("POST", "/api/lanes/clone", {
|
||||
url: "https://forge.test/o/r.git",
|
||||
parent: WORK,
|
||||
name: "r-progress",
|
||||
progressToken: token,
|
||||
});
|
||||
assert.equal(r.status, 201, JSON.stringify(r.body));
|
||||
const p = await request("GET", `/api/lanes/clone/progress/${token}`);
|
||||
assert.equal(p.status, 200);
|
||||
assert.equal(p.body.state, "done");
|
||||
assert.equal(p.body.position, null);
|
||||
assert.equal(
|
||||
(await request("GET", "/api/lanes/clone/progress/nope-nope-nope-nope")).status,
|
||||
404
|
||||
);
|
||||
const bad = await request("POST", "/api/lanes/clone", {
|
||||
url: "https://forge.test/o/r.git",
|
||||
parent: WORK,
|
||||
name: "r-badtoken",
|
||||
progressToken: "x",
|
||||
});
|
||||
assert.equal(bad.status, 400);
|
||||
assert.equal(bad.body.error.code, "EBADFIELD");
|
||||
assert.ok(!fs.existsSync(path.join(WORK, "r-badtoken")));
|
||||
});
|
||||
|
||||
it("refuses a cross-origin request", async () => {
|
||||
const r = await request(
|
||||
"POST",
|
||||
@@ -649,6 +706,8 @@ describe("git-identity, member account (S3, stubbed)", () => {
|
||||
/^core\.sshCommand=ssh -o IdentityFile='.*\/id_ed25519' -o IdentityFile='.*\/id_rsa' -o IdentitiesOnly=yes$/
|
||||
);
|
||||
assert.deepEqual(args.slice(-3), ["--", "git@gh.test:o/r.git", "r"]);
|
||||
// index-pack gets a share of the cores, never persisted into the clone.
|
||||
assert.match(args.slice(0, i).join(" "), /-c pack\.threads=\d+/);
|
||||
});
|
||||
|
||||
it("gives a member's fetch their own store, and puts a typed login in it only for the call", () => {
|
||||
|
||||
Reference in New Issue
Block a user