name: Laravel CI-CD on: push: branches: ["main"] env: REGISTRY: gitea.leonmorival.com IMAGE_NAME: daily_meal/bemeal-api jobs: # ========================= # TESTS # ========================= test: name: Tests Unitaires runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Run Tests uses: docker://laravelsail/php84-composer:latest env: APP_ENV: testing APP_KEY: base64:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= DB_CONNECTION: sqlite DB_DATABASE: ":memory:" with: args: > bash -c "composer install --no-interaction --ignore-platform-req=ext-intl && php artisan test" # ========================= # BUILD & PUSH # ========================= build: name: Build & Push Docker needs: test runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Login to Gitea Registry run: | echo "${{ secrets.TOKEN_GITEA }}" \ | docker login $REGISTRY \ -u "${{ github.actor }}" \ --password-stdin - name: Build & Push image run: | IMAGE=$REGISTRY/$IMAGE_NAME docker build -t "$IMAGE:${{ github.sha }}" -t "$IMAGE:latest" . docker push "$IMAGE:${{ github.sha }}" docker push "$IMAGE:latest" # ========================= # DEPLOY BLUE / GREEN + SWITCH NGINX # ========================= deploy: name: Deploy Blue/Green needs: build runs-on: ubuntu-latest steps: - name: Checkout deploy files uses: actions/checkout@v4 - name: Upload docker-compose uses: appleboy/scp-action@v0.1.7 with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} source: docker-compose.prod.yml target: ${{ secrets.DEPLOY_PATH || '/opt/bemeal' }} - name: Deploy via SSH uses: appleboy/ssh-action@v1.0.3 env: IMAGE_TAG: ${{ github.sha }} REGISTRY: ${{ env.REGISTRY }} IMAGE_NAME: ${{ env.IMAGE_NAME }} DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} key: ${{ secrets.SSH_PRIVATE_KEY }} envs: REGISTRY,IMAGE_NAME,IMAGE_TAG,DEPLOY_PATH script: | set -eu APP_DIR="${DEPLOY_PATH:-/opt/bemeal}" COMPOSE_FILE="docker-compose.prod.yml" NGINX_CONTAINER="nginx" cd "$APP_DIR" COMPOSE="docker compose --env-file .env.prod -f $COMPOSE_FILE" IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" echo "Pull image: $IMAGE" docker pull "$IMAGE" echo "Start shared services" $COMPOSE up -d pgsql redis meilisearch # ========================= # BLUE / GREEN DETECTION # ========================= ACTIVE="" if docker ps --format '{{.Names}}' | grep -q "bemeal-api-blue"; then if [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-blue 2>/dev/null || true)" = "healthy" ]; then ACTIVE="blue" fi fi if docker ps --format '{{.Names}}' | grep -q "bemeal-api-green"; then if [ "$(docker inspect -f '{{.State.Health.Status}}' bemeal-api-green 2>/dev/null || true)" = "healthy" ]; then ACTIVE="green" fi fi if [ "$ACTIVE" = "blue" ]; then TARGET="green" else TARGET="blue" fi TARGET_SERVICE="bemeal-api-$TARGET" echo "Deploying to $TARGET_SERVICE" IMAGE="$REGISTRY/$IMAGE_NAME:$IMAGE_TAG" \ $COMPOSE up -d --force-recreate --no-deps "$TARGET_SERVICE" # ========================= # HEALTHCHECK # ========================= echo "Waiting for healthcheck..." for i in $(seq 1 60); do STATUS="$(docker inspect -f '{{.State.Health.Status}}' "$TARGET_SERVICE" 2>/dev/null || true)" if [ "$STATUS" = "healthy" ]; then break fi sleep 2 done if [ "$STATUS" != "healthy" ]; then echo "Healthcheck failed" docker logs --tail=200 "$TARGET_SERVICE" || true exit 1 fi docker exec "$TARGET_SERVICE" wget -qO- http://127.0.0.1/api/health >/dev/null || exit 1 echo "API healthy → switching Nginx" # ========================= # NGINX SWITCH AUTOMATIQUE # ========================= if [ "$TARGET" = "blue" ]; then echo "server bemeal-api-blue:80;" > /opt/bemeal/nginx/upstream.conf else echo "server bemeal-api-green:80;" > /opt/bemeal/nginx/upstream.conf fi docker exec "$NGINX_CONTAINER" nginx -s reload echo "Deployment successful" # ========================= # CLEAN OLD CONTAINER # ========================= if [ -n "$ACTIVE" ] && [ "$ACTIVE" != "$TARGET" ]; then echo "Stopping old $ACTIVE" $COMPOSE stop "bemeal-api-$ACTIVE" || true $COMPOSE rm -f "bemeal-api-$ACTIVE" || true fi