import { execFile } from 'child_process';
|
import { promisify } from 'util';
|
import * as path from 'path';
|
import { abgleichMisserfolg, alsIsoDatum, dateiVorhanden, mitTemporaeremOrdner, type Abgleichauftrag, type Bezugsquelle, type QuellenAbgleich } from './basis';
|
|
const execFileAsync = promisify(execFile);
|
|
/**
|
* SVN: exportiert eine Datei aus einem Repository, erkennt Änderungen über die Revision.
|
* Anmeldedaten kommen aus dem Windows-Anmeldespeicher des Dienstbenutzers —
|
* die Erstanmeldung muss einmal interaktiv als dieser Benutzer erfolgt sein.
|
*/
|
export class SvnQuelle implements Bezugsquelle {
|
async gleicheAb(auftrag: Abgleichauftrag): Promise<QuellenAbgleich> {
|
const dateiname = decodeURIComponent(auftrag.wert.split('/').pop() ?? 'datei');
|
let revision = '';
|
let veroeffentlichtAm = '';
|
try {
|
const svnInfo = (element: string) =>
|
execFileAsync('svn', ['info', '--show-item', element, '--non-interactive', auftrag.wert], { timeout: 60000, windowsHide: true });
|
revision = (await svnInfo('last-changed-revision')).stdout.trim();
|
veroeffentlichtAm = alsIsoDatum((await svnInfo('last-changed-date')).stdout.trim());
|
} catch (fehler) {
|
return abgleichMisserfolg(auftrag.aktuelleVersion, svnFehlertext(fehler));
|
}
|
|
if (revision && revision === auftrag.aktuelleVersion && dateiVorhanden(auftrag.zielordner, dateiname)) {
|
return { erfolg: true, geaendert: false, version: revision, dateiname, meldung: 'Bereits aktuell.', veroeffentlichtAm };
|
}
|
|
const ablage = mitTemporaeremOrdner(auftrag.zielordner);
|
try {
|
await execFileAsync('svn', ['export', '--force', '--non-interactive', auftrag.wert, path.join(ablage.ordner, dateiname)], {
|
timeout: 300000,
|
windowsHide: true,
|
});
|
ablage.uebernehmen();
|
return { erfolg: true, geaendert: true, version: revision || new Date().toISOString(), dateiname, meldung: `SVN-Revision ${revision} exportiert.`, veroeffentlichtAm };
|
} catch (fehler) {
|
ablage.verwerfen();
|
return abgleichMisserfolg(auftrag.aktuelleVersion, svnFehlertext(fehler));
|
}
|
}
|
}
|
|
/** Macht fehlende Anmeldedaten als häufigste Ursache kenntlich. */
|
function svnFehlertext(fehler: unknown): string {
|
const text = fehler instanceof Error ? fehler.message : String(fehler);
|
if (/authoriz|authentic|E170001|credentials/i.test(text)) {
|
return `SVN-Anmeldung fehlt oder ist ungültig. Einmalig als Dienstbenutzer anmelden (die Anmeldung wird dann im Windows-Speicher hinterlegt). Ursprüngliche Meldung: ${text}`;
|
}
|
return `SVN fehlgeschlagen: ${text}`;
|
}
|