Update-Skript fuer Linux/macOS und Windows
update.sh bzw. update.ps1 holen den neuen Stand und bauen den Stack neu: - Bricht ab, wenn git, docker compose oder die .env fehlen. - Bricht ab, wenn es lokale Aenderungen gibt — ein "git pull" wuerde dort im Konflikt enden. - Holt den Stand mit "git merge --ff-only" und zeigt die neuen Commits. - Baut nur neu, wenn es tatsaechlich Aenderungen gab (--force erzwingt es). - Wartet, bis die Anwendung auf APP_PORT antwortet, und gibt bei Zeitablauf das Container-Protokoll aus, statt stillschweigend Erfolg zu melden. - Zeigt zum Schluss die Migrationszeilen aus dem Start und den Status. - --prune raeumt auf Wunsch verwaiste Images auf, die bei jedem Neubau zurueckbleiben. README um einen Abschnitt "Aktualisieren" ergaenzt.
This commit is contained in:
@@ -12,6 +12,7 @@ vorbereitet.
|
|||||||
## Inhalt
|
## Inhalt
|
||||||
|
|
||||||
- [Schnellstart](#schnellstart)
|
- [Schnellstart](#schnellstart)
|
||||||
|
- [Aktualisieren](#aktualisieren)
|
||||||
- [Konfiguration](#konfiguration)
|
- [Konfiguration](#konfiguration)
|
||||||
- [Bedienung](#bedienung)
|
- [Bedienung](#bedienung)
|
||||||
- [Technischer Aufbau](#technischer-aufbau)
|
- [Technischer Aufbau](#technischer-aufbau)
|
||||||
@@ -45,6 +46,53 @@ docker compose down -v # Stack stoppen und Datenbank löschen
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Aktualisieren
|
||||||
|
|
||||||
|
Für neue Versionen liegt ein Update-Skript bei — einmal für Linux/macOS und
|
||||||
|
einmal für Windows. Es holt den aktuellen Stand aus Git, baut den Stack neu
|
||||||
|
und wartet, bis die Anwendung wieder antwortet.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./update.sh # Linux / macOS
|
||||||
|
```
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
.\update.ps1 # Windows
|
||||||
|
```
|
||||||
|
|
||||||
|
Beim ersten Aufruf unter Linux muss das Skript einmal ausführbar gemacht
|
||||||
|
werden: `chmod +x update.sh`.
|
||||||
|
|
||||||
|
Was das Skript tut:
|
||||||
|
|
||||||
|
1. prüft, ob `git`, `docker compose` und die `.env` vorhanden sind,
|
||||||
|
2. bricht ab, wenn es **lokale Änderungen** gibt — sonst würde `git pull` im
|
||||||
|
Konflikt enden,
|
||||||
|
3. holt den neuen Stand mit `git merge --ff-only` und zeigt die neuen Commits,
|
||||||
|
4. baut den Stack nur neu, wenn es tatsächlich Änderungen gab,
|
||||||
|
5. wartet, bis die Anwendung auf `APP_PORT` antwortet, und zeigt sonst das
|
||||||
|
Protokoll,
|
||||||
|
6. gibt die Migrationszeilen aus dem Start-Protokoll und den Containerstatus
|
||||||
|
aus.
|
||||||
|
|
||||||
|
Optionen:
|
||||||
|
|
||||||
|
| Option | Wirkung |
|
||||||
|
| ------------------- | -------------------------------------------------------------- |
|
||||||
|
| `--force` / `-Force` | Auch ohne neue Commits neu bauen |
|
||||||
|
| `--prune` / `-Prune` | Danach verwaiste Docker-Images entfernen (`docker image prune`) |
|
||||||
|
| `--help` | Hilfe anzeigen |
|
||||||
|
|
||||||
|
Die Datenbank bleibt beim Update erhalten — neue Migrationen wendet der
|
||||||
|
App-Container beim Start automatisch an. Wer lieber von Hand aktualisiert:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
docker compose up -d --build
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Konfiguration
|
## Konfiguration
|
||||||
|
|
||||||
Alle Einstellungen stehen in der `.env` im Projektverzeichnis (Vorlage:
|
Alle Einstellungen stehen in der `.env` im Projektverzeichnis (Vorlage:
|
||||||
@@ -171,6 +219,7 @@ prisma/
|
|||||||
migrations/ Versionierte Migrationen
|
migrations/ Versionierte Migrationen
|
||||||
docker/
|
docker/
|
||||||
entrypoint.sh Migration + Serverstart im Container
|
entrypoint.sh Migration + Serverstart im Container
|
||||||
|
update.sh / update.ps1 Update-Skript (git pull + Neubau + Prüfung)
|
||||||
src/
|
src/
|
||||||
app/
|
app/
|
||||||
page.tsx Startseite mit dem Kachel-Grid
|
page.tsx Startseite mit dem Kachel-Grid
|
||||||
|
|||||||
Regular → Executable
+149
@@ -0,0 +1,149 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
QR-Code-Generator aktualisieren: neuen Stand holen, Stack neu bauen, starten.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Windows-Gegenstück zu update.sh. Holt den aktuellen Stand aus dem Git-Remote
|
||||||
|
und baut den Docker-Stack neu, wenn es Änderungen gab.
|
||||||
|
|
||||||
|
.PARAMETER Force
|
||||||
|
Auch dann neu bauen, wenn es keine neuen Commits gibt.
|
||||||
|
|
||||||
|
.PARAMETER Prune
|
||||||
|
Nach dem Bauen verwaiste Docker-Images entfernen.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
.\update.ps1
|
||||||
|
.\update.ps1 -Force -Prune
|
||||||
|
#>
|
||||||
|
[CmdletBinding()]
|
||||||
|
param(
|
||||||
|
[switch]$Force,
|
||||||
|
[switch]$Prune
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
|
||||||
|
function Write-Info { param([string]$Text) Write-Host "→ $Text" }
|
||||||
|
function Write-Ok { param([string]$Text) Write-Host "✓ $Text" -ForegroundColor Green }
|
||||||
|
function Stop-WithError {
|
||||||
|
param([string]$Text)
|
||||||
|
Write-Host "✗ $Text" -ForegroundColor Red
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Immer im Projektverzeichnis arbeiten, egal von wo das Skript gestartet wird.
|
||||||
|
Set-Location -Path $PSScriptRoot
|
||||||
|
|
||||||
|
# --- Voraussetzungen --------------------------------------------------------
|
||||||
|
|
||||||
|
foreach ($tool in @('git', 'docker')) {
|
||||||
|
if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) {
|
||||||
|
Stop-WithError "$tool ist nicht installiert."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
docker compose version *> $null
|
||||||
|
if (-not $?) {
|
||||||
|
Stop-WithError 'docker compose ist nicht verfügbar (Docker Compose v2 wird benötigt).'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path .env)) {
|
||||||
|
Stop-WithError '.env fehlt. Anlegen mit: Copy-Item .env.example .env'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ein "git pull" auf einem veränderten Arbeitsverzeichnis endet im Konflikt.
|
||||||
|
$dirty = git status --porcelain
|
||||||
|
if ($dirty) {
|
||||||
|
Write-Host '✗ Es gibt lokale Änderungen im Arbeitsverzeichnis:' -ForegroundColor Red
|
||||||
|
git status --short
|
||||||
|
Write-Host ''
|
||||||
|
Stop-WithError "Bitte zuerst committen oder mit 'git stash' beiseitelegen."
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Neuen Stand holen ------------------------------------------------------
|
||||||
|
|
||||||
|
$before = git rev-parse HEAD
|
||||||
|
$branch = git rev-parse --abbrev-ref HEAD
|
||||||
|
|
||||||
|
Write-Info "Hole neuen Stand von origin/$branch …"
|
||||||
|
git fetch --quiet origin
|
||||||
|
if (-not $?) { Stop-WithError 'Der Remote ist nicht erreichbar.' }
|
||||||
|
|
||||||
|
# --ff-only: lieber abbrechen als eine Merge-Situation erzeugen.
|
||||||
|
git merge --ff-only "origin/$branch"
|
||||||
|
if (-not $?) {
|
||||||
|
Stop-WithError 'Der lokale Stand lässt sich nicht ohne Merge aktualisieren. Bitte von Hand prüfen.'
|
||||||
|
}
|
||||||
|
|
||||||
|
$after = git rev-parse HEAD
|
||||||
|
|
||||||
|
if ($before -eq $after) {
|
||||||
|
Write-Ok "Bereits auf dem neuesten Stand ($(git rev-parse --short HEAD))."
|
||||||
|
} else {
|
||||||
|
Write-Ok "Aktualisiert: $(git rev-parse --short $before) → $(git rev-parse --short $after)"
|
||||||
|
Write-Host ''
|
||||||
|
git --no-pager log --oneline "$before..$after"
|
||||||
|
Write-Host ''
|
||||||
|
}
|
||||||
|
|
||||||
|
# --- Stack neu bauen --------------------------------------------------------
|
||||||
|
|
||||||
|
$running = @(docker compose ps --status running --quiet).Count
|
||||||
|
|
||||||
|
if (($before -eq $after) -and (-not $Force) -and ($running -gt 0)) {
|
||||||
|
Write-Ok 'Der Stack läuft und es gibt nichts Neues — kein Neubau nötig.'
|
||||||
|
Write-Host ' (Mit .\update.ps1 -Force lässt sich ein Neubau erzwingen.)'
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Info 'Baue und starte den Stack neu — das dauert einige Minuten …'
|
||||||
|
docker compose up -d --build
|
||||||
|
if (-not $?) { Stop-WithError 'Der Neubau ist fehlgeschlagen.' }
|
||||||
|
|
||||||
|
# --- Warten, bis die Anwendung antwortet ------------------------------------
|
||||||
|
|
||||||
|
# Nur die Ziffern behalten — so stören weder Anführungszeichen noch Leerzeichen.
|
||||||
|
$portLine = Select-String -Path .env -Pattern '^APP_PORT=' | Select-Object -First 1
|
||||||
|
$appPort = if ($portLine) { ($portLine.Line -replace '\D', '') } else { '' }
|
||||||
|
if (-not $appPort) { $appPort = '3000' }
|
||||||
|
|
||||||
|
Write-Info 'Warte darauf, dass die Anwendung antwortet …'
|
||||||
|
$ready = $false
|
||||||
|
for ($attempt = 0; $attempt -lt 60; $attempt++) {
|
||||||
|
try {
|
||||||
|
Invoke-WebRequest -Uri "http://localhost:$appPort/" -UseBasicParsing -TimeoutSec 5 | Out-Null
|
||||||
|
$ready = $true
|
||||||
|
break
|
||||||
|
} catch {
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $ready) {
|
||||||
|
docker compose logs --tail 40 app
|
||||||
|
Stop-WithError 'Die Anwendung antwortet nach 2 Minuten nicht. Protokoll siehe oben.'
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Ok "Die Anwendung antwortet auf http://localhost:$appPort"
|
||||||
|
|
||||||
|
# --- Ergebnis ---------------------------------------------------------------
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Info 'Datenbankmigrationen beim Start:'
|
||||||
|
docker compose logs app |
|
||||||
|
Select-String -Pattern 'migration', 'Migration', 'pending' |
|
||||||
|
Select-Object -Last 5 |
|
||||||
|
ForEach-Object { " $($_.Line)" }
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
if ($Prune) {
|
||||||
|
Write-Host ''
|
||||||
|
Write-Info 'Räume verwaiste Docker-Images auf …'
|
||||||
|
docker image prune --force
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ''
|
||||||
|
Write-Ok "Fertig — Version $(git rev-parse --short HEAD)"
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# =============================================================================
|
||||||
|
# QR-Code-Generator aktualisieren
|
||||||
|
#
|
||||||
|
# ./update.sh neuen Stand holen und, falls es Änderungen gibt,
|
||||||
|
# den Stack neu bauen und starten
|
||||||
|
# ./update.sh --force auch ohne neue Commits neu bauen
|
||||||
|
# ./update.sh --prune danach verwaiste Docker-Images aufräumen
|
||||||
|
# ./update.sh --help diese Hilfe
|
||||||
|
# =============================================================================
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
FORCE=0
|
||||||
|
PRUNE=0
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'HILFE'
|
||||||
|
QR-Code-Generator aktualisieren.
|
||||||
|
|
||||||
|
./update.sh neuen Stand holen und, falls es Änderungen gibt,
|
||||||
|
den Stack neu bauen und starten
|
||||||
|
./update.sh --force auch ohne neue Commits neu bauen
|
||||||
|
./update.sh --prune danach verwaiste Docker-Images aufräumen
|
||||||
|
./update.sh --help diese Hilfe
|
||||||
|
HILFE
|
||||||
|
}
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--force) FORCE=1 ;;
|
||||||
|
--prune) PRUNE=1 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*)
|
||||||
|
echo "Unbekannte Option: $1" >&2
|
||||||
|
echo "Aufruf: ./update.sh [--force] [--prune]" >&2
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# Immer im Projektverzeichnis arbeiten, egal von wo das Skript gestartet wird.
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
info() { echo "→ $*"; }
|
||||||
|
ok() { echo "✓ $*"; }
|
||||||
|
fail() { echo "✗ $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
# --- Voraussetzungen --------------------------------------------------------
|
||||||
|
|
||||||
|
command -v git >/dev/null 2>&1 || fail "git ist nicht installiert."
|
||||||
|
command -v docker >/dev/null 2>&1 || fail "docker ist nicht installiert."
|
||||||
|
docker compose version >/dev/null 2>&1 ||
|
||||||
|
fail "docker compose ist nicht verfügbar (Docker Compose v2 wird benötigt)."
|
||||||
|
|
||||||
|
[ -f .env ] || fail ".env fehlt. Anlegen mit: cp .env.example .env"
|
||||||
|
|
||||||
|
# Ein "git pull" auf einem verändertem Arbeitsverzeichnis endet im Konflikt.
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo "✗ Es gibt lokale Änderungen im Arbeitsverzeichnis:" >&2
|
||||||
|
git status --short >&2
|
||||||
|
echo "" >&2
|
||||||
|
fail "Bitte zuerst committen oder mit 'git stash' beiseitelegen."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Neuen Stand holen ------------------------------------------------------
|
||||||
|
|
||||||
|
BEFORE="$(git rev-parse HEAD)"
|
||||||
|
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
|
||||||
|
info "Hole neuen Stand von origin/$BRANCH …"
|
||||||
|
git fetch --quiet origin
|
||||||
|
# --ff-only: lieber abbrechen als eine Merge-Situation erzeugen, die auf einem
|
||||||
|
# Server niemand auflösen will.
|
||||||
|
git merge --ff-only "origin/$BRANCH" ||
|
||||||
|
fail "Der lokale Stand lässt sich nicht ohne Merge aktualisieren. Bitte von Hand prüfen."
|
||||||
|
|
||||||
|
AFTER="$(git rev-parse HEAD)"
|
||||||
|
|
||||||
|
if [ "$BEFORE" = "$AFTER" ]; then
|
||||||
|
ok "Bereits auf dem neuesten Stand ($(git rev-parse --short HEAD))."
|
||||||
|
else
|
||||||
|
ok "Aktualisiert: $(git rev-parse --short "$BEFORE") → $(git rev-parse --short "$AFTER")"
|
||||||
|
echo ""
|
||||||
|
git --no-pager log --oneline "$BEFORE..$AFTER"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Stack neu bauen --------------------------------------------------------
|
||||||
|
|
||||||
|
RUNNING="$(docker compose ps --status running --quiet 2>/dev/null | wc -l | tr -d ' ')"
|
||||||
|
|
||||||
|
if [ "$BEFORE" = "$AFTER" ] && [ "$FORCE" -eq 0 ] && [ "$RUNNING" -gt 0 ]; then
|
||||||
|
ok "Der Stack läuft und es gibt nichts Neues — kein Neubau nötig."
|
||||||
|
echo " (Mit ./update.sh --force lässt sich ein Neubau erzwingen.)"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Baue und starte den Stack neu — das dauert einige Minuten …"
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# --- Warten, bis die Anwendung antwortet ------------------------------------
|
||||||
|
|
||||||
|
# Nur die Ziffern behalten — so stören weder Anführungszeichen noch ein
|
||||||
|
# Wagenrücklauf aus einer unter Windows bearbeiteten .env.
|
||||||
|
APP_PORT="$(grep -m1 '^APP_PORT=' .env | cut -d= -f2 | tr -cd '0-9')"
|
||||||
|
[ -n "$APP_PORT" ] || APP_PORT=3000
|
||||||
|
|
||||||
|
probe() {
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
curl --silent --fail --max-time 5 --output /dev/null "http://localhost:$APP_PORT/"
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
wget --quiet --timeout=5 --tries=1 --spider "http://localhost:$APP_PORT/"
|
||||||
|
else
|
||||||
|
return 0 # Kein Prüfwerkzeug vorhanden — dann eben ohne Prüfung.
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
info "Warte darauf, dass die Anwendung antwortet …"
|
||||||
|
ATTEMPT=0
|
||||||
|
until probe; do
|
||||||
|
ATTEMPT=$((ATTEMPT + 1))
|
||||||
|
if [ "$ATTEMPT" -ge 60 ]; then
|
||||||
|
echo ""
|
||||||
|
docker compose logs --tail 40 app >&2
|
||||||
|
fail "Die Anwendung antwortet nach 2 Minuten nicht. Protokoll siehe oben."
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
ok "Die Anwendung antwortet auf http://localhost:$APP_PORT"
|
||||||
|
|
||||||
|
# --- Ergebnis ---------------------------------------------------------------
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
info "Datenbankmigrationen beim Start:"
|
||||||
|
docker compose logs app 2>/dev/null |
|
||||||
|
grep -E "migration|Migration|pending" |
|
||||||
|
tail -n 5 |
|
||||||
|
sed 's/^/ /'
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
if [ "$PRUNE" -eq 1 ]; then
|
||||||
|
echo ""
|
||||||
|
info "Räume verwaiste Docker-Images auf …"
|
||||||
|
docker image prune --force
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
ok "Fertig — Version $(git rev-parse --short HEAD)"
|
||||||
Reference in New Issue
Block a user