#!/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}/" 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"