- Report Issue button in portal header files a Forgejo issue (portal + claude-queue labels) - Windows scheduled watcher runs headless Claude Code on queued issues and opens a PR - .forgejo/workflows/deploy.yml deploys v* release tags via the pms1 host runner (pm2 restart ppms) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
// Forgejo issue integration for the in-app "Report Issue" button.
|
|
// Issues are created on the configured repo and labelled so the
|
|
// automated Claude watcher can pick them up.
|
|
|
|
const FORGEJO_URL = process.env.FORGEJO_URL ?? "https://git.pelagiamarine.com";
|
|
const FORGEJO_REPO = process.env.FORGEJO_REPO ?? "shad0w/pelagia-portal";
|
|
|
|
interface CreateIssueInput {
|
|
title: string;
|
|
body: string;
|
|
labels: string[];
|
|
}
|
|
|
|
interface ForgejoIssue {
|
|
number: number;
|
|
html_url: string;
|
|
}
|
|
|
|
async function forgejoFetch(path: string, init?: RequestInit): Promise<Response> {
|
|
const token = process.env.FORGEJO_TOKEN;
|
|
if (!token) throw new Error("FORGEJO_TOKEN is not configured");
|
|
|
|
return fetch(`${FORGEJO_URL}/api/v1${path}`, {
|
|
...init,
|
|
headers: {
|
|
Authorization: `token ${token}`,
|
|
"Content-Type": "application/json",
|
|
...init?.headers,
|
|
},
|
|
});
|
|
}
|
|
|
|
/** Resolve label names to IDs (the create-issue API only accepts IDs). */
|
|
async function resolveLabelIds(names: string[]): Promise<number[]> {
|
|
const res = await forgejoFetch(`/repos/${FORGEJO_REPO}/labels?limit=50`);
|
|
if (!res.ok) return [];
|
|
const labels: { id: number; name: string }[] = await res.json();
|
|
return labels.filter((l) => names.includes(l.name)).map((l) => l.id);
|
|
}
|
|
|
|
export async function createForgejoIssue(input: CreateIssueInput): Promise<ForgejoIssue> {
|
|
const labelIds = await resolveLabelIds(input.labels);
|
|
|
|
const res = await forgejoFetch(`/repos/${FORGEJO_REPO}/issues`, {
|
|
method: "POST",
|
|
body: JSON.stringify({ title: input.title, body: input.body, labels: labelIds }),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const text = await res.text();
|
|
throw new Error(`Forgejo issue creation failed (${res.status}): ${text.slice(0, 200)}`);
|
|
}
|
|
|
|
const issue = await res.json();
|
|
return { number: issue.number, html_url: issue.html_url };
|
|
}
|