ci(deploy): add migration control and deployment improvements
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m11s
Test Suite / flutter-quality (push) Failing after 1m20s

- Add SKIP_MIGRATION environment variable to control automatic Prisma migrations in backend
- Update Dockerfile to conditionally run migrations based on SKIP_MIGRATION flag
- Enhance deploy.sh with:
  - Better error handling using set -euo pipefail
  - New --skip-migration flag to bypass automatic migration startup
  - Improved documentation and help text
  - New helper functions for waiting on services and database
  - Better status reporting for migration results
- Update compose.yml to include SKIP_MIGRATION environment variable
This commit is contained in:
Nils-Johan Gynther
2026-05-21 14:13:54 +02:00
parent 67c3170067
commit 0ebb39150f
3 changed files with 155 additions and 74 deletions
+1 -1
View File
@@ -31,4 +31,4 @@ COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/dist ./dist
EXPOSE 8080
CMD ["sh", "-c", "until ./node_modules/.bin/prisma migrate deploy; do echo 'Migration failed, retrying in 5s...'; sleep 5; done && node dist/main"]
CMD ["sh", "-c", "if [ \"${SKIP_MIGRATION:-false}\" != \"true\" ]; then echo 'Running automatic Prisma migration...'; until ./node_modules/.bin/prisma migrate deploy --schema prisma/schema.prisma; do echo 'Migration failed, retrying in 5s...'; sleep 5; done; else echo 'Skipping automatic Prisma migration (SKIP_MIGRATION=true).'; fi && node dist/main"]
+1
View File
@@ -23,6 +23,7 @@ services:
IMPORTER_SERVICE_URL: "http://importer-api:3001"
RECEIPT_TRACE_DECISIONS: "${RECEIPT_TRACE_DECISIONS:-0}"
PRISMA_LOG_QUERIES: "${PRISMA_LOG_QUERIES:-0}"
SKIP_MIGRATION: "${SKIP_MIGRATION:-false}"
volumes:
- recipe_images:/app/recipe-images
depends_on:
+145 -65
View File
@@ -9,15 +9,17 @@
# ./deploy.sh --flutter bygg bara flutter web-app
# ./deploy.sh --importer bygg bara importer-microservice
# ./deploy.sh --seed kör full seed på databasen (opt-in)
# ./deploy.sh --migrate kör Prisma-migrationer (opt-in)
# ./deploy.sh --migrate kör Prisma-migrationer explicit (opt-in)
# ./deploy.sh --skip-migration hoppa över automatisk startup-migrering i recipe-api
# ./deploy.sh --clean-database kör underhålls-SQL som rensar data men behåller kategorier
# ./deploy.sh --pull-always kontrollera uppdateringar för basimages (flutter:3.41.9, node:24.15.0 etc)
# ./deploy.sh --pull-always kontrollera uppdateringar för basimages
# ./deploy.sh --backend --seed kombinera flaggor fritt (git pull körs alltid)
set -e
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
START_TS="$(date +%s)"
# ── Flaggor ──────────────────────────────────────────────────────────────────
BUILD_BACKEND=false
@@ -26,9 +28,79 @@ BUILD_IMPORTER=false
RUN_SEED=false
RUN_MIGRATE=false
RUN_CLEAN_DATABASE=false
SKIP_MIGRATION=false
PULL_IMAGES=false # --pull=false är standard (snabbt)
BUILD_ALL=true # om inga specifika tjänster anges, bygg allt
# ── Hjälpfunktioner ───────────────────────────────────────────────────────────
info() { echo "[INFO] $*"; }
warn() { echo "[WARN] $*"; }
fatal() { echo "[ERROR] $*"; exit 1; }
require_cmd() {
command -v "$1" >/dev/null 2>&1 || fatal "Kommando saknas: $1"
}
read_env_value() {
local key="$1"
local line
line=$(grep -E "^${key}=" .env | tail -n 1 || true)
line="${line#*=}"
line="${line%\"}"
line="${line#\"}"
line="${line%\'}"
line="${line#\'}"
printf '%s' "$line"
}
wait_for_backend_prisma() {
info "Väntar på att backend är redo för Prisma-kommandon..."
for i in $(seq 1 30); do
if docker exec recipe-api sh -lc "test -f /app/prisma/schema.prisma" >/dev/null 2>&1; then
return 0
fi
info " ...försök $i/30"
sleep 2
done
return 1
}
wait_for_db() {
local root_password="$1"
info "Väntar på att databasen är redo..."
for i in $(seq 1 30); do
if docker exec recipe-db mariadb-admin ping -h 127.0.0.1 -uroot -p"$root_password" --silent 2>/dev/null; then
return 0
fi
info " ...försök $i/30"
sleep 2
done
return 1
}
run_prisma_migrate_deploy() {
local output
info "Kör Prisma-migrationer (deploy)..."
info " ▶ Kör: npx prisma migrate deploy"
if ! output=$(docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma" 2>&1); then
echo "$output"
fatal "Prisma migration misslyckades."
fi
echo "$output"
if echo "$output" | grep -qi "No pending migrations"; then
info "Migration-status: inga väntande migrationer."
elif echo "$output" | grep -qi "Applying migration"; then
info "Migration-status: minst en migration applicerades."
else
warn "Migration-status: kunde inte avgöra om nya migrationer applicerades."
fi
info "Migrationer slutförda utan fel."
}
for arg in "$@"; do
case "$arg" in
--backend) BUILD_BACKEND=true; BUILD_ALL=false ;;
@@ -36,13 +108,14 @@ for arg in "$@"; do
--importer) BUILD_IMPORTER=true; BUILD_ALL=false ;;
--seed) RUN_SEED=true ;;
--migrate) RUN_MIGRATE=true; BUILD_BACKEND=true; BUILD_ALL=false ;;
--skip-migration) SKIP_MIGRATION=true ;;
--clean-database) RUN_CLEAN_DATABASE=true; BUILD_BACKEND=true; BUILD_ALL=false ;;
--pull-always) PULL_IMAGES=true ;;
--help|-h)
sed -n '/^# Användning:/,/^[^#]/p' "$0" | grep '^#' | sed 's/^# \?//'
exit 0
;;
*) echo "Okänd flagga: $arg (--help för hjälp)"; exit 1 ;;
*) fatal "Okänd flagga: $arg (--help för hjälp)" ;;
esac
done
@@ -52,99 +125,106 @@ if [ "$BUILD_ALL" = true ]; then
BUILD_IMPORTER=true
fi
# ── Validering ────────────────────────────────────────────────────────────────
if [ ! -f ".env" ]; then
echo "Fel: .env saknas. Kopiera .env.example och fyll i värdena:"
echo " cp .env.example .env && nano .env"
exit 1
# Om explicit migration begärs, stäng av automigrering i containern för att undvika dubbelkörning.
if [ "$RUN_MIGRATE" = true ]; then
SKIP_MIGRATION=true
fi
# ── Validering ────────────────────────────────────────────────────────────────
[ -f ".env" ] || fatal ".env saknas. Kör: cp .env.example .env && nano .env"
require_cmd git
require_cmd docker
if [ "$BUILD_BACKEND" = true ] || [ "$RUN_SEED" = true ] || [ "$RUN_CLEAN_DATABASE" = true ] || [ "$RUN_MIGRATE" = true ]; then
require_cmd grep
fi
export SKIP_MIGRATION
COMPOSE_CMD=(docker compose -f compose.yml -f compose.flutter.yml)
# ── Git pull ──────────────────────────────────────────────────────────────────
echo "Hämtar senaste kod (recipe-app)..."
info "Hämtar senaste kod (recipe-app)..."
git pull origin main
echo "Hämtar senaste kod (microservice-importer)..."
if [ -d "$SCRIPT_DIR/../microservice-importer/.git" ]; then
info "Hämtar senaste kod (microservice-importer)..."
(cd "$SCRIPT_DIR/../microservice-importer" && git pull origin main)
# ── Bygger valda tjänster ─────────────────────────────────────────────────────
COMPOSE="docker compose -f compose.yml -f compose.flutter.yml"
SERVICES=""
[ "$BUILD_BACKEND" = true ] && SERVICES="$SERVICES recipe-api"
[ "$BUILD_FLUTTER" = true ] && SERVICES="$SERVICES recipe-flutter"
[ "$BUILD_IMPORTER" = true ] && SERVICES="$SERVICES importer-api"
echo "Bygger: ${SERVICES:-alla tjänster}..."
if [ "$PULL_IMAGES" = true ]; then
# Kontrollera om nya versioner av basimages finns på Docker Hub / ghcr.io
echo " (kontrollerar uppdateringar för basimages...)"
$COMPOSE build $SERVICES
else
# Standard: använd lokala cachade images, snabbare
$COMPOSE build --pull=false $SERVICES
warn "microservice-importer repo hittades inte på förväntad path, hoppar över git pull där."
fi
echo "Startar tjänster..."
$COMPOSE up -d
# ── Bygger valda tjänster ─────────────────────────────────────────────────────
SERVICES=()
[ "$BUILD_BACKEND" = true ] && SERVICES+=(recipe-api)
[ "$BUILD_FLUTTER" = true ] && SERVICES+=(recipe-flutter)
[ "$BUILD_IMPORTER" = true ] && SERVICES+=(importer-api)
# ── Prisma migreringar och databasrensning (opt-in) ───────────────────────────
if [ "${#SERVICES[@]}" -eq 0 ]; then
info "Bygger: alla tjänster..."
else
info "Bygger: ${SERVICES[*]}"
fi
if [ "$PULL_IMAGES" = true ]; then
info "(kontrollerar uppdateringar för basimages...)"
"${COMPOSE_CMD[@]}" build "${SERVICES[@]}"
else
"${COMPOSE_CMD[@]}" build --pull=false "${SERVICES[@]}"
fi
info "Startar tjänster..."
"${COMPOSE_CMD[@]}" up -d
# ── Prisma migreringar och databasrensning (opt-in) ─────────────────────────
if [ "$RUN_MIGRATE" = true ] || [ "$RUN_CLEAN_DATABASE" = true ]; then
CLEAN_SQL_FILE="backend/prisma/maintenance/clean-database.sql"
echo "Väntar på att backend är redo för Prisma-kommandon..."
for i in $(seq 1 30); do
if docker exec recipe-api sh -lc "test -f /app/prisma/schema.prisma" >/dev/null 2>&1; then
break
fi
echo " ...försök $i/30"
sleep 2
done
wait_for_backend_prisma || fatal "Backend blev inte redo för Prisma-kommandon i tid."
if [ "$RUN_MIGRATE" = true ]; then
echo "Kör Prisma-migrationer (deploy)..."
docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma"
run_prisma_migrate_deploy
fi
if [ "$RUN_CLEAN_DATABASE" = true ]; then
if [ ! -f "$CLEAN_SQL_FILE" ]; then
echo "Fel: saknar $CLEAN_SQL_FILE"
exit 1
fi
[ -f "$CLEAN_SQL_FILE" ] || fatal "Saknar $CLEAN_SQL_FILE"
MARIADB_ROOT_PASSWORD=$(grep MARIADB_ROOT_PASSWORD .env | cut -d '=' -f2 | tr -d '"' | tr -d "'")
MARIADB_DATABASE=$(grep MARIADB_DATABASE .env | cut -d '=' -f2 | tr -d '"' | tr -d "'")
MARIADB_ROOT_PASSWORD="$(read_env_value MARIADB_ROOT_PASSWORD)"
MARIADB_DATABASE="$(read_env_value MARIADB_DATABASE)"
echo "Kör databasrensning från $CLEAN_SQL_FILE ..."
[ -n "$MARIADB_ROOT_PASSWORD" ] || fatal "MARIADB_ROOT_PASSWORD saknas i .env"
[ -n "$MARIADB_DATABASE" ] || fatal "MARIADB_DATABASE saknas i .env"
info "Kör databasrensning från $CLEAN_SQL_FILE ..."
docker exec -i recipe-db mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" "$MARIADB_DATABASE" < "$CLEAN_SQL_FILE"
echo "Databasrensning klar (kategorier bevarade enligt SQL-filen)."
info "Databasrensning klar (kategorier bevarade enligt SQL-filen)."
fi
echo "Uppdaterar Prisma Client..."
info "Uppdaterar Prisma Client..."
docker exec recipe-api sh -lc "cd /app && npx prisma generate --schema prisma/schema.prisma"
fi
# ── Seed (opt-in) ─────────────────────────────────────────────────────────────
if [ "$RUN_SEED" = true ]; then
MARIADB_ROOT_PASSWORD=$(grep MARIADB_ROOT_PASSWORD .env | cut -d '=' -f2 | tr -d '"' | tr -d "'")
MARIADB_DATABASE=$(grep MARIADB_DATABASE .env | cut -d '=' -f2 | tr -d '"' | tr -d "'")
MARIADB_ROOT_PASSWORD="$(read_env_value MARIADB_ROOT_PASSWORD)"
MARIADB_DATABASE="$(read_env_value MARIADB_DATABASE)"
echo "Väntar på att databasen är redo..."
for i in $(seq 1 30); do
if docker exec recipe-db mariadb-admin ping -h 127.0.0.1 -uroot -p"$MARIADB_ROOT_PASSWORD" --silent 2>/dev/null; then
break
fi
echo " ...försök $i/30"
sleep 2
done
[ -n "$MARIADB_ROOT_PASSWORD" ] || fatal "MARIADB_ROOT_PASSWORD saknas i .env"
[ -n "$MARIADB_DATABASE" ] || fatal "MARIADB_DATABASE saknas i .env"
wait_for_db "$MARIADB_ROOT_PASSWORD" || fatal "Databasen blev inte redo i tid."
if [ -f "db/seeds/seed_all.sql" ]; then
docker exec -i recipe-db mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" "$MARIADB_DATABASE" \
< db/seeds/seed_all.sql
echo "Full seed klar."
docker exec -i recipe-db mariadb -uroot -p"$MARIADB_ROOT_PASSWORD" "$MARIADB_DATABASE" < db/seeds/seed_all.sql
info "Full seed klar."
else
echo "Ingen db/seeds/seed_all.sql hittades — hoppar över seed."
warn "Ingen db/seeds/seed_all.sql hittades — hoppar över seed."
fi
fi
echo "Status:"
$COMPOSE ps
info "Status:"
"${COMPOSE_CMD[@]}" ps
END_TS="$(date +%s)"
DURATION="$((END_TS - START_TS))"
info "Deploy klart på ${DURATION}s."