#!/usr/bin/env bash # # Erstellt einen Production-Build und veröffentlicht ihn auf den Branch main. # # 1. Wechselt auf main und setzt ihn auf den Stand von dev # 2. Entfernt Kommentare, minifiziert CSS, obfuskiert JavaScript # 3. Committet und pusht main (optional) # 4. Wechselt zurück auf den ursprünglichen Branch (dev bleibt unverändert) # # Nutzung: # ./scripts/publish-to-main.sh # ./scripts/publish-to-main.sh --push # ./scripts/publish-to-main.sh --dry-run # ./scripts/publish-to-main.sh --allow-dirty --message "chore(release): v1.2" # set -euo pipefail PUSH=false DRY_RUN=false ALLOW_DIRTY=false MESSAGE="" usage() { cat <<'EOF' Usage: publish-to-main.sh [OPTIONS] Options: --push Push nach origin/main --dry-run Git-Schritte nur anzeigen (Build wird ausgeführt) --allow-dirty Uncommittete Änderungen erlauben --message TEXT Commit-Nachricht -h, --help Hilfe anzeigen EOF } while [[ $# -gt 0 ]]; do case "$1" in --push) PUSH=true shift ;; --dry-run) DRY_RUN=true shift ;; --allow-dirty) ALLOW_DIRTY=true shift ;; --message) MESSAGE="${2:-}" shift 2 ;; -h|--help) usage exit 0 ;; *) echo "Unbekannte Option: $1" >&2 usage >&2 exit 1 ;; esac done SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" BUILD_DIR="$ROOT/scripts/build" ORIGINAL_BRANCH="" step() { echo "" echo "==> $1" } require_command() { if ! command -v "$1" >/dev/null 2>&1; then echo "FEHLER: '$1' nicht gefunden. Bitte installieren und PATH setzen." >&2 exit 1 fi } ensure_git_clean() { if [[ -n "$(git -C "$ROOT" status --porcelain)" ]]; then echo "FEHLER: Uncommittete Änderungen. Bitte zuerst committen oder stashen." >&2 exit 1 fi } cleanup_on_error() { echo "" echo "FEHLER: Abgebrochen." >&2 cd "$ROOT" || true if [[ -n "$ORIGINAL_BRANCH" && "$DRY_RUN" == false ]]; then git checkout "$ORIGINAL_BRANCH" 2>/dev/null || true fi } trap cleanup_on_error ERR require_command node require_command npm require_command git cd "$ROOT" if [[ "$ALLOW_DIRTY" == false ]]; then ensure_git_clean else echo "WARNUNG: --allow-dirty aktiv – uncommittete Änderungen werden mit veröffentlicht." >&2 fi ORIGINAL_BRANCH="$(git branch --show-current | tr -d '[:space:]')" if [[ "$ORIGINAL_BRANCH" != "dev" ]]; then echo "WARNUNG: Empfohlen auf Branch 'dev' zu starten (aktuell: ${ORIGINAL_BRANCH:-detached})" >&2 fi if [[ -z "$MESSAGE" ]]; then MESSAGE="chore(release): production build $(date '+%Y-%m-%d %H:%M')" fi step "Installiere Build-Abhängigkeiten" cd "$BUILD_DIR" if [[ "$DRY_RUN" == false ]]; then npm ci --no-fund --no-audit fi step "Wechsle auf main und synchronisiere mit dev" cd "$ROOT" if [[ "$DRY_RUN" == true ]]; then echo "[DryRun] git checkout main" echo "[DryRun] git reset --hard dev" else git checkout main git reset --hard dev fi step "Production-Build (Kommentare entfernen, JS obfuscaten)" cd "$BUILD_DIR" if [[ "$DRY_RUN" == true ]]; then echo "[DryRun] npm run build:in-place" else npm run build:in-place fi step "Production-Build committen" cd "$ROOT" if [[ "$DRY_RUN" == true ]]; then echo "[DryRun] git add -A" echo "[DryRun] git commit -m \"$MESSAGE\"" else git add -A if git diff --cached --quiet; then echo "WARNUNG: Keine Build-Änderungen – nichts zu committen." >&2 else git commit -m "$MESSAGE" fi fi if [[ "$PUSH" == true ]]; then step "Push nach origin/main" if [[ "$DRY_RUN" == true ]]; then echo "[DryRun] git push origin main" else git push origin main fi else echo "Hinweis: Ohne --push wurde nur lokal auf main gebaut." fi step "Zurück auf ${ORIGINAL_BRANCH:-dev}" if [[ "$DRY_RUN" == false ]]; then if [[ -n "$ORIGINAL_BRANCH" ]]; then git checkout "$ORIGINAL_BRANCH" else git checkout dev fi fi trap - ERR echo "" echo "Production-Release abgeschlossen." if [[ "$PUSH" == false && "$DRY_RUN" == false ]]; then echo "Zum Veröffentlichen: git push origin main" fi