scripts: deploy-prod.sh — Prod-Installation via rsync/SSH spiegeln
Neues Skript scripts/deploy-prod.sh baut das badge-freie Prod-Bundle (npm run build:prod), stellt es lokal wie der Pages-Workflow zusammen (LICENSE danebenlegen + ../LICENSE-Link geradeziehen, Footer-Version + Commit-Link) und spiegelt es per rsync --delete ueber SSH in ein als Argument uebergebenes Zielverzeichnis — am Ziel bleibt nichts Altes stehen. - Pflichtargument: rsync-Ziel (z. B. user@host:~/doms/.../subs-ssl/werkbaum/). - Ohne -y erst --dry-run-Vorschau + Rueckfrage (--delete ist destruktiv). - Warnt bei unsauberem Arbeitsbaum (Commit-Link zeigt auf HEAD). - node_modules-Guard: npm ci nur wenn noetig. End-to-End gegen ein lokales Ziel verifiziert: --delete raeumt Alt-Datei und Alt-Unterordner weg, deployte index.html enthaelt 0x 🚧/🔧 (nur die ungenutzte CSS-Regel), LICENSE danebengelegt + Link auf "LICENSE", Version 1.0.x + Commit. Doku: README (de/en) Abschnitt Prod-Installation, DECISIONS D16 (warum die sed-Zusammenstellung bewusst in Workflow UND Skript liegt). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6f184222a6
commit
ec6d2124de
@@ -86,6 +86,22 @@ selbst geradeziehst: der Footer-Link **MIT-License** zeigt relativ auf
|
||||
an), und die **Versionsnummer** bleibt der Quelltext-Platzhalter `1.0` (der
|
||||
Workflow ersetzt ihn sonst aus `VERSION` + Commit-Zahl).
|
||||
|
||||
**Bequemer: `scripts/deploy-prod.sh`.** Das Skript macht genau den obigen
|
||||
Prod-Build **und** die beiden Nacharbeiten (LICENSE danebenlegen + Link
|
||||
geradeziehen, Footer-Version + Commit-Link wie beim Pages-Workflow) und spiegelt
|
||||
das Ergebnis per rsync/SSH auf einen Server. Zielpfad als einziges Pflichtargument:
|
||||
|
||||
```bash
|
||||
scripts/deploy-prod.sh mih00@mih00.hostsharing.net:~/doms/javagil.de/subs-ssl/werkbaum/
|
||||
```
|
||||
|
||||
Ohne `-y` zeigt es zuerst eine `--dry-run`-Vorschau und fragt nach. `rsync
|
||||
--delete` sorgt dafür, dass am Ziel **nichts Altes** stehen bleibt — das
|
||||
Zielverzeichnis gilt also als exklusiv für Werkbaum. (Hostsharing: für eine
|
||||
**Subdomain** wie `werkbank.javagil.de` unter der aufgeschalteten Domain
|
||||
`javagil.de` ist das Web-Verzeichnis `…/subs-ssl/<name>/`; eine direkt
|
||||
aufgeschaltete Domain läge unter `…/htdocs-ssl/`.)
|
||||
|
||||
## Projektdokumente
|
||||
|
||||
- `frontend/` — Editor · `backend/` — Kotlin/Spring (Gerüst folgt, siehe backend/README.md)
|
||||
|
||||
@@ -84,6 +84,22 @@ that file one level above `index.html`, or adjust the link), and the **version
|
||||
number** stays the source placeholder `1.0` (the workflow otherwise replaces it
|
||||
from `VERSION` + commit count).
|
||||
|
||||
**Easier: `scripts/deploy-prod.sh`.** The script runs exactly that prod build
|
||||
**and** both fix-ups (drop `LICENSE` alongside + straighten the link, footer
|
||||
version + commit link just like the Pages workflow) and mirrors the result to a
|
||||
server via rsync/SSH. The target path is the only required argument:
|
||||
|
||||
```bash
|
||||
scripts/deploy-prod.sh mih00@mih00.hostsharing.net:~/doms/javagil.de/subs-ssl/werkbaum/
|
||||
```
|
||||
|
||||
Without `-y` it first shows a `--dry-run` preview and asks for confirmation.
|
||||
`rsync --delete` ensures **nothing old** is left at the target — so the target
|
||||
directory is treated as exclusive to Werkbaum. (Hostsharing: for a **subdomain**
|
||||
like `werkbank.javagil.de` under the parent domain `javagil.de` the web directory
|
||||
is `…/subs-ssl/<name>/`; a directly-served domain would live under
|
||||
`…/htdocs-ssl/`.)
|
||||
|
||||
## Project documents
|
||||
|
||||
- `frontend/` — editor · `backend/` — Kotlin/Spring (scaffold to follow, see backend/README.md)
|
||||
|
||||
@@ -159,6 +159,16 @@ stabil wirkenden. Bewusst **kein** i18n-UI-Text im `I18N`-Objekt (kein
|
||||
Produkt-Feature, sondern Build-Metainformation; D14) — der `title` ist knapp
|
||||
zweisprachig (DE · EN).
|
||||
|
||||
**Self-hosted Deploy (`scripts/deploy-prod.sh`):** Für eine eigene produktive
|
||||
Installation (nicht GitHub Pages) baut das Skript per `npm run build:prod`
|
||||
(badge-frei), stellt lokal dasselbe zusammen wie der Pages-Workflow (LICENSE
|
||||
danebenlegen + `../LICENSE`-Link geradeziehen, Footer-Version + Commit-Link) und
|
||||
spiegelt es per `rsync --delete` über SSH ins Zielverzeichnis (nichts Altes bleibt
|
||||
stehen). Die Zusammenstell-Schritte (dieselben drei `sed`-Regeln) liegen bewusst
|
||||
in **beiden** — Workflow und Skript —, weil GitHub Pages die `dist/` selbst baut
|
||||
und der SSH-Weg keinen Pages-Runner hat; bei Änderungen an den `sed`-Regeln beide
|
||||
Stellen nachziehen (Anker `<a class="ver">` / `../LICENSE`).
|
||||
|
||||
(Nummerierung: D15 war bereits für den kompakten Modus vergeben, daher D16.)
|
||||
|
||||
## D17 — Kleiner Bildschirm: ein Bereich, kompakte Legende & Sprachwahl
|
||||
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Werkbaum — Produktions-Deploy via rsync/SSH.
|
||||
#
|
||||
# Baut das badge-freie Prod-Bundle (`npm run build:prod`, ohne den
|
||||
# Entwicklungs-Hinweis hinter dem Titel), stellt es lokal genauso zusammen wie
|
||||
# der GitHub-Pages-Workflow (LICENSE-Link geradeziehen + Footer-Version/Commit)
|
||||
# und spiegelt es per rsync in ein Zielverzeichnis. `--delete`: am Ziel bleibt
|
||||
# nichts Altes stehen.
|
||||
#
|
||||
# Verwendung:
|
||||
# scripts/deploy-prod.sh [-y] <rsync-ziel>
|
||||
#
|
||||
# -y ohne Rückfrage spiegeln (sonst erst Vorschau via --dry-run + Nachfrage)
|
||||
#
|
||||
# Beispiel (Hostsharing, Subdomain werkbank.javagil.de unter der Domain
|
||||
# javagil.de — deshalb `subs-ssl/`; wäre die Domain direkt aufgeschaltet, läge
|
||||
# das Web-Verzeichnis in `htdocs-ssl/`):
|
||||
#
|
||||
# scripts/deploy-prod.sh mih00@mih00.hostsharing.net:~/doms/javagil.de/subs-ssl/werkbaum/
|
||||
#
|
||||
# ACHTUNG: Das Zielverzeichnis wird als exklusiv für Werkbaum angenommen —
|
||||
# `--delete` entfernt dort ALLES, was nicht zum Bundle gehört.
|
||||
#
|
||||
# Siehe README (Abschnitt Deployment) und docs/DECISIONS.md D16/D19.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ---- Argumente ----
|
||||
YES=0
|
||||
TARGET=""
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
-y|--yes) YES=1 ;;
|
||||
-h|--help)
|
||||
awk 'NR>2 { if ($0 ~ /^#/) { sub(/^# ?/, ""); print } else exit }' "$0"
|
||||
exit 0 ;;
|
||||
-*)
|
||||
echo "Unbekannte Option: $arg" >&2; exit 2 ;;
|
||||
*)
|
||||
if [ -n "$TARGET" ]; then echo "Zu viele Argumente." >&2; exit 2; fi
|
||||
TARGET="$arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$TARGET" ]; then
|
||||
echo "Usage: $0 [-y] <rsync-ziel>" >&2
|
||||
echo " z.B. $0 mih00@mih00.hostsharing.net:~/doms/javagil.de/subs-ssl/werkbaum/" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# ---- Repo-Wurzel (Skript ist unter scripts/) ----
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
# ---- 1) Prod-Build (ohne Build-Hinweis) ----
|
||||
if [ ! -d frontend/node_modules ]; then
|
||||
echo "==> node_modules fehlt — npm ci"
|
||||
npm ci --prefix frontend
|
||||
fi
|
||||
echo "==> npm run build:prod"
|
||||
npm run build:prod --prefix frontend
|
||||
|
||||
# ---- 2) Staging zusammenstellen (wie der Pages-Workflow, aber lokal) ----
|
||||
STAGE="$(mktemp -d)"
|
||||
trap 'rm -rf "$STAGE"' EXIT
|
||||
|
||||
# Footer-Version: Major.Minor aus VERSION, Micro = Commits seit dem letzten
|
||||
# VERSION-Bump; Versionslink zeigt auf den exakt deployten Commit. Best effort —
|
||||
# ohne Git bleibt der Quelltext-Platzhalter stehen.
|
||||
SED_ARGS=(-e 's#\.\./LICENSE#LICENSE#g')
|
||||
if git -C "$ROOT" rev-parse HEAD >/dev/null 2>&1; then
|
||||
if [ -n "$(git -C "$ROOT" status --porcelain)" ]; then
|
||||
echo " ! Arbeitsbaum ist nicht sauber — der Versions-Commit-Link zeigt auf HEAD," >&2
|
||||
echo " der deployte Inhalt kann davon abweichen." >&2
|
||||
fi
|
||||
MAJORMINOR="$(tr -d '[:space:]' < VERSION)"
|
||||
BASE="$(git -C "$ROOT" log -1 --format=%H -- VERSION || true)"
|
||||
[ -z "$BASE" ] && BASE="$(git -C "$ROOT" rev-list --max-parents=0 HEAD | tail -1)"
|
||||
MICRO="$(git -C "$ROOT" rev-list --count "${BASE}..HEAD")"
|
||||
BUILD_VERSION="${MAJORMINOR}.${MICRO}"
|
||||
COMMIT_URL="https://github.com/mhoennig/werkbaum/commit/$(git -C "$ROOT" rev-parse HEAD)"
|
||||
echo "==> Footer-Version ${BUILD_VERSION} -> ${COMMIT_URL}"
|
||||
SED_ARGS+=(-e "s#\(<a class=\"ver\" href=\"\)[^\"]*#\1${COMMIT_URL}#")
|
||||
SED_ARGS+=(-e "s#\(<a class=\"ver\"[^>]*>\)[0-9.]\+</a>#\1${BUILD_VERSION}</a>#")
|
||||
else
|
||||
echo " ! kein Git-Repo — Footer behält den Versions-Platzhalter" >&2
|
||||
fi
|
||||
|
||||
sed "${SED_ARGS[@]}" frontend/dist/index.html > "$STAGE/index.html"
|
||||
cp LICENSE "$STAGE/LICENSE"
|
||||
|
||||
# ---- 3) Spiegeln (--delete: nichts Altes bleibt am Ziel) ----
|
||||
if [ "$YES" -ne 1 ]; then
|
||||
echo "==> Vorschau (rsync --dry-run --delete) nach ${TARGET}:"
|
||||
rsync -avz --delete --dry-run "$STAGE"/ "$TARGET"
|
||||
printf '==> Wirklich spiegeln? --delete löscht am Ziel alles Fremde. [y/N] '
|
||||
read -r ANS
|
||||
case "$ANS" in
|
||||
y|Y|j|J) : ;;
|
||||
*) echo "Abgebrochen."; exit 1 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo "==> rsync -> ${TARGET}"
|
||||
rsync -avz --delete "$STAGE"/ "$TARGET"
|
||||
echo "==> Fertig."
|
||||
Reference in New Issue
Block a user