stages: - build - migrate - deploy - seed default: tags: - self-hosted-prod workflow: rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' when: always - when: never variables: DOCKER_BUILDKIT: "1" IMAGE_TAG: "production_${CI_COMMIT_SHORT_SHA}" IMAGE_NAME: "${CI_REGISTRY_IMAGE}:${IMAGE_TAG}" IMAGE_LATEST: "${CI_REGISTRY_IMAGE}:production_latest" DEPLOY_DIR: "/opt/deploy/lti" COMPOSE_FILE: "docker-compose.yaml" # ========================= # BUILD (AUTO) # ========================= build_production: stage: build rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' script: | set -e docker info echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" echo "✅ Build image: $IMAGE_NAME" docker build -t "$IMAGE_NAME" -f Dockerfile . echo "✅ Push image: $IMAGE_NAME" docker push "$IMAGE_NAME" echo "✅ Tag latest: $IMAGE_LATEST" docker tag "$IMAGE_NAME" "$IMAGE_LATEST" docker push "$IMAGE_LATEST" # ========================= # MIGRATE (PRODUCTION - MANUAL) # ========================= migrate_production: stage: migrate rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' allow_failure: false needs: - job: build_production artifacts: false script: | set -e cd /opt/deploy/lti test -f .env || (echo "❌ .env not found" && exit 1) set -a . ./.env set +a # Validasi env wajib : "${DB_HOST:?DB_HOST not set}" : "${DB_PORT:?DB_PORT not set}" : "${DB_USER:?DB_USER not set}" : "${DB_PASSWORD:?DB_PASSWORD not set}" : "${DB_NAME:?DB_NAME not set}" DB_SSLMODE="${DB_SSLMODE:-require}" export DATABASE_URL="postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=${DB_SSLMODE}" echo "✅ Running migrations (production)..." docker run --rm \ -v "/opt/deploy/lti/internal/database/migrations:/migrations:ro" \ migrate/migrate:v4.15.2 \ -path=/migrations -database "$DATABASE_URL" up # ========================= # DEPLOY (AUTO) # ========================= deploy_production: stage: deploy rules: - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "production"' needs: # - job: migrate_production # artifacts: false - job: build_production artifacts: false script: | set -e docker info echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" cd "$DEPLOY_DIR" test -f "$COMPOSE_FILE" || (echo "❌ $COMPOSE_FILE not found in $DEPLOY_DIR" && exit 1) test -f .env || (echo "❌ .env not found in $DEPLOY_DIR" && exit 1) docker compose -f "$COMPOSE_FILE" pull docker compose -f "$COMPOSE_FILE" up -d --force-recreate docker image prune -f # ========================= # SEED (MANUAL) # ========================= seed_production: stage: seed rules: - if: '$CI_COMMIT_BRANCH == "production"' when: manual script: | set -e cd /opt/deploy/lti test -f .env || (echo "❌ .env not found" && exit 1) echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" docker compose --env-file .env pull seed docker compose --env-file .env run --rm seed