Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e1d6c4418 | ||
|
|
2dbb30041b |
@@ -17,6 +17,11 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
|
||||
|
||||
### Sicherheit
|
||||
|
||||
## [0.4.2] – 2026-09-17
|
||||
|
||||
### Geändert
|
||||
- Der Issue-Abgleich holt Serveradresse und Repository aus dem Git-Remote und den Token aus den hinterlegten Git-Zugangsdaten; ein zweiter Token in der `.env` ist nicht mehr nötig (#020)
|
||||
|
||||
## [0.4.1] – 2026-09-17
|
||||
|
||||
### Hinzugefügt
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Zeitübersicht
|
||||
|
||||
**Gesamt:** 2 h 48 min
|
||||
**Gesamt:** 2 h 58 min
|
||||
|
||||
| Issue | Zeit |
|
||||
|---|---|
|
||||
@@ -13,7 +13,7 @@
|
||||
| #005 Agent-Skript | 0 h 25 min |
|
||||
| #006 Kunden und Hostzuweisung | 0 h 35 min |
|
||||
| #007 Host-Tabelle und Detailansichten | 0 h 25 min |
|
||||
| #020 Issues nach Gitea übertragen | 0 h 10 min |
|
||||
| #020 Issues nach Gitea übertragen | 0 h 20 min |
|
||||
|
||||
| Meilenstein | Zeit |
|
||||
|---|---|
|
||||
@@ -22,12 +22,13 @@
|
||||
| v0.3.0 | 0 h 35 min | **abgeschlossen**
|
||||
| v0.4.0 | 0 h 25 min | **abgeschlossen**
|
||||
| v0.4.1 | 0 h 10 min | **abgeschlossen**
|
||||
| v0.4.2 | 0 h 10 min | **abgeschlossen**
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-17 · Sitzung 1 · 19:36–laufend
|
||||
**Issue:** #001–#007 (Meilensteine v0.1.0 bis v0.4.0)
|
||||
**Version:** 0.4.1
|
||||
**Version:** 0.4.2
|
||||
|
||||
### Erledigt
|
||||
- Auftrag gelesen, Rückfragen gestellt
|
||||
@@ -49,6 +50,8 @@
|
||||
- #007 abgeschlossen: Host-Tabelle mit Suche, fünf Filtern, Sortierung, Seitenblättern und CSV-Export; Host-Detail mit sechs Tabs, Notizen, Tags, Kundenzuweisung und Token-Widerruf
|
||||
- Meilenstein v0.4.0 abgeschlossen und getaggt; 59 Tests
|
||||
- #020 abgeschlossen: Abgleichskript für den Gitea-Issue-Tracker (Missverständnis geklärt – die Issues lagen bisher nur als Dateien im Repo, nicht im Tracker)
|
||||
- Token kommt aus den Git-Zugangsdaten statt aus einer zweiten Stelle; 20 Issues, 15 Labels und 13 Meilensteine in Gitea angelegt
|
||||
- Alles nach Gitea gepusht: main bei 51243c2, Tags v0.1.0 bis v0.4.1
|
||||
|
||||
### Entscheidungen
|
||||
- **Screenshots mit Firefox headless** statt Playwright: Firefox ist vorhanden, es muss kein Browser samt Systembibliotheken nachinstalliert werden (dafür wäre root nötig).
|
||||
@@ -75,5 +78,4 @@
|
||||
- Offen: Rolle „readonly“ zusätzlich zu Admin? (vorläufig vorgesehen)
|
||||
|
||||
### Nächster Schritt
|
||||
- Gitea-Token erzeugen und `npm run issues:sync` einmal echt laufen lassen
|
||||
- #008 Laufzeiten und Rechnungen: Vorschläge bestätigen, Rechnungen erfassen, Häkchen direkt in der Tabelle
|
||||
|
||||
@@ -52,16 +52,16 @@ jedem Klon mit. Wer sie zusätzlich im Gitea-Tracker haben möchte, gleicht sie
|
||||
|
||||
```bash
|
||||
npm run issues:sync -- --dry-run # zeigt nur, was passieren würde
|
||||
|
||||
GITEA_URL=https://git.example.de \
|
||||
GITEA_REPO=benutzer/ProxLedger \
|
||||
GITEA_TOKEN=... \
|
||||
npm run issues:sync
|
||||
npm run issues:sync # echter Abgleich
|
||||
```
|
||||
|
||||
Der Token braucht das Recht `issue:write` (Gitea: Einstellungen → Anwendungen → Zugriffstoken). Er kann auch in
|
||||
`.env` stehen, die Datei ist von Git ausgenommen. Der Abgleich legt Labels und Meilensteine an, erkennt
|
||||
vorhandene Issues an der Nummer im Titel, aktualisiert sie und schließt erledigte.
|
||||
Serveradresse und Repository liest das Skript aus dem Git-Remote, den Token aus den hinterlegten
|
||||
Git-Zugangsdaten (Schlüsselbund oder `~/.git-credentials`) – derselbe Token wie fürs Pushen, er braucht
|
||||
zusätzlich das Recht `issue:write`. Wer beides trennen möchte, setzt `GITEA_URL`, `GITEA_REPO` und
|
||||
`GITEA_TOKEN` in der `.env`.
|
||||
|
||||
Der Abgleich legt Labels und Meilensteine an, erkennt vorhandene Issues an der Nummer im Titel,
|
||||
aktualisiert sie und schließt erledigte. Ein zweiter Lauf erzeugt keine Dubletten.
|
||||
|
||||
## Dokumentation
|
||||
|
||||
|
||||
@@ -27,5 +27,9 @@ aktuell bleiben – ohne dass sie von Hand nachgepflegt werden müssen.
|
||||
## Notizen / Entscheidungen
|
||||
- Die Markdown-Dateien bleiben die Quelle der Wahrheit: Sie liegen im Repo, sind ohne Netz lesbar und
|
||||
wandern mit jedem Klon mit. Gitea bekommt eine Kopie zum Kommentieren und Zuweisen.
|
||||
- Der erste echte Lauf steht noch aus: Dafür braucht es einen Gitea-Token mit `issue:write`.
|
||||
Der Trockenlauf liest alle 20 Dateien korrekt.
|
||||
- Der Token kommt aus den Git-Zugangsdaten (`git credential fill`), Adresse und Repo aus dem Remote.
|
||||
Damit muss derselbe Token nicht zweimal gepflegt werden.
|
||||
- Gefragt werden zuerst die eingerichteten Helfer (Schlüsselbund), danach zusätzlich `~/.git-credentials` –
|
||||
je nach Rechner ist mal das eine, mal das andere gefüllt.
|
||||
- Erster echter Lauf am 17.09.2026: 20 Issues, 15 Labels und 13 Meilensteine angelegt, erledigte Issues
|
||||
direkt geschlossen. Zweiter Lauf: 0 neue, 20 aktualisiert – keine Dubletten.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "proxledger",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
+56
-5
@@ -10,6 +10,7 @@
|
||||
* Die Markdown-Dateien bleiben die Quelle der Wahrheit; Gitea erhält eine Kopie zum Kommentieren.
|
||||
*/
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFile, readdir } from "node:fs/promises";
|
||||
import "dotenv/config";
|
||||
import path from "node:path";
|
||||
@@ -138,6 +139,49 @@ class Gitea {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt Serveradresse und Repo aus dem Git-Remote, damit beides nicht doppelt gepflegt werden muss.
|
||||
* Beispiel: https://git.example.de/benutzer/ProxLedger.git → { basis, repo }
|
||||
*/
|
||||
function ausRemote(): { basis: string; repo: string } | null {
|
||||
try {
|
||||
const url = execFileSync("git", ["remote", "get-url", "origin"], { encoding: "utf8" }).trim();
|
||||
const treffer = /^https?:\/\/(?:[^@/]*@)?([^/]+)\/(.+?)(?:\.git)?$/.exec(url);
|
||||
if (!treffer) return null;
|
||||
return { basis: `https://${treffer[1]}`, repo: treffer[2] };
|
||||
} catch {
|
||||
return 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.
|
||||
*/
|
||||
function tokenAusGitCredentials(basis: string): string | null {
|
||||
const host = new URL(basis).host;
|
||||
|
||||
// Erst die eingerichteten Helfer (z. B. Schlüsselbund), dann zusätzlich die Datei
|
||||
// ~/.git-credentials – je nach Rechner ist mal das eine, mal das andere gefüllt.
|
||||
for (const zusatz of [[], ["-c", "credential.helper=store"]]) {
|
||||
try {
|
||||
const antwort = execFileSync("git", [...zusatz, "credential", "fill"], {
|
||||
input: `protocol=https\nhost=${host}\n\n`,
|
||||
encoding: "utf8",
|
||||
// Keine Rückfrage im Terminal: Wenn nichts hinterlegt ist, soll der Aufruf scheitern.
|
||||
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" },
|
||||
stdio: ["pipe", "pipe", "ignore"],
|
||||
});
|
||||
const passwort = /^password=(.*)$/m.exec(antwort);
|
||||
if (passwort?.[1]) return passwort[1];
|
||||
} catch {
|
||||
// Nächste Möglichkeit versuchen.
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Titel in Gitea, z. B. „#004 Agent-Skript“ – daran werden vorhandene Issues wiedererkannt. */
|
||||
function giteaTitel(issue: IssueDatei): string {
|
||||
return `#${String(issue.nummer).padStart(3, "0")} ${issue.titel}`;
|
||||
@@ -145,9 +189,11 @@ function giteaTitel(issue: IssueDatei): string {
|
||||
|
||||
async function main() {
|
||||
const trockenlauf = process.argv.includes("--dry-run");
|
||||
const basis = (process.env.GITEA_URL ?? "").replace(/\/$/, "");
|
||||
const repo = process.env.GITEA_REPO ?? "";
|
||||
const token = process.env.GITEA_TOKEN ?? "";
|
||||
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 tokenQuelle = process.env.GITEA_TOKEN ? "Umgebungsvariable" : "Git-Zugangsdaten";
|
||||
|
||||
const verzeichnis = path.join(process.cwd(), "docs", "issues");
|
||||
const issues = await dateienLesen(verzeichnis);
|
||||
@@ -155,7 +201,10 @@ async function main() {
|
||||
|
||||
if (trockenlauf || !basis || !repo || !token) {
|
||||
if (!trockenlauf) {
|
||||
console.log("\nGITEA_URL, GITEA_REPO oder GITEA_TOKEN fehlen – es läuft nur ein Trockenlauf.\n");
|
||||
console.log(
|
||||
"\nAdresse, Repo oder Token nicht ermittelbar – es läuft nur ein Trockenlauf.\n" +
|
||||
"Entweder GITEA_TOKEN setzen oder einmal `git push` ausführen, damit der Token hinterlegt wird.\n",
|
||||
);
|
||||
}
|
||||
for (const issue of issues) {
|
||||
console.log(
|
||||
@@ -165,11 +214,13 @@ async function main() {
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
"\nEchter Abgleich: GITEA_URL, GITEA_REPO und GITEA_TOKEN setzen (Token mit Recht „issue:write“).",
|
||||
`\nZiel wäre: ${basis || "?"}/${repo || "?"} – Token ${token ? "vorhanden" : "fehlt"}.` +
|
||||
"\nDer Token wird aus den Git-Zugangsdaten gelesen; er braucht zusätzlich das Recht „issue:write“.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Ziel: ${basis}/${repo} (Token aus: ${tokenQuelle})`);
|
||||
const gitea = new Gitea(basis, repo, token);
|
||||
|
||||
const vorhandeneLabels = new Map((await gitea.labels()).map((l) => [l.name.toLowerCase(), l.id]));
|
||||
|
||||
Reference in New Issue
Block a user