stages: - build - deploy - notify variables: # OVH Container Registry URL (without https://) # Image name and tag (format: registry/project/image) IMAGE_NAME: "easyportal" IMAGE_TAG: "${OVH_REGISTRY_URL}/${IMAGE_NAME}:${CI_COMMIT_REF_SLUG}-${CI_PIPELINE_ID}" IMAGE_LATEST: "${OVH_REGISTRY_URL}/${IMAGE_NAME}:latest" build: stage: build image: docker:20.10.16 rules: - if: '$CI_COMMIT_BRANCH == "develop"' when: always - when: manual before_script: # Use host Docker daemon via socket - docker info # Login to OVH Container Registry - echo "Logging into OVH Container Registry..." - echo "$OVH_REGISTRY_PASSWORD" | docker login -u "$OVH_REGISTRY_USERNAME" "$OVH_REGISTRY_URL" --password-stdin script: # Build the FrankenPHP image - echo "Building Docker image..." - docker build --build-arg APP_ENV=prod --target frankenphp_prod -t $IMAGE_TAG -t $IMAGE_LATEST . # Push both tags to OVH registry - echo "Pushing image to OVH registry..." - docker push $IMAGE_TAG - docker push $IMAGE_LATEST # Display image info - echo "Successfully pushed:" - echo " - $IMAGE_TAG" - echo " - $IMAGE_LATEST" after_script: - docker logout $OVH_REGISTRY_URL || true deploy: stage: deploy image: alpine:latest needs: - build rules: - if: '$CI_COMMIT_BRANCH == "develop"' when: on_success - when: manual before_script: - apk add --no-cache openssh-client - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - echo "Host *\n StrictHostKeyChecking no\n UserKnownHostsFile=/dev/null" > ~/.ssh/config - chmod 600 ~/.ssh/config - ssh-keyscan -H $SERVER_IP >> ~/.ssh/known_hosts || true script: - | ssh $SSH_USER@$SERVER_IP << ENDSSH set -e cd /mnt/external-disk/easy-monitor/easyportal # ===== PRE-DEPLOYMENT DOCKER CLEANUP ===== echo '===== Starting pre-deployment Docker cleanup =====' # Show current Docker space usage echo 'Current Docker space usage:' docker system df # Clean build cache echo 'Cleaning Docker build cache...' docker builder prune -f || true # Clean stopped containers echo 'Cleaning stopped containers...' docker container prune -f || true # Clean unused networks echo 'Cleaning unused networks...' docker network prune -f || true # Show space usage after cleanup echo 'Docker space usage after initial cleanup:' docker system df # Login to OVH registry echo "$OVH_REGISTRY_PASSWORD" | docker login -u "$OVH_REGISTRY_USERNAME" "$OVH_REGISTRY_URL" --password-stdin # Pull latest image echo 'Pulling latest image...' docker pull $IMAGE_LATEST # Clean unused images before deployment echo 'Cleaning unused Docker images...' docker image prune -f || true # Update .env.compose with new image tag echo 'Updating .env.compose with new image...' echo "EASYPORTAL_IMAGE=$IMAGE_LATEST" > .env.compose # Recreate php and messenger containers with new image echo 'Restarting EasyPortal services with new image...' docker compose --env-file .env.compose up -d --force-recreate --no-deps php messenger # Wait for service to be ready sleep 15 # Run migrations echo 'Running database migrations...' docker compose --env-file .env.compose exec -T php php bin/console doctrine:migrations:migrate --no-interaction # Clear cache echo 'Clearing Symfony cache...' docker compose --env-file .env.compose exec -T php php bin/console cache:clear --env=prod --no-debug # ===== POST-DEPLOYMENT CLEANUP ===== echo '===== Final Docker cleanup =====' docker builder prune -f || true docker container prune -f || true # Keep last 3 image versions echo 'Cleaning old image versions (keeping last 3)...' docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedAt}}' | grep -v REPOSITORY | sort -k1,1 -k4,4r | awk ' { repo_tag = \$1":" \$2 if (repo_count[\$1]++ >= 3 && \$2 != "latest" && \$1 != "") { print \$3 } }' | xargs -r docker rmi -f || true echo 'Final Docker space usage:' docker system df echo '===== Deployment completed successfully! =====' ENDSSH after_script: - rm -f ~/.ssh/id_rsa environment: name: production url: https://testportail.solutions-easy.com notify_success: stage: notify image: curlimages/curl:latest needs: - deploy rules: - when: on_success - when: manual allow_failure: true script: - | COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8) DEPLOYMENT_TYPE="Déploiement Beta" if [ "$CI_COMMIT_BRANCH" != "develop" ]; then DEPLOYMENT_TYPE="Déploiement Beta" fi curl -H "Content-Type: application/json" -d "{ \"type\": \"message\", \"attachments\": [ { \"contentType\": \"application/vnd.microsoft.card.adaptive\", \"content\": { \"type\": \"AdaptiveCard\", \"body\": [ { \"type\": \"TextBlock\", \"text\": \"$DEPLOYMENT_TYPE\", \"weight\": \"Bolder\", \"size\": \"Large\" }, { \"type\": \"TextBlock\", \"text\": \"**App:** EasyPortal\", \"wrap\": true }, { \"type\": \"TextBlock\", \"text\": \"**Version:** [$CI_COMMIT_REF_NAME - $COMMIT_SHORT_SHA]($CI_PROJECT_URL/-/commit/$CI_COMMIT_SHA)\", \"wrap\": true, \"markdown\": true }, { \"type\": \"TextBlock\", \"text\": \"**Pipeline:** [Voir le pipeline]($CI_PIPELINE_URL)\", \"wrap\": true, \"markdown\": true }, { \"type\": \"TextBlock\", \"text\": \"**Auteur:** $GITLAB_USER_LOGIN\", \"wrap\": true }, { \"type\": \"TextBlock\", \"text\": \"**Statut:** Succès ✓\", \"wrap\": true, \"color\": \"Good\" } ], \"actions\": [ { \"type\": \"Action.OpenUrl\", \"title\": \"Voir le pipeline\", \"url\": \"$CI_PIPELINE_URL\" }, { \"type\": \"Action.OpenUrl\", \"title\": \"Voir le commit\", \"url\": \"$CI_PROJECT_URL/-/commit/$CI_COMMIT_SHA\" } ], \"\$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\", \"version\": \"1.0\" } } ] }" "$TEAMS_WEBHOOK_URL" notify_failure: stage: notify image: curlimages/curl:latest needs: - deploy rules: - when: on_failure script: - | COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8) DEPLOYMENT_TYPE="Déploiement Beta" if [ "$CI_COMMIT_BRANCH" != "develop" ]; then DEPLOYMENT_TYPE="Déploiement Beta" fi curl -H "Content-Type: application/json" -d "{ \"type\": \"message\", \"attachments\": [ { \"contentType\": \"application/vnd.microsoft.card.adaptive\", \"content\": { \"type\": \"AdaptiveCard\", \"body\": [ { \"type\": \"TextBlock\", \"text\": \"$DEPLOYMENT_TYPE\", \"weight\": \"Bolder\", \"size\": \"Large\" }, { \"type\": \"TextBlock\", \"text\": \"**App:** EasyPortal\", \"wrap\": true }, { \"type\": \"TextBlock\", \"text\": \"**Version:** [$CI_COMMIT_REF_NAME - $COMMIT_SHORT_SHA]($CI_PROJECT_URL/-/commit/$CI_COMMIT_SHA)\", \"wrap\": true, \"markdown\": true }, { \"type\": \"TextBlock\", \"text\": \"**Pipeline:** [Voir le pipeline]($CI_PIPELINE_URL)\", \"wrap\": true, \"markdown\": true }, { \"type\": \"TextBlock\", \"text\": \"**Auteur:** $GITLAB_USER_LOGIN\", \"wrap\": true }, { \"type\": \"TextBlock\", \"text\": \"**Statut:** Échec ✗\", \"wrap\": true, \"color\": \"Attention\" } ], \"actions\": [ { \"type\": \"Action.OpenUrl\", \"title\": \"Voir le pipeline\", \"url\": \"$CI_PIPELINE_URL\" }, { \"type\": \"Action.OpenUrl\", \"title\": \"Voir le commit\", \"url\": \"$CI_PROJECT_URL/-/commit/$CI_COMMIT_SHA\" } ], \"\$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\", \"version\": \"1.0\" } } ] }" "$TEAMS_WEBHOOK_URL"