mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-28 04:02:18 +01:00
feat: integrate managed state repo
This commit is contained in:
1 parent
bcf6c0ad65
commit
09696ad5d7
12 files changed
+454
-30
No files matched your search
@@ -0,0 +1,50 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../utils/banner.js", () => ({ printPaperclipCliBanner: vi.fn() }));
|
||||
vi.mock("@clack/prompts", () => ({ intro: vi.fn(), outro: vi.fn() }));
|
||||
|
||||
import { stateRestoreCommand } from "../commands/state.js";
|
||||
|
||||
const originalEnv = process.env;
|
||||
|
||||
describe("state restore command", () => {
|
||||
beforeEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({ restored: ["agents/a/AGENTS.md"], dryRun: true }),
|
||||
}));
|
||||
vi.spyOn(console, "log").mockImplementation(() => undefined);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it("calls the company restore API for --from-git", async () => {
|
||||
await stateRestoreCommand(undefined, {
|
||||
apiUrl: "http://paperclip.test/api",
|
||||
token: "token",
|
||||
json: true,
|
||||
fromGit: "/backup/state.git",
|
||||
companyId: "company-1",
|
||||
ref: "refs/heads/main",
|
||||
dryRun: true,
|
||||
});
|
||||
|
||||
expect(fetch).toHaveBeenCalledWith(
|
||||
"http://paperclip.test/api/companies/company-1/state-repo/restore",
|
||||
expect.objectContaining({
|
||||
method: "POST",
|
||||
body: JSON.stringify({ source: "/backup/state.git", ref: "refs/heads/main", dryRun: true }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("requires a company for git restores", async () => {
|
||||
delete process.env.PAPERCLIP_COMPANY_ID;
|
||||
await expect(stateRestoreCommand(undefined, { fromGit: "/backup/state.git" })).rejects.toThrow("--company-id");
|
||||
});
|
||||
});
|
||||
@@ -25,10 +25,19 @@ export async function stateSnapshotCommand(opts: StateApiOptions) {
|
||||
else p.outro(pc.green(`Snapshot stored: ${result.objectKey}`));
|
||||
}
|
||||
|
||||
export async function stateRestoreCommand(objectKey: string, opts: StateApiOptions) {
|
||||
export async function stateRestoreCommand(objectKey: string | undefined, opts: StateApiOptions & { fromGit?: string; companyId?: string; ref?: string; dryRun?: boolean }) {
|
||||
printPaperclipCliBanner();
|
||||
p.intro(pc.bgCyan(pc.black(" paperclip state restore ")));
|
||||
const result = await callStateApi("/instance/state-snapshots/restore", opts, { objectKey });
|
||||
const companyId = opts.companyId || process.env.PAPERCLIP_COMPANY_ID;
|
||||
if (opts.fromGit && !companyId) throw new Error("--company-id or PAPERCLIP_COMPANY_ID is required with --from-git");
|
||||
if (!opts.fromGit && !objectKey) throw new Error("object-key is required unless --from-git is used");
|
||||
const result = opts.fromGit
|
||||
? await callStateApi(`/companies/${companyId}/state-repo/restore`, opts, {
|
||||
source: opts.fromGit,
|
||||
ref: opts.ref,
|
||||
dryRun: opts.dryRun,
|
||||
})
|
||||
: await callStateApi("/instance/state-snapshots/restore", opts, { objectKey });
|
||||
if (opts.json) console.log(JSON.stringify(result, null, 2));
|
||||
else p.outro(pc.green(`Snapshot restored: ${objectKey}`));
|
||||
else p.outro(pc.green(opts.fromGit ? `Git state restored: ${opts.fromGit}` : `Snapshot restored: ${objectKey}`));
|
||||
}
|
||||
+6
-2
@@ -169,8 +169,12 @@ state.command("snapshot")
|
||||
.option("--json", "Print result as JSON")
|
||||
.action(stateSnapshotCommand);
|
||||
state.command("restore")
|
||||
.description("Restore an encrypted instance-state snapshot")
|
||||
.argument("<object-key>", "Storage-provider object key returned by state snapshot")
|
||||
.description("Restore an encrypted snapshot or company state from git")
|
||||
.argument("[object-key]", "Storage-provider object key returned by state snapshot")
|
||||
.option("--from-git <path>", "Restore company state from a bare repo or git bundle")
|
||||
.option("--company-id <id>", "Company to restore (defaults to PAPERCLIP_COMPANY_ID)")
|
||||
.option("--ref <ref>", "Git ref to restore", "main")
|
||||
.option("--dry-run", "List files without writing them")
|
||||
.option("--api-url <url>", "Paperclip API base URL")
|
||||
.option("--token <token>", "Board/admin bearer token")
|
||||
.option("--json", "Print result as JSON")
|
||||
|
||||
Reference in new issue
Block a user