#!/usr/bin/env bash # Van Emous CRM lokaal deployen vanaf een git-tag (zelfde werkwijze als straks op de server). # # ./deploy.sh nieuwste tag installeren en starten # ./deploy.sh v0.0.1 een bepaalde versie (ook om terug te rollen) # ./deploy.sh start de huidige versie starten zonder opnieuw te installeren # ./deploy.sh list geïnstalleerde versies tonen # # Elke versie komt uit `git archive ` (precies wat getagd is, niet de werkkopie) in # $CRM_HOME/releases/ met een eigen venv. `current` wijst naar de actieve versie. # De data ($CRM_HOME/data) is gedeeld en blijft bestaan; vóór elke versiewissel gaat er # een kopie van de database naar data/backups/, omdat migraties bij het starten draaien. # # De 3D-engine voor de viewer (gekozen model) zit niet in git: bij elke deploy komt die uit de # configurator ($CONFIGURATOR, standaard ../configurator) in de actieve versie (tools/configurator-sync.sh). # # Instellingen via omgevingsvariabelen: CRM_HOME (standaard ~/.local/share/vanemous-crm), # PORT (5050), HOST (127.0.0.1), WORKERS (2), CONFIGURATOR (../configurator). set -euo pipefail REPO="$(cd "$(dirname "$0")" && pwd)" CRM_HOME="${CRM_HOME:-$HOME/.local/share/vanemous-crm}" RELEASES="$CRM_HOME/releases" DATA="$CRM_HOME/data" PORT="${PORT:-5050}" HOST="${HOST:-127.0.0.1}" WORKERS="${WORKERS:-2}" CONFIGURATOR="${CONFIGURATOR:-$REPO/../configurator}" say() { printf '\033[38;2;185;89;19m▸\033[0m %s\n' "$*"; } fail() { printf 'Fout: %s\n' "$*" >&2; exit 1; } current_version() { [ -L "$CRM_HOME/current" ] && basename "$(readlink "$CRM_HOME/current")" || true; } start() { local dir="$CRM_HOME/current" [ -d "$dir" ] || fail "nog geen versie geïnstalleerd; draai eerst ./deploy.sh" say "Van Emous CRM $(current_version) op http://$HOST:$PORT (Ctrl+C om te stoppen)" say "data: $DATA" cd "$dir" CRM_DATA="$DATA" exec "$dir/.venv/bin/gunicorn" --workers "$WORKERS" --bind "$HOST:$PORT" \ --access-logfile - --error-logfile - app:app } case "${1:-}" in list) active="$(current_version)" for d in "$RELEASES"/*/; do [ -d "$d" ] || { echo "Nog geen versies geïnstalleerd."; exit 0; } v="$(basename "$d")"; [ "$v" = "$active" ] && echo "* $v (actief)" || echo " $v" done exit 0 ;; start) start ;; esac # --- welke versie --------------------------------------------------------------- git -C "$REPO" rev-parse --git-dir >/dev/null 2>&1 || fail "$REPO is geen git-repository" TAG="${1:-$(git -C "$REPO" tag --list 'v*' --sort=-v:refname | head -n1)}" [ -n "$TAG" ] || fail "geen versietags gevonden (verwacht bijv. v0.0.1)" [[ "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || fail "onbekende versie '$TAG' (verwacht vX.Y.Z)" git -C "$REPO" rev-parse -q --verify "refs/tags/$TAG" >/dev/null || fail "tag $TAG bestaat niet" # versie in de code moet bij de tag horen code_version="$(git -C "$REPO" show "$TAG:app.py" | sed -nE "s/^__version__ = '([^']+)'.*/\1/p")" [ "v$code_version" = "$TAG" ] || fail "tag $TAG bevat __version__ = '$code_version' in app.py; tag en code komen niet overeen" if [ -n "$(git -C "$REPO" status --porcelain)" ]; then say "let op: je werkkopie heeft niet-gecommitte wijzigingen; die zitten niet in $TAG" fi # --- installeren ---------------------------------------------------------------- target="$RELEASES/$TAG" mkdir -p "$RELEASES" "$DATA/backups" if [ -f "$target/.installed" ]; then say "$TAG is al geïnstalleerd" else say "$TAG uitpakken naar $target" rm -rf "$target"; mkdir -p "$target" git -C "$REPO" archive "$TAG" | tar -x -C "$target" say "venv en afhankelijkheden installeren" python3 -m venv "$target/.venv" "$target/.venv/bin/pip" install --quiet --upgrade pip "$target/.venv/bin/pip" install --quiet -r "$target/requirements.txt" gunicorn "$target/.venv/bin/python" -m py_compile "$target"/*.py git -C "$REPO" rev-parse "$TAG^{commit}" > "$target/.installed" fi # --- wisselen --------------------------------------------------------------------- previous="$(current_version)" if [ -f "$DATA/crm.db" ] && [ "$previous" != "$TAG" ]; then backup="$DATA/backups/crm-$(date +%Y%m%d-%H%M%S)-${previous:-onbekend}-naar-$TAG.db" # sqlite3 .backup is veilig bij WAL; anders een gewone kopie if command -v sqlite3 >/dev/null; then sqlite3 "$DATA/crm.db" ".backup '$backup'"; else cp "$DATA/crm.db" "$backup"; fi say "database geback-upt: $backup" fi ln -sfn "$target" "$CRM_HOME/current" say "actief: $TAG${previous:+ (was $previous)}" # --- 3D-engine (configurator) ------------------------------------------------------ if [ -x "$target/tools/configurator-sync.sh" ]; then "$target/tools/configurator-sync.sh" "$(cd "$CONFIGURATOR" 2>/dev/null && pwd || echo "$CONFIGURATOR")" "$target/static/configurator" \ || say "let op: geen configurator in $CONFIGURATOR; de viewer van het gekozen model werkt dan niet" fi start