diff --git a/auditor/gitea.ts b/auditor/gitea.ts index 2b20d57..2d897e8 100644 --- a/auditor/gitea.ts +++ b/auditor/gitea.ts @@ -9,28 +9,42 @@ import { readFile } from "node:fs/promises"; import type { PrSnapshot } from "./types.ts"; -const HOST = process.env.GITEA_HOST ?? "https://git.agentview.dev"; +// 2026-09-02: repo home moved to git.warren.help in the VPS +// consolidation; git.agentview.dev now dead-ends at a Traefik default +// cert + 404 (auditor had been failing listOpenPrs every cycle since +// Aug 25). Credentials come from ~/.git-credentials for HOST's domain, +// falling back to the user:pass embedded in this repo's origin remote. +// Basic auth works for both a PAT and a password on Gitea. +const HOST = process.env.GITEA_HOST ?? "https://git.warren.help"; const OWNER = "profit"; const REPO = "lakehouse"; const CRED_FILE = "/home/profit/.git-credentials"; +const GIT_CONFIG = "/home/profit/lakehouse/.git/config"; -let cachedPat: string | null = null; +let cachedAuth: string | null = null; -async function getPat(): Promise { - if (cachedPat) return cachedPat; - const raw = await readFile(CRED_FILE, "utf8"); - for (const line of raw.split("\n")) { - const m = line.match(/^https:\/\/[^:]+:([^@]+)@git\.agentview\.dev/); - if (m) { cachedPat = m[1]; return m[1]; } +async function getAuthHeader(): Promise { + if (cachedAuth) return cachedAuth; + const hostDomain = new URL(HOST).host.replace(/\./g, "\\."); + const credRe = new RegExp(`^https://([^:]+):([^@]+)@${hostDomain}`); + for (const file of [CRED_FILE, GIT_CONFIG]) { + let raw: string; + try { raw = await readFile(file, "utf8"); } catch { continue; } + for (const line of raw.split("\n")) { + const m = line.trim().replace(/^url = /, "").match(credRe); + if (m) { + cachedAuth = `Basic ${Buffer.from(`${decodeURIComponent(m[1])}:${decodeURIComponent(m[2])}`).toString("base64")}`; + return cachedAuth; + } + } } - throw new Error(`no Gitea PAT in ${CRED_FILE}`); + throw new Error(`no Gitea credentials for ${HOST} in ${CRED_FILE} or ${GIT_CONFIG}`); } async function giteaFetch(path: string, init: RequestInit = {}): Promise { - const pat = await getPat(); const url = `${HOST}/api/v1${path}`; const headers = new Headers(init.headers); - headers.set("Authorization", `token ${pat}`); + headers.set("Authorization", await getAuthHeader()); if (init.body && !headers.has("content-type")) { headers.set("content-type", "application/json"); }