v0.0.2: pre-productie (Bootstrap 5.3.3, LightPdf klant-PDF, server-setup en release-script)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Machiel
2026-10-15 11:58:52 +02:00
co-authored by Claude Opus 5.5
parent bffa053c05
commit 7d302dc405
48 changed files with 2769 additions and 698 deletions
Executable
+105
View File
@@ -0,0 +1,105 @@
#!/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 <tag>` (precies wat getagd is, niet de werkkopie) in
# $CRM_HOME/releases/<tag> 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