chore(gitea): Issues nach Gitea, Basic-Auth fuer Konto-Passwort (#030)

In den Git-Zugangsdaten steht das Konto-Passwort, kein API-Token. Gitea
nimmt es nur per Basic-Auth an; das Skript erkennt das jetzt selbst.
Alle 30 Issues mit Labels und Meilensteinen uebertragen. Neu: #031
Rotation des Deployment-Keys und #032 Nacharbeiten fuer 1.0.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 17:13:15 +02:00
co-authored by Claude Opus 5.5
parent 9759a91e34
commit 87b733cc9f
4 changed files with 71 additions and 12 deletions
+18 -8
View File
@@ -76,14 +76,15 @@ class Gitea {
constructor(
private readonly basis: string,
private readonly repo: string,
private readonly token: string,
/** Fertiger Wert fuer den Authorization-Header (`token …` oder `Basic …`). */
private readonly autorisierung: string,
) {}
private async anfrage<T>(pfad: string, optionen: RequestInit = {}): Promise<T> {
const antwort = await fetch(`${this.basis}/api/v1/repos/${this.repo}${pfad}`, {
...optionen,
headers: {
Authorization: `token ${this.token}`,
Authorization: this.autorisierung,
"Content-Type": "application/json",
...optionen.headers,
},
@@ -149,10 +150,12 @@ function ausRemote(): { basis: string; repo: string } | null {
}
/**
* Holt den Token aus den Git-Zugangsdaten (Schlüsselbund oder `~/.git-credentials`).
* So genügt ein einziger Token für Push und API – er muss nicht zusätzlich in der .env stehen.
* Holt die Zugangsdaten aus Git (Schlüsselbund oder `~/.git-credentials`), damit sie nicht
* zusätzlich in der .env stehen müssen. Hinterlegt ist dort ein API-Token oder – wie bei
* sshkeyvault – das Konto-Passwort; Gitea nimmt beides an, das Passwort aber nur per Basic-Auth
* (#030).
*/
function tokenAusGitCredentials(basis: string): string | null {
function zugangAusGitCredentials(basis: string): { benutzer: string; geheimnis: string } | null {
const host = new URL(basis).host;
// Erst die eingerichteten Helfer (z. B. Schlüsselbund), dann zusätzlich die Datei
@@ -167,7 +170,8 @@ function tokenAusGitCredentials(basis: string): string | null {
stdio: ["pipe", "pipe", "ignore"],
});
const passwort = /^password=(.*)$/m.exec(antwort);
if (passwort?.[1]) return passwort[1];
const benutzer = /^username=(.*)$/m.exec(antwort);
if (passwort?.[1]) return { benutzer: benutzer?.[1] ?? "", geheimnis: passwort[1] };
} catch {
// Nächste Möglichkeit versuchen.
}
@@ -186,8 +190,14 @@ async function main() {
const remote = ausRemote();
const basis = (process.env.GITEA_URL ?? remote?.basis ?? "").replace(/\/$/, "");
const repo = process.env.GITEA_REPO ?? remote?.repo ?? "";
const token = process.env.GITEA_TOKEN || (basis ? (tokenAusGitCredentials(basis) ?? "") : "");
const zugang = process.env.GITEA_TOKEN ? null : basis ? zugangAusGitCredentials(basis) : null;
const token = process.env.GITEA_TOKEN || zugang?.geheimnis || "";
const tokenQuelle = process.env.GITEA_TOKEN ? "Umgebungsvariable" : "Git-Zugangsdaten";
// Ein Gitea-Token sind 40 Hex-Zeichen; alles andere ist ein Passwort und geht per Basic-Auth.
const autorisierung =
process.env.GITEA_TOKEN || /^[0-9a-f]{40}$/.test(token) || !zugang?.benutzer
? `token ${token}`
: `Basic ${Buffer.from(`${zugang.benutzer}:${token}`).toString("base64")}`;
const verzeichnis = path.join(process.cwd(), "docs", "issues");
const issues = await dateienLesen(verzeichnis);
@@ -215,7 +225,7 @@ async function main() {
}
console.log(`Ziel: ${basis}/${repo} (Token aus: ${tokenQuelle})`);
const gitea = new Gitea(basis, repo, token);
const gitea = new Gitea(basis, repo, autorisierung);
const vorhandeneLabels = new Map((await gitea.labels()).map((l) => [l.name.toLowerCase(), l.id]));
const benoetigteLabels = [...new Set(issues.flatMap((issue) => issue.labels))];