name: Deploy Production (qr.crewsportswear.app) on: push: branches: - main - master workflow_dispatch: jobs: deploy: runs-on: ubuntu-latest container: image: catthehacker/ubuntu:act-latest steps: - name: Checkout code shell: sh run: | git clone $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git /workspace/repo cd /workspace/repo git checkout $GITHUB_REF_NAME - name: Build Docker image shell: sh run: | cd /workspace/repo docker build -t qr-code-api:latest . docker save qr-code-api:latest | gzip > qr-code-api.tar.gz - name: Setup SSH shell: sh env: DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} run: | mkdir -p ~/.ssh chmod 700 ~/.ssh echo "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts - name: Upload image and compose shell: sh env: DEPLOY_USER: ${{ secrets.DEPLOY_USER }} DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} run: | scp -i ~/.ssh/id_ed25519 \ /workspace/repo/qr-code-api.tar.gz \ /workspace/repo/docker-compose.prod.yml \ ${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/ - name: Deploy on server shell: sh env: DEPLOY_USER: ${{ secrets.DEPLOY_USER }} DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} run: | ssh -i ~/.ssh/id_ed25519 $DEPLOY_USER@$DEPLOY_HOST << 'EOF' set -e DEPLOY_DIR="/var/www/apps/qr-code-api" sudo mkdir -p "$DEPLOY_DIR" sudo chown $USER:$USER "$DEPLOY_DIR" echo "Loading image" docker load < /tmp/qr-code-api.tar.gz echo "Removing old qr-code-api images" docker images | grep qr-code-api | grep -v "$(docker images qr-code-api:latest -q)" | awk '{print $3}' | xargs -r docker rmi -f || true echo "Updating compose file" cp /tmp/docker-compose.prod.yml "$DEPLOY_DIR/docker-compose.yml" cd "$DEPLOY_DIR" echo "Checking .env file" if [ ! -f .env ]; then echo ".env file not found at $DEPLOY_DIR/.env" echo "Please create it first with optional/custom variables such as:" echo " - TRUST_PROXY" echo " - ALLOWED_QR_IPS" exit 1 fi echo "Fixing .env permissions" sudo chown $USER:$USER .env sudo chmod 600 .env echo "Ensure networks" docker network inspect traefik-public >/dev/null 2>&1 || \ docker network create traefik-public docker network inspect crew-app-net >/dev/null 2>&1 || \ docker network create crew-app-net echo "Restarting service" docker compose down || true docker compose up -d --remove-orphans echo "Waiting for container startup" sleep 10 if docker ps --format '{{.Names}}' | grep -q '^qr_code_api$'; then echo "Service container is running" else echo "Service container is not running" docker compose logs exit 1 fi echo "Cleanup temporary artifacts" rm -f /tmp/qr-code-api.tar.gz /tmp/docker-compose.prod.yml echo "Deployment complete" echo "Application URL: https://qr.crewsportswear.app" EOF - name: Health check shell: sh run: | echo "Waiting for service to be ready" MAX_ATTEMPTS=12 ATTEMPT=1 while [ "$ATTEMPT" -le "$MAX_ATTEMPTS" ]; do HTTP_CODE=$(curl -k -sS -o /dev/null -w "%{http_code}" --max-time 30 https://qr.crewsportswear.app/health || true) if [ -z "$HTTP_CODE" ]; then HTTP_CODE="000" fi if [ "$HTTP_CODE" = "200" ]; then echo "Health check passed (HTTP $HTTP_CODE)" exit 0 fi echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: health check not ready yet (HTTP $HTTP_CODE)" ATTEMPT=$((ATTEMPT + 1)) sleep 10 done echo "Health check failed after $MAX_ATTEMPTS attempts" exit 1