fix(coding-agent): refresh temporary git extension caches on cache hits

This commit is contained in:
Mario Zechner 2026-02-06 22:01:49 +01:00
parent 92fdb53c10
commit 310da43042
3 changed files with 101 additions and 2 deletions

View file

@ -7,6 +7,7 @@
*/
import { spawnSync } from "node:child_process";
import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
@ -132,6 +133,20 @@ describe("DefaultPackageManager git update", () => {
expect(getCurrentCommit(installedDir)).toBe(latestCommit);
expect(getFileContent(installedDir, "extension.ts")).toBe("// v4");
});
it("should update even when local checkout has no upstream", async () => {
setupRemoteAndInstall();
createCommit(remoteDir, "extension.ts", "// v2", "Second commit");
const latestCommit = createCommit(remoteDir, "extension.ts", "// v3", "Third commit");
const detachedCommit = getCurrentCommit(installedDir);
git(["checkout", detachedCommit], installedDir);
await packageManager.update();
expect(getCurrentCommit(installedDir)).toBe(latestCommit);
expect(getFileContent(installedDir, "extension.ts")).toBe("// v3");
});
});
describe("force-push scenarios", () => {
@ -233,6 +248,69 @@ describe("DefaultPackageManager git update", () => {
});
});
describe("temporary git sources", () => {
it("should refresh cached temporary git sources when resolving", async () => {
const gitHost = "github.com";
const gitPath = "test/extension";
const hash = createHash("sha256").update(`git-${gitHost}-${gitPath}`).digest("hex").slice(0, 8);
const cachedDir = join(tmpdir(), "pi-extensions", `git-${gitHost}`, hash, gitPath);
const extensionFile = join(cachedDir, "pi-extensions", "session-breakdown.ts");
rmSync(cachedDir, { recursive: true, force: true });
mkdirSync(join(cachedDir, "pi-extensions"), { recursive: true });
writeFileSync(
join(cachedDir, "package.json"),
JSON.stringify({ pi: { extensions: ["./pi-extensions"] } }, null, 2),
);
writeFileSync(extensionFile, "// stale");
const executedCommands: string[] = [];
const managerWithInternals = packageManager as unknown as {
runCommand: (command: string, args: string[], options?: { cwd?: string }) => Promise<void>;
};
managerWithInternals.runCommand = async (command, args) => {
executedCommands.push(`${command} ${args.join(" ")}`);
if (command === "git" && args[0] === "reset") {
writeFileSync(extensionFile, "// fresh");
}
};
await packageManager.resolveExtensionSources([gitSource], { temporary: true });
expect(executedCommands).toContain("git fetch --prune origin");
expect(getFileContent(cachedDir, "pi-extensions/session-breakdown.ts")).toBe("// fresh");
});
it("should not refresh pinned temporary git sources", async () => {
const gitHost = "github.com";
const gitPath = "test/extension";
const hash = createHash("sha256").update(`git-${gitHost}-${gitPath}`).digest("hex").slice(0, 8);
const cachedDir = join(tmpdir(), "pi-extensions", `git-${gitHost}`, hash, gitPath);
const extensionFile = join(cachedDir, "pi-extensions", "session-breakdown.ts");
rmSync(cachedDir, { recursive: true, force: true });
mkdirSync(join(cachedDir, "pi-extensions"), { recursive: true });
writeFileSync(
join(cachedDir, "package.json"),
JSON.stringify({ pi: { extensions: ["./pi-extensions"] } }, null, 2),
);
writeFileSync(extensionFile, "// pinned");
const executedCommands: string[] = [];
const managerWithInternals = packageManager as unknown as {
runCommand: (command: string, args: string[], options?: { cwd?: string }) => Promise<void>;
};
managerWithInternals.runCommand = async (command, args) => {
executedCommands.push(`${command} ${args.join(" ")}`);
};
await packageManager.resolveExtensionSources([`${gitSource}@main`], { temporary: true });
expect(executedCommands).toEqual([]);
expect(getFileContent(cachedDir, "pi-extensions/session-breakdown.ts")).toBe("// pinned");
});
});
describe("scope-aware update", () => {
it("should not install locally when source is only registered globally", async () => {
setupRemoteAndInstall();