Eine Vorlage allein bringt wenig — sie stellt Fragen, die der Meldende meist nicht beantworten kann. Deshalb zwei Teile: diagnose.sh sammelt in einem Zug, was zur Analyse gebraucht wird: Fassungen aller acht Programme, Betriebssystem, Container ja/nein, Dateisystem des Repositorys, PostgreSQL-Fassung, Schemastand, Dienstzustand, Gesundheitsbericht (der auch bei 503 den vollstaendigen Bericht traegt), Bestand, die letzten nicht erfolgreichen Laeufe mit Fehlercode UND Fehlerklasse, die gemessene Durchsetzungsstufe und die letzten Fehlerzeilen. Es liest nur. Geheimnisse kommen nicht hinein, und der Weg dahin ist umgekehrt: Es gibt eine Liste der Werte, die gezeigt werden duerfen. Eine Sperrliste vergaesse den naechsten neuen Wert. Zusaetzlich werden die tatsaechlichen Geheimnisse gelesen und aus JEDER Ausgabe entfernt — auch aus Protokollzeilen, in die sie auf einem unvorhergesehenen Weg geraten sind. Real geprueft: weder Datenbankpasswort noch Schluessel noch Administratorpasswort stehen im Bericht. Die Vorlage beginnt mit sieben Faellen, die wie ein Fehler aussehen und gewolltes Verhalten sind — "advisory" statt "filesystem", ein Teilfehler, "geloescht aber nichts frei", 503 mit vollstaendigem Bericht. Das ist keine Abwehr, sondern spart beiden Seiten einen halben Tag. Pflichtfelder sind Beobachtung, Erwartung, Schritte, Bereich, Datenrisiko, Haeufigkeit und der Diagnosebericht; Fehlercode und request_id stehen eigens da, weil sie die beiden wertvollsten Angaben sind. Beim Erproben zwei Funde: - Die Installationsanleitung verlangte PostgreSQL 17. setup.sh installiert auf Debian 12 aber 15 — und alles lief, bis hin zu einem echten Sicherungslauf. Die Anforderung lautet jetzt 15 (geprueft gegen 17 in CI und Entwicklung, gegen 15 auf Debian 12), und setup.sh lehnt aeltere Fassungen ab statt sie stillschweigend zu nehmen. - Ein Repository auf der Platte, das nicht in der Control Plane eingetragen ist, faellt niemandem auf: Die Sicherung laeuft nie, weil der Server das Ziel nicht kennt. Der Bericht benennt diesen Fall jetzt ausdruecklich. Gegen das echte v1.0.0-rc1-Paket gefahren: Installation, erzeugte Stoerung (ALL_SOURCES_FAILED / source), Bericht zeigt Code, Klasse, "overlayfs" und "nie gemessen". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
178 lines
6.8 KiB
Bash
Executable File
178 lines
6.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Baut das Auslieferungspaket der Version V1 (Phase 23).
|
|
#
|
|
# Das Paket enthält alles, was §25 des Umsetzungsplans verlangt: Server,
|
|
# Oberfläche, beide Agenten, Repository-Werkzeuge, Migrationen und die
|
|
# Dokumentation. Es ist bewusst **ein** Verzeichnisbaum je Zielplattform und
|
|
# kein Installationsprogramm: Ein Betreiber soll sehen können, was er auspackt.
|
|
#
|
|
# Aufruf: make release (oder scripts/build-release.sh)
|
|
|
|
set -euo pipefail
|
|
|
|
# releaseVersion ist die Version des Pakets.
|
|
#
|
|
# Ohne Git-Tag der Kurz-Hash — nie eine erfundene Nummer: Ein Paket, dessen
|
|
# Version man nicht auf einen Stand zurückführen kann, ist bei einer
|
|
# Fehlersuche wertlos.
|
|
releaseVersion="${BUILD_VERSION:-$(git describe --tags --always --dirty 2>/dev/null || echo "0.1.0-dev")}"
|
|
|
|
# distributionRoot ist das Ausgabeverzeichnis.
|
|
distributionRoot="dist"
|
|
|
|
# targetPlatforms sind die ausgelieferten Zielplattformen.
|
|
#
|
|
# Das Format ist "betriebssystem/architektur/dateiendung". macOS ist bewusst
|
|
# nicht dabei: Dort wurde entwickelt, aber der Plan nennt es nicht als Ziel, und
|
|
# eine Plattform auszuliefern, für die es kein Betriebskonzept gibt, weckt
|
|
# Erwartungen, die niemand einlöst.
|
|
targetPlatforms=(
|
|
"linux/amd64/"
|
|
"linux/arm64/"
|
|
"windows/amd64/.exe"
|
|
)
|
|
|
|
# serverCommands sind die Programme der Control Plane.
|
|
serverCommands=(
|
|
"syncova-api:apps/api/cmd/syncova-api"
|
|
"syncova-migrate:apps/api/cmd/syncova-migrate"
|
|
"syncova-admin:apps/api/cmd/syncova-admin"
|
|
"syncova-repo:apps/api/cmd/syncova-repo"
|
|
"syncova-dr:apps/api/cmd/syncova-dr"
|
|
"syncova-proxmox:apps/api/cmd/syncova-proxmox"
|
|
"syncova-bench:apps/api/cmd/syncova-bench"
|
|
)
|
|
|
|
# agentCommand ist das Programm des Agenten.
|
|
agentCommand="syncova-agent:apps/agent/cmd/syncova-agent"
|
|
|
|
# windowsServerCommands sind die Programme, die es auch für Windows gibt.
|
|
#
|
|
# Der vollständige Server läuft unter Linux; für Windows wird der Agent
|
|
# ausgeliefert und dazu die Werkzeuge, die ein Betreiber dort tatsächlich
|
|
# braucht. Alles andere zu übersetzen, nur weil es übersetzt, ergäbe Programme
|
|
# ohne Betriebskonzept.
|
|
windowsServerCommands=(
|
|
"syncova-repo:apps/api/cmd/syncova-repo"
|
|
)
|
|
|
|
echo "Syncova V1 — Auslieferungspaket ${releaseVersion}"
|
|
echo
|
|
|
|
rm -rf "${distributionRoot}"
|
|
mkdir -p "${distributionRoot}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Oberfläche
|
|
# ---------------------------------------------------------------------------
|
|
#
|
|
# Zuerst, weil sie am längsten braucht und weil ein Fehler hier den ganzen Lauf
|
|
# ungültig macht: Ein Paket ohne Oberfläche ist kein vollständiges Paket.
|
|
|
|
echo "==> Weboberfläche"
|
|
|
|
if [[ ! -d apps/web/node_modules ]]; then
|
|
echo " Abhängigkeiten fehlen — 'make web-install' zuerst ausführen." >&2
|
|
exit 1
|
|
fi
|
|
|
|
(cd apps/web && npm run build >/dev/null)
|
|
|
|
if [[ ! -f apps/web/dist/index.html ]]; then
|
|
echo " Der Bau der Oberfläche hat kein index.html erzeugt." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Programme je Zielplattform
|
|
# ---------------------------------------------------------------------------
|
|
|
|
for platformEntry in "${targetPlatforms[@]}"; do
|
|
IFS='/' read -r targetOS targetArch binaryExtension <<< "${platformEntry}"
|
|
|
|
packageName="syncova-${releaseVersion}-${targetOS}-${targetArch}"
|
|
packageRoot="${distributionRoot}/${packageName}"
|
|
|
|
echo "==> ${targetOS}/${targetArch}"
|
|
|
|
mkdir -p "${packageRoot}/bin" "${packageRoot}/migrations" "${packageRoot}/docs" \
|
|
"${packageRoot}/deployment"
|
|
|
|
# Welche Programme in dieses Paket gehören.
|
|
commandList=("${agentCommand}")
|
|
|
|
if [[ "${targetOS}" == "windows" ]]; then
|
|
commandList+=("${windowsServerCommands[@]}")
|
|
else
|
|
commandList+=("${serverCommands[@]}")
|
|
fi
|
|
|
|
for commandEntry in "${commandList[@]}"; do
|
|
commandName="${commandEntry%%:*}"
|
|
commandPath="${commandEntry#*:}"
|
|
|
|
# CGO aus: Ein statisch gebundenes Programm läuft auf jeder
|
|
# Distribution, unabhängig von deren libc-Version. Ohne diesen Schalter
|
|
# scheitert der Start auf einem älteren System mit einer Meldung über
|
|
# GLIBC, die niemand einem Backupprogramm zuordnet.
|
|
CGO_ENABLED=0 GOOS="${targetOS}" GOARCH="${targetArch}" \
|
|
go build -trimpath -ldflags "-s -w -X main.buildVersion=${releaseVersion}" \
|
|
-o "${packageRoot}/bin/${commandName}${binaryExtension}" "./${commandPath}"
|
|
|
|
echo " ${commandName}${binaryExtension}"
|
|
done
|
|
|
|
# Die Oberfläche gehört nur zum Serverpaket.
|
|
if [[ "${targetOS}" != "windows" ]]; then
|
|
mkdir -p "${packageRoot}/web"
|
|
cp -R apps/web/dist/. "${packageRoot}/web/"
|
|
fi
|
|
|
|
# Migrationen als Dateien **zusätzlich** zur Einbettung: Sie stecken bereits
|
|
# im Binary, aber ein Betreiber, der eine Abfrage nachvollziehen will, soll
|
|
# sie lesen können, ohne den Quellbestand zu holen.
|
|
cp migrations/*.sql "${packageRoot}/migrations/"
|
|
cp migrations/checksums.txt "${packageRoot}/migrations/"
|
|
|
|
cp -R docs/. "${packageRoot}/docs/"
|
|
cp README.md CHANGELOG.md "${packageRoot}/"
|
|
|
|
# Die Betriebsskripte gehoeren neben die Programme, nicht in einen
|
|
# Unterordner: Wer ein Paket auspackt, soll setup.sh sehen, ohne zu suchen.
|
|
# Fuer Windows entfallen sie — dort gibt es weder systemd noch apt.
|
|
if [[ "${targetOS}" != "windows" ]]; then
|
|
install -m 0755 scripts/setup.sh scripts/update.sh scripts/uninstall.sh \
|
|
scripts/diagnose.sh "${packageRoot}/"
|
|
fi
|
|
cp deployment/docker-compose.yml "${packageRoot}/deployment/" 2>/dev/null || true
|
|
cp deployment/syncova-agent.service "${packageRoot}/deployment/" 2>/dev/null || true
|
|
|
|
# Prüfsummen je Paket. Sie liegen **im** Paket, damit sich der Inhalt auch
|
|
# dann prüfen lässt, wenn nur er übertragen wurde.
|
|
(cd "${packageRoot}" && find . -type f ! -name SHA256SUMS -exec shasum -a 256 {} \; \
|
|
| sed 's|\./||' | sort -k2 > SHA256SUMS)
|
|
|
|
# Archiv: tar für Unix, zip für Windows — das jeweils übliche Format. Ein
|
|
# tar.gz auf einem Windows-Server auszupacken verlangt Zusatzwerkzeug.
|
|
if [[ "${targetOS}" == "windows" ]]; then
|
|
(cd "${distributionRoot}" && zip -qr "${packageName}.zip" "${packageName}")
|
|
else
|
|
(cd "${distributionRoot}" && tar -czf "${packageName}.tar.gz" "${packageName}")
|
|
fi
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. Prüfsummen über die Archive
|
|
# ---------------------------------------------------------------------------
|
|
|
|
(cd "${distributionRoot}" && shasum -a 256 ./*.tar.gz ./*.zip 2>/dev/null \
|
|
| sed 's|\./||' | sort -k2 > SHA256SUMS)
|
|
|
|
echo
|
|
echo "Auslieferungspakete:"
|
|
(cd "${distributionRoot}" && ls -lh ./*.tar.gz ./*.zip 2>/dev/null \
|
|
| awk '{printf " %-52s %s\n", $9, $5}')
|
|
echo
|
|
echo "Prüfsummen: ${distributionRoot}/SHA256SUMS"
|