feat: add forgejo actions coverage receipt
This commit is contained in:
parent
f4714f146a
commit
1135ef4d7c
|
|
@ -272,6 +272,9 @@ fable-agent plinius godmode "improve explanation quality"
|
||||||
- `--body <text>`
|
- `--body <text>`
|
||||||
- `--out <path>`
|
- `--out <path>`
|
||||||
- `--dry-run`
|
- `--dry-run`
|
||||||
|
- `forgejo actions-coverage`
|
||||||
|
- `--repo <repo>`
|
||||||
|
- `--output <path>`
|
||||||
- `forgejo intake <ref>`
|
- `forgejo intake <ref>`
|
||||||
- `--repo <repo>`
|
- `--repo <repo>`
|
||||||
- `--out <path>`
|
- `--out <path>`
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,26 @@ import * as fs from "node:fs";
|
||||||
import * as os from "node:os";
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { createForgejoPullRequest, getForgejoFileContent, listForgejoIssues } from "./forgejo-cli.js";
|
import { createForgejoActionsCoverageReceipt, createForgejoPullRequest, getForgejoFileContent, listForgejoIssues } from "./forgejo-cli.js";
|
||||||
|
|
||||||
function tmpDir(): string {
|
function tmpDir(): string {
|
||||||
return fs.mkdtempSync(path.join(os.tmpdir(), "forgejo-cli-"));
|
return fs.mkdtempSync(path.join(os.tmpdir(), "forgejo-cli-"));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("forgejo cli helpers", () => {
|
describe("forgejo cli helpers", () => {
|
||||||
|
it("documents Forgejo Actions coverage without scraping or mutation", () => {
|
||||||
|
const receipt = createForgejoActionsCoverageReceipt("org/repo", new Date("2026-07-01T00:00:00.000Z"));
|
||||||
|
|
||||||
|
expect(receipt).toMatchObject({
|
||||||
|
schema: "fable.forgejo.actions_coverage.v1",
|
||||||
|
repo: "org/repo",
|
||||||
|
decision: "api-only",
|
||||||
|
policy: "no-web-ui-scraping-no-action-mutation",
|
||||||
|
deployAttempted: false,
|
||||||
|
});
|
||||||
|
expect(receipt.rows.find((row) => row.capability === "actions cancel/rerun/trigger")?.status).toBe("blocked");
|
||||||
|
});
|
||||||
|
|
||||||
it("lists issues via Forgejo API", async () => {
|
it("lists issues via Forgejo API", async () => {
|
||||||
const calls: string[] = [];
|
const calls: string[] = [];
|
||||||
const issues = await listForgejoIssues({
|
const issues = await listForgejoIssues({
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,41 @@ export interface ForgejoPullRequestReceipt {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ForgejoActionsCoverageReceipt {
|
||||||
|
schema: "fable.forgejo.actions_coverage.v1";
|
||||||
|
createdAt: string;
|
||||||
|
repo?: string;
|
||||||
|
rows: Array<{ capability: string; status: "covered" | "unsupported" | "blocked"; reason: string }>;
|
||||||
|
decision: "api-only";
|
||||||
|
policy: "no-web-ui-scraping-no-action-mutation";
|
||||||
|
deployAttempted: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createForgejoActionsCoverageReceipt(repo?: string, now = new Date()): ForgejoActionsCoverageReceipt {
|
||||||
|
return {
|
||||||
|
schema: "fable.forgejo.actions_coverage.v1",
|
||||||
|
createdAt: now.toISOString(),
|
||||||
|
repo,
|
||||||
|
rows: [
|
||||||
|
{ capability: "actions runs", status: "unsupported", reason: "Forgejo full run details commonly require UI endpoints; Fable will not scrape by default" },
|
||||||
|
{ capability: "actions jobs", status: "unsupported", reason: "use official API only when available" },
|
||||||
|
{ capability: "actions logs", status: "unsupported", reason: "logs may contain secrets; no web UI scraping" },
|
||||||
|
{ capability: "actions artifacts", status: "unsupported", reason: "artifacts may contain secrets; no web UI scraping" },
|
||||||
|
{ capability: "actions cancel/rerun/trigger", status: "blocked", reason: "mutating CI actions requires explicit implementation and approval" },
|
||||||
|
{ capability: "pull request create", status: "covered", reason: "forgejo pr-create emits fable.forgejo.pull_request.v1" },
|
||||||
|
],
|
||||||
|
decision: "api-only",
|
||||||
|
policy: "no-web-ui-scraping-no-action-mutation",
|
||||||
|
deployAttempted: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeForgejoActionsCoverageReceipt(file: string, receipt: ForgejoActionsCoverageReceipt): string {
|
||||||
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||||
|
fs.writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
export async function forgejoApi<T>(opts: ForgejoCliOptions, endpoint: string, init: { method?: string; body?: unknown } = {}): Promise<T> {
|
export async function forgejoApi<T>(opts: ForgejoCliOptions, endpoint: string, init: { method?: string; body?: unknown } = {}): Promise<T> {
|
||||||
const base = requireBaseUrl(opts.baseUrl);
|
const base = requireBaseUrl(opts.baseUrl);
|
||||||
const res = await (opts.fetcher ?? fetch)(`${base}/api/v1${endpoint}`, {
|
const res = await (opts.fetcher ?? fetch)(`${base}/api/v1${endpoint}`, {
|
||||||
|
|
|
||||||
|
|
@ -102,8 +102,8 @@ export type { PromptFingerprintFinding, PromptFingerprintReceipt } from "./promp
|
||||||
export { createJailbreakAssessmentReceipt, writeJailbreakAssessmentReceipt } from "./jailbreak-assessment.js";
|
export { createJailbreakAssessmentReceipt, writeJailbreakAssessmentReceipt } from "./jailbreak-assessment.js";
|
||||||
export type { JailbreakAssessmentOptions, JailbreakAssessmentReceipt, JailbreakAssessmentScores, JailbreakDecision } from "./jailbreak-assessment.js";
|
export type { JailbreakAssessmentOptions, JailbreakAssessmentReceipt, JailbreakAssessmentScores, JailbreakDecision } from "./jailbreak-assessment.js";
|
||||||
|
|
||||||
export { createForgejoPullRequest, forgejoApi, getForgejoFileContent, getForgejoRepository, listForgejoIssues, listForgejoPullRequests, listForgejoRepositories } from "./forgejo-cli.js";
|
export { createForgejoActionsCoverageReceipt, createForgejoPullRequest, forgejoApi, getForgejoFileContent, getForgejoRepository, listForgejoIssues, listForgejoPullRequests, listForgejoRepositories, writeForgejoActionsCoverageReceipt } from "./forgejo-cli.js";
|
||||||
export type { ForgejoCliFetch, ForgejoCliOptions, ForgejoPullRequestReceipt } from "./forgejo-cli.js";
|
export type { ForgejoActionsCoverageReceipt, ForgejoCliFetch, ForgejoCliOptions, ForgejoPullRequestReceipt } from "./forgejo-cli.js";
|
||||||
|
|
||||||
export { createPlanReceipt, writePlanReceipt } from "./plan-receipt.js";
|
export { createPlanReceipt, writePlanReceipt } from "./plan-receipt.js";
|
||||||
export type { PlanAllowedOutcome, PlanPhase, PlanReceipt, PlanReceiptOptions, PlanTaskType } from "./plan-receipt.js";
|
export type { PlanAllowedOutcome, PlanPhase, PlanReceipt, PlanReceiptOptions, PlanTaskType } from "./plan-receipt.js";
|
||||||
|
|
|
||||||
14
src/index.ts
14
src/index.ts
|
|
@ -1824,6 +1824,20 @@ forgejo
|
||||||
if (result.receipt.receipts.quarantine) process.exit(1);
|
if (result.receipt.receipts.quarantine) process.exit(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
forgejo
|
||||||
|
.command("actions-coverage")
|
||||||
|
.description("Write a Forgejo Actions CLI coverage receipt without web scraping or action mutation")
|
||||||
|
.option("--repo <repo>", "Repo slug, e.g. org/repo")
|
||||||
|
.option("--output <path>", "Receipt output path", path.join(".fable", "forgejo", "actions-coverage-live.json"))
|
||||||
|
.action(async (opts: { repo?: string; output?: string }) => {
|
||||||
|
const { createForgejoActionsCoverageReceipt, writeForgejoActionsCoverageReceipt } = await import("./fable5/forgejo-cli.js");
|
||||||
|
const receipt = createForgejoActionsCoverageReceipt(opts.repo);
|
||||||
|
const out = opts.output ?? path.join(".fable", "forgejo", "actions-coverage-live.json");
|
||||||
|
writeForgejoActionsCoverageReceipt(out, receipt);
|
||||||
|
console.log(JSON.stringify(receipt, null, 2));
|
||||||
|
console.log(`\n Receipt: ${out}\n`);
|
||||||
|
});
|
||||||
|
|
||||||
forgejo
|
forgejo
|
||||||
.command("comment <ref>")
|
.command("comment <ref>")
|
||||||
.description("Post a scanned receipt-backed comment to a Forgejo issue or PR")
|
.description("Post a scanned receipt-backed comment to a Forgejo issue or PR")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue