diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 12ea13be..b3b473c5 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -2,7 +2,7 @@ name: Build & Deploy KLZ Cables on: push: - branches: [main] + branches: [main, staging] jobs: build-and-deploy: @@ -15,6 +15,7 @@ jobs: - name: ๐Ÿ“‹ Log Workflow Start run: | echo "๐Ÿš€ Starting deployment for ${{ github.repository }} (${{ github.ref }})" + echo " โ€ข Branch: ${{ github.ref_name }}" echo " โ€ข Commit: ${{ github.sha }}" echo " โ€ข Timestamp: $(date -u +'%Y-%m-%d %H:%M:%S UTC')" @@ -33,23 +34,48 @@ jobs: # LOGGING: Build Phase # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - name: ๐Ÿ—๏ธ Build Docker image + env: + NEXT_PUBLIC_BASE_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_BASE_URL || (secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }} + NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }} + NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }} run: | - echo "๐Ÿ—๏ธ Building Docker image (linux/arm64)..." + echo "๐Ÿ—๏ธ Building Docker image (linux/arm64) for branch ${{ github.ref_name }}..." docker buildx build \ --pull \ --platform linux/arm64 \ - --build-arg NEXT_PUBLIC_BASE_URL="${{ secrets.NEXT_PUBLIC_BASE_URL }}" \ - --build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}" \ - --build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL="${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }}" \ + --build-arg NEXT_PUBLIC_BASE_URL="$NEXT_PUBLIC_BASE_URL" \ + --build-arg NEXT_PUBLIC_UMAMI_WEBSITE_ID="$NEXT_PUBLIC_UMAMI_WEBSITE_ID" \ + --build-arg NEXT_PUBLIC_UMAMI_SCRIPT_URL="$NEXT_PUBLIC_UMAMI_SCRIPT_URL" \ + -t registry.infra.mintel.me/mintel/klz-cables.com:${{ github.sha }} \ -t registry.infra.mintel.me/mintel/klz-cables.com:latest \ --push . # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• # LOGGING: Deployment Phase # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• - - name: ๐Ÿš€ Deploy to production server + - name: ๐Ÿš€ Deploy to server + env: + NEXT_PUBLIC_BASE_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_BASE_URL || (secrets.STAGING_NEXT_PUBLIC_BASE_URL || secrets.NEXT_PUBLIC_BASE_URL) }} + NEXT_PUBLIC_UMAMI_WEBSITE_ID: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || (secrets.STAGING_NEXT_PUBLIC_UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID) }} + NEXT_PUBLIC_UMAMI_SCRIPT_URL: ${{ github.ref_name == 'main' && secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || (secrets.STAGING_NEXT_PUBLIC_UMAMI_SCRIPT_URL || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL) }} + SENTRY_DSN: ${{ github.ref_name == 'main' && secrets.SENTRY_DSN || (secrets.STAGING_SENTRY_DSN || secrets.SENTRY_DSN) }} + MAIL_HOST: ${{ github.ref_name == 'main' && secrets.MAIL_HOST || (secrets.STAGING_MAIL_HOST || secrets.MAIL_HOST) }} + MAIL_PORT: ${{ github.ref_name == 'main' && secrets.MAIL_PORT || (secrets.STAGING_MAIL_PORT || secrets.MAIL_PORT) }} + MAIL_USERNAME: ${{ github.ref_name == 'main' && secrets.MAIL_USERNAME || (secrets.STAGING_MAIL_USERNAME || secrets.MAIL_USERNAME) }} + MAIL_PASSWORD: ${{ github.ref_name == 'main' && secrets.MAIL_PASSWORD || (secrets.STAGING_MAIL_PASSWORD || secrets.MAIL_PASSWORD) }} + MAIL_FROM: ${{ github.ref_name == 'main' && secrets.MAIL_FROM || (secrets.STAGING_MAIL_FROM || secrets.MAIL_FROM) }} + MAIL_RECIPIENTS: ${{ github.ref_name == 'main' && secrets.MAIL_RECIPIENTS || (secrets.STAGING_MAIL_RECIPIENTS || secrets.MAIL_RECIPIENTS) }} run: | - echo "๐Ÿš€ Deploying to alpha.mintel.me..." + BRANCH=${{ github.ref_name }} + if [ "$BRANCH" = "main" ]; then + ENV_FILE=.env.prod + TRAEFIK_HOST="klz-cables.com, www.klz-cables.com" + else + ENV_FILE=.env.staging + TRAEFIK_HOST="staging.klz-cables.com" + fi + + echo "๐Ÿš€ Deploying branch $BRANCH to $ENV_FILE..." # Setup SSH mkdir -p ~/.ssh @@ -60,60 +86,61 @@ jobs: # Create .env file content cat > /tmp/klz-cables.env << EOF # ============================================================================ - # KLZ Cables - Production Environment Configuration + # KLZ Cables - Environment Configuration ($BRANCH) # ============================================================================ # Auto-generated by CI/CD workflow # DO NOT EDIT MANUALLY - Changes will be overwritten on next deployment # ============================================================================ - # Application NODE_ENV=production - NEXT_PUBLIC_BASE_URL=${{ secrets.NEXT_PUBLIC_BASE_URL }} - - # Analytics (Umami) - NEXT_PUBLIC_UMAMI_WEBSITE_ID=${{ secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID }} - NEXT_PUBLIC_UMAMI_SCRIPT_URL=${{ secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL }} - - # Error Tracking (GlitchTip/Sentry) - SENTRY_DSN=${{ secrets.SENTRY_DSN }} - - # Email Configuration (Mailgun) - MAIL_HOST=${{ secrets.MAIL_HOST }} - MAIL_PORT=${{ secrets.MAIL_PORT }} - MAIL_USERNAME=${{ secrets.MAIL_USERNAME }} - MAIL_PASSWORD=${{ secrets.MAIL_PASSWORD }} - MAIL_FROM=${{ secrets.MAIL_FROM }} - MAIL_RECIPIENTS=${{ secrets.MAIL_RECIPIENTS }} + NEXT_PUBLIC_BASE_URL=$NEXT_PUBLIC_BASE_URL + NEXT_PUBLIC_UMAMI_WEBSITE_ID=$NEXT_PUBLIC_UMAMI_WEBSITE_ID + NEXT_PUBLIC_UMAMI_SCRIPT_URL=$NEXT_PUBLIC_UMAMI_SCRIPT_URL + SENTRY_DSN=$SENTRY_DSN + MAIL_HOST=$MAIL_HOST + MAIL_PORT=$MAIL_PORT + MAIL_USERNAME=$MAIL_USERNAME + MAIL_PASSWORD=$MAIL_PASSWORD + MAIL_FROM=$MAIL_FROM + MAIL_RECIPIENTS=$MAIL_RECIPIENTS + # Deployment variables for docker-compose + IMAGE_TAG=${{ github.sha }} + TRAEFIK_HOST=$TRAEFIK_HOST + ENV_FILE=$ENV_FILE EOF - # Upload .env and deploy - scp -o StrictHostKeyChecking=accept-new /tmp/klz-cables.env root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/.env + # Upload .env and docker-compose.yml + scp -o StrictHostKeyChecking=accept-new /tmp/klz-cables.env root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/$ENV_FILE + scp -o StrictHostKeyChecking=accept-new docker-compose.yml root@alpha.mintel.me:/home/deploy/sites/klz-cables.com/docker-compose.yml ssh -o StrictHostKeyChecking=accept-new root@alpha.mintel.me bash << EOF set -e cd /home/deploy/sites/klz-cables.com - chmod 600 .env - chown deploy:deploy .env + chmod 600 $ENV_FILE + chown deploy:deploy $ENV_FILE echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin - docker pull registry.infra.mintel.me/mintel/klz-cables.com:latest - docker-compose down + echo "๐Ÿ“ฅ Pulling images..." + IMAGE_TAG=${{ github.sha }} ENV_FILE=$ENV_FILE TRAEFIK_HOST="$TRAEFIK_HOST" docker compose --env-file $ENV_FILE pull echo "๐Ÿš€ Starting containers..." - docker-compose up -d + IMAGE_TAG=${{ github.sha }} ENV_FILE=$ENV_FILE TRAEFIK_HOST="$TRAEFIK_HOST" docker compose --env-file $ENV_FILE up -d + + echo "๐Ÿงน Cleaning up old images..." + docker system prune -f echo "โณ Giving the app a few seconds to warm up..." sleep 10 echo "๐Ÿ” Checking container status..." - docker-compose ps + docker compose --env-file $ENV_FILE ps - if ! docker-compose ps | grep -q "Up"; then + if ! docker compose --env-file $ENV_FILE ps | grep -q "Up"; then echo "โŒ Container failed to start" - docker-compose logs --tail=100 + docker compose --env-file $ENV_FILE logs --tail=100 exit 1 fi @@ -130,6 +157,7 @@ jobs: run: | echo "๐Ÿ“Š Status: ${{ job.status }}" echo "๐ŸŽฏ Target: alpha.mintel.me" + echo "๐ŸŒฟ Branch: ${{ github.ref_name }}" # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ• # NOTIFICATION: Gotify @@ -140,7 +168,7 @@ jobs: echo "Sending success notification to Gotify..." RESPONSE=$(curl -k -s -w "\n%{http_code}" -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \ -F "title=โœ… Deployment Success: ${{ github.repository }}" \ - -F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref }}) was successful. + -F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref_name }}) was successful. Commit: ${{ github.sha }} Actor: ${{ github.actor }} @@ -164,7 +192,7 @@ jobs: echo "Sending failure notification to Gotify..." RESPONSE=$(curl -k -s -w "\n%{http_code}" -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \ -F "title=โŒ Deployment Failed: ${{ github.repository }}" \ - -F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref }}) failed! + -F "message=The deployment of ${{ github.repository }} (branch: ${{ github.ref_name }}) failed! Commit: ${{ github.sha }} Actor: ${{ github.actor }} diff --git a/docker-compose.yml b/docker-compose.yml index d8b0f825..39cd00b1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,21 @@ services: app: - image: registry.infra.mintel.me/mintel/klz-cables.com:latest + image: registry.infra.mintel.me/mintel/klz-cables.com:${IMAGE_TAG:-latest} restart: always networks: - infra ports: - "3000:3000" env_file: - - .env + - ${ENV_FILE:-.env} labels: - "traefik.enable=true" # HTTP โ‡’ HTTPS redirect - - "traefik.http.routers.klz-cables-web.rule=(Host(`klz-cables.com`) || Host(`www.klz-cables.com`) || Host(`staging.klz-cables.com`)) && !PathPrefix(`/.well-known/acme-challenge/`)" + - "traefik.http.routers.klz-cables-web.rule=Host(`${TRAEFIK_HOST}`) && !PathPrefix(`/.well-known/acme-challenge/`)" - "traefik.http.routers.klz-cables-web.entrypoints=web" - "traefik.http.routers.klz-cables-web.middlewares=redirect-https" # HTTPS router - - "traefik.http.routers.klz-cables.rule=Host(`klz-cables.com`) || Host(`www.klz-cables.com`) || Host(`staging.klz-cables.com`)" + - "traefik.http.routers.klz-cables.rule=Host(`${TRAEFIK_HOST}`)" - "traefik.http.routers.klz-cables.entrypoints=websecure" - "traefik.http.routers.klz-cables.tls.certresolver=le" - "traefik.http.routers.klz-cables.tls=true"