ci(deploy): add migration control and deployment improvements
- 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:
+1
-1
@@ -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"]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,23 +28,94 @@ 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 ;;
|
||||
--flutter) BUILD_FLUTTER=true; BUILD_ALL=false ;;
|
||||
--importer) BUILD_IMPORTER=true; BUILD_ALL=false ;;
|
||||
--seed) RUN_SEED=true ;;
|
||||
--migrate) RUN_MIGRATE=true; BUILD_BACKEND=true; BUILD_ALL=false ;;
|
||||
--clean-database) RUN_CLEAN_DATABASE=true; BUILD_BACKEND=true; BUILD_ALL=false ;;
|
||||
--pull-always) PULL_IMAGES=true ;;
|
||||
--backend) BUILD_BACKEND=true; BUILD_ALL=false ;;
|
||||
--flutter) BUILD_FLUTTER=true; BUILD_ALL=false ;;
|
||||
--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)..."
|
||||
(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
|
||||
if [ -d "$SCRIPT_DIR/../microservice-importer/.git" ]; then
|
||||
info "Hämtar senaste kod (microservice-importer)..."
|
||||
(cd "$SCRIPT_DIR/../microservice-importer" && git pull origin main)
|
||||
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."
|
||||
|
||||
Reference in New Issue
Block a user