diff --git a/README.de.md b/README.de.md index caac64d..d7c30c4 100644 --- a/README.de.md +++ b/README.de.md @@ -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//`; eine direkt +aufgeschaltete Domain läge unter `…/htdocs-ssl/`.) + ## Projektdokumente - `frontend/` — Editor · `backend/` — Kotlin/Spring (Gerüst folgt, siehe backend/README.md) diff --git a/README.md b/README.md index 4d3585c..ea1f80c 100644 --- a/README.md +++ b/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//`; a directly-served domain would live under +`…/htdocs-ssl/`.) + ## Project documents - `frontend/` — editor · `backend/` — Kotlin/Spring (scaffold to follow, see backend/README.md) diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index 90b777f..a8f40ab 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.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 `` / `../LICENSE`). + (Nummerierung: D15 war bereits für den kompakten Modus vergeben, daher D16.) ## D17 — Kleiner Bildschirm: ein Bereich, kompakte Legende & Sprachwahl diff --git a/scripts/deploy-prod.sh b/scripts/deploy-prod.sh new file mode 100755 index 0000000..a7b8a9c --- /dev/null +++ b/scripts/deploy-prod.sh @@ -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] +# +# -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] " >&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#\(]*>\)[0-9.]\+#\1${BUILD_VERSION}#") +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."