Centralize encrypted bot profiles
Check / deno (pull_request) Failing after 42s

This commit is contained in:
Danny Kim
2026-09-24 04:56:11 +09:00
parent ba9e0c0787
commit 80f3d922d1
22 changed files with 1973 additions and 526 deletions
+202
View File
@@ -0,0 +1,202 @@
import { assert, assertEquals, assertRejects } from "jsr:@std/assert@1";
import { type PostContext, postRuntime } from "./post.ts";
import type { RuntimeManifest, RuntimeOutcome } from "./runtime.ts";
const context: PostContext = {
apiUrl: "https://gitea.example/api/v1",
repository: "owner/repo",
issueIndex: "7",
eventName: "issue_comment",
};
async function fixture(
source: { ok: false } | { ok: true; body: string },
review?: string,
anchors?: unknown,
): Promise<{
manifestPath: string;
runtimeDir: string;
reviewerFile: string;
reviewPath: string;
outcomeFile: string;
}> {
const runtimeDir = await Deno.makeTempDir();
const reviewerFile = `${runtimeDir}/reviewer-token`;
const outcomeFile = `${runtimeDir}/outcome.json`;
const profileHome = `${runtimeDir}/profile-0`;
await Deno.mkdir(profileHome);
await Deno.writeTextFile(reviewerFile, "reviewer-secret");
const outcome: RuntimeOutcome = source.ok
? {
...source,
reviewPath: `${profileHome}/review.md`,
anchorsPath: `${profileHome}/anchors.json`,
}
: source;
await Deno.writeTextFile(outcomeFile, JSON.stringify(outcome));
if (outcome.ok) {
if (review !== undefined) {
await Deno.writeTextFile(outcome.reviewPath, review, { mode: 0o600 });
await Deno.chmod(outcome.reviewPath, 0o600);
}
if (anchors !== undefined) {
await Deno.writeTextFile(outcome.anchorsPath, JSON.stringify(anchors), {
mode: 0o600,
});
await Deno.chmod(outcome.anchorsPath, 0o600);
}
}
const manifestPath = `${runtimeDir}/manifest.json`;
const manifest: RuntimeManifest = {
runtimeDir,
manifestPath,
reviewerFile,
outcomeFile,
profiles: [{
index: 0,
type: "claude",
model: "test",
uid: Deno.uid()!,
gid: Deno.gid()!,
home: profileHome,
tokenFile: null,
}],
};
await Deno.writeTextFile(manifestPath, JSON.stringify(manifest));
return {
manifestPath,
runtimeDir,
reviewerFile,
reviewPath: `${profileHome}/review.md`,
outcomeFile,
};
}
const missing = async (path: string): Promise<boolean> => {
try {
await Deno.stat(path);
return false;
} catch (error) {
if (error instanceof Deno.errors.NotFound) return true;
throw error;
}
};
Deno.test("posts a successful issue result and removes runtime credentials", async () => {
const result = await fixture({ ok: true, body: "done" });
const requests: Array<{ url: string; init?: RequestInit }> = [];
await postRuntime(result.manifestPath, context, (url, init) => {
requests.push({ url: String(url), init });
return Promise.resolve(new Response(null, { status: 201 }));
});
assertEquals(requests.length, 1);
assertEquals(
requests[0].url,
`${context.apiUrl}/repos/owner/repo/issues/7/comments`,
);
assertEquals(JSON.parse(String(requests[0].init?.body)), { body: "done" });
assertEquals(requests[0].init?.headers, {
Authorization: "token reviewer-secret",
"Content-Type": "application/json",
});
assert(await missing(result.runtimeDir));
});
Deno.test("retries a rejected inline review without anchors", async () => {
const result = await fixture(
{ ok: true, body: "ignored final response" },
"Approved\nReady",
[{ path: "run.ts", line: 1, side: "new", body: "Finding" }],
);
const bodies: unknown[] = [];
await postRuntime(result.manifestPath, {
...context,
eventName: "pull_request",
}, (_url, init) => {
bodies.push(JSON.parse(String(init?.body)));
return Promise.resolve(
new Response(null, { status: bodies.length === 1 ? 422 : 200 }),
);
});
assertEquals((bodies[0] as { comments: unknown[] }).comments.length, 1);
assertEquals((bodies[1] as { comments: unknown[] }).comments, []);
assert(await missing(result.runtimeDir));
});
Deno.test("reports a generic agent failure and keeps the action failed", async () => {
const result = await fixture({ ok: false });
const bodies: unknown[] = [];
await assertRejects(
() =>
postRuntime(result.manifestPath, context, (_url, init) => {
bodies.push(JSON.parse(String(init?.body)));
return Promise.resolve(new Response(null, { status: 201 }));
}),
Error,
"agent phase failed",
);
assertEquals(bodies, [{ body: "AI bot failed." }]);
assert(!JSON.stringify(bodies).includes("reviewer-secret"));
assert(await missing(result.runtimeDir));
});
Deno.test("reports a generic failure when the runner wrote no outcome", async () => {
const result = await fixture({ ok: false });
await Deno.remove(result.outcomeFile);
const bodies: unknown[] = [];
await assertRejects(
() =>
postRuntime(result.manifestPath, context, (_url, init) => {
bodies.push(JSON.parse(String(init?.body)));
return Promise.resolve(new Response(null, { status: 201 }));
}),
Error,
"agent phase failed",
);
assertEquals(bodies, [{ body: "AI bot failed." }]);
assert(await missing(result.runtimeDir));
});
Deno.test("removes runtime credentials when posting fails", async () => {
const result = await fixture({ ok: true, body: "done" });
await assertRejects(
() =>
postRuntime(
result.manifestPath,
context,
() => Promise.resolve(new Response("failed", { status: 500 })),
),
Error,
"500",
);
assert(await missing(result.runtimeDir));
});
Deno.test("refuses to follow an agent artifact symlink", async () => {
if (Deno.build.os === "windows") return;
const result = await fixture({ ok: true, body: "done" });
await Deno.symlink(result.reviewerFile, result.reviewPath);
let requests = 0;
await assertRejects(
() =>
postRuntime(result.manifestPath, context, () => {
requests++;
return Promise.resolve(new Response(null, { status: 200 }));
}),
Error,
"regular private file",
);
assertEquals(requests, 0);
assert(await missing(result.runtimeDir));
});