pelagia-portal/App/lib/forgejo.ts
Hardik 8b6d4e8ea6 feat(automation): issue-to-deploy pipeline — Report Issue button, Claude watcher, tag-triggered deploy
- 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>
2026-06-11 16:39:43 +05:30

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 };
}