fix(cli): allow cached managed reinstalls

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-27 15:32:13 +00:00
1 parent dc06b610b9
commit 94b68d2437
2 files changed
+23 -2

No files matched your search

+21
View File
@@ -85,6 +85,27 @@ describe("managed install commands", () => {
expect(runCommand.mock.calls[0]?.[0]).toBe(process.execPath);
});
it("installs a GitHub branch through codeload and reuses the resolved SHA", async () => {
const sha = "c".repeat(40);
const runCommand = vi.fn(async (file: string, args: string[]) => {
if (file === "curl" && !args.includes("--output")) return { stdout: JSON.stringify({ sha }), stderr: "" };
if (file === "curl") { fs.writeFileSync(args[args.indexOf("--output") + 1], "archive"); return { stdout: "", stderr: "" }; }
if (file === "tar") { const checkout = args[args.indexOf("-C") + 1]; fs.mkdirSync(path.join(checkout, "cli"), { recursive: true }); fs.writeFileSync(path.join(checkout, "cli", "package.json"), JSON.stringify({ version: "0.3.1" })); return { stdout: "", stderr: "" }; }
if (file === "corepack" || file === "bash") return { stdout: "", stderr: "" };
if (file === "npm" && args[0] === "pack") { fs.writeFileSync(path.join(args[args.indexOf("--pack-destination") + 1], "paperclipai-0.3.1.tgz"), "package"); return { stdout: "", stderr: "" }; }
if (file === "npm" && args[0] === "install") { const prefix = args[args.indexOf("--prefix") + 1]; const packageRoot = path.join(prefix, "node_modules", "paperclipai"); fs.mkdirSync(path.join(packageRoot, "dist"), { recursive: true }); fs.writeFileSync(path.join(packageRoot, "package.json"), JSON.stringify({ version: "0.3.1" })); fs.writeFileSync(path.join(packageRoot, "dist", "index.js"), "#!/usr/bin/env node\n"); return { stdout: "", stderr: "" }; }
if (file === process.execPath) return { stdout: "0.3.1\n", stderr: "" };
throw new Error(`Unexpected command: ${file} ${args.join(" ")}`);
});
await installCommand({ ref: "master", repo: "HenkDz/paperclip" }, { runCommand });
await installCommand({ ref: "master", repo: "HenkDz/paperclip" }, { runCommand });
const manifest = readInstallManifest(resolveInstallStorePaths());
expect(manifest).toMatchObject({ source: "git", repo: "HenkDz/paperclip", ref: "master", sha });
expect(manifest?.payloadPath).toContain(path.join("git", sha.slice(0, 12)));
expect(runCommand.mock.calls.filter(([command, args]) => command === "curl" && args.includes("--output"))).toHaveLength(1);
expect(runCommand.mock.calls.filter(([command]) => command === "corepack")).toHaveLength(1);
});
it("installs through the shim, reports provenance, and uninstalls without deleting user data", async () => {
const version = "2026.720.0";
const runCommand = vi.fn(async (file: string, args: string[], _options?: unknown) => {
+2 -2
View File
@@ -381,8 +381,8 @@ export function writeManagedShim(paths = resolveInstallStorePaths()): void {
const homeDir = path.dirname(path.dirname(path.dirname(paths.shimPath)));
const localDir = path.dirname(path.dirname(paths.shimPath));
fs.mkdirSync(homeDir, { recursive: true, mode: 0o700 });
fs.mkdirSync(localDir, { mode: 0o755 });
fs.mkdirSync(path.dirname(paths.shimPath), { mode: 0o755 });
fs.mkdirSync(localDir, { recursive: true, mode: 0o755 });
fs.mkdirSync(path.dirname(paths.shimPath), { recursive: true, mode: 0o755 });
assertManagedShimWritable(paths);
const entrypoint = path.join(paths.currentPath, "node_modules", "paperclipai", "dist", "index.js");
const contents = `#!/bin/sh\n# ${MANAGED_SHIM_MARKER}\nset -eu\nexec ${shellQuote(process.execPath)} ${shellQuote(entrypoint)} "\$@"\n`;