Files

196 lines
6.4 KiB
YAML

name: Deploy to Production
on:
workflow_run:
workflows: ["Build Application Image"]
types:
- completed
branches:
- main
- production
workflow_dispatch:
inputs:
image_tag:
description: 'Image tag to deploy'
required: true
default: 'latest'
env:
IMAGE_NAME: jd-book-uploader
IMAGE_TAG: ${{ inputs.image_tag || 'latest' }}
REGISTRY: ${{ secrets.REGISTRY_URL || '' }}
jobs:
deploy:
name: Deploy to Production
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up SSH
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Configure SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_KNOWN_HOSTS }}" >> ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
- name: Download image artifact
if: env.REGISTRY == ''
uses: actions/download-artifact@v4
with:
name: docker-image
workflow: build.yml
run-id: ${{ github.event.workflow_run.id }}
- name: Prepare deployment files
run: |
mkdir -p deployment/tmp
# Create .env.production
cat > deployment/tmp/.env.production << EOF
PORT=${{ secrets.PORT || '8080' }}
FRONTEND_URL=${{ secrets.FRONTEND_URL }}
DB_HOST=${{ secrets.DB_HOST }}
DB_PORT=${{ secrets.DB_PORT }}
DB_USER=${{ secrets.DB_USER }}
DB_PASSWORD=${{ secrets.DB_PASSWORD }}
DB_NAME=${{ secrets.DB_NAME }}
FIREBASE_PROJECT_ID=${{ secrets.FIREBASE_PROJECT_ID }}
FIREBASE_STORAGE_BUCKET=${{ secrets.FIREBASE_STORAGE_BUCKET }}
FIREBASE_CREDENTIALS_FILE=${{ secrets.FIREBASE_CREDENTIALS_FILE_PATH || './firebase-credentials.json' }}
EOF
# Create deployment script
cat > deployment/tmp/deploy.sh << 'DEPLOY_SCRIPT'
#!/bin/bash
set -e
IMAGE_NAME="${{ env.IMAGE_NAME }}"
IMAGE_TAG="${{ env.IMAGE_TAG }}"
CONTAINER_NAME="jd-book-uploader"
set -a
source .env.production
set +a
# Stop existing container
if podman ps -a --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
podman stop "${CONTAINER_NAME}" 2>/dev/null || true
podman rm "${CONTAINER_NAME}" 2>/dev/null || true
fi
# Load image if artifact provided
if [ -f image.tar ]; then
podman load -i image.tar
rm -f image.tar
fi
# Pull from registry if configured
if [ -n "${{ env.REGISTRY }}" ]; then
podman pull --tls-verify=false "${{ env.REGISTRY }}/${IMAGE_NAME}:${IMAGE_TAG}"
podman tag "${{ env.REGISTRY }}/${IMAGE_NAME}:${IMAGE_TAG}" "${IMAGE_NAME}:${IMAGE_TAG}"
fi
# Build run command
PODMAN_CMD=(
podman run -d
--name "${CONTAINER_NAME}"
--network=host
--user root
--restart=unless-stopped
)
# Add environment variables
while IFS='=' read -r key value; do
[[ "$key" =~ ^#.*$ ]] && continue
[[ -z "$key" ]] && continue
value=$(echo "$value" | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//")
if [ "$key" != "FIREBASE_CREDENTIALS_FILE" ]; then
PODMAN_CMD+=(-e "${key}=${value}")
fi
done < .env.production
# Mount Firebase credentials
FIREBASE_CREDS="${FIREBASE_CREDENTIALS_FILE}"
if [ -f "$FIREBASE_CREDS" ]; then
PODMAN_CMD+=(-v "${FIREBASE_CREDS}:/app/firebase-credentials.json:ro,z")
PODMAN_CMD+=(-e "FIREBASE_CREDENTIALS_FILE=/app/firebase-credentials.json")
fi
PODMAN_CMD+=("${IMAGE_NAME}:${IMAGE_TAG}")
"${PODMAN_CMD[@]}"
sleep 3
if podman ps --format "{{.Names}}" | grep -q "^${CONTAINER_NAME}$"; then
echo "✓ Container started"
podman logs "${CONTAINER_NAME}" --tail 20
else
echo "✗ Container failed"
podman logs "${CONTAINER_NAME}" --tail 50
exit 1
fi
DEPLOY_SCRIPT
chmod +x deployment/tmp/deploy.sh
- name: Transfer files
run: |
# Ensure remote deployment directory exists
ssh ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} "mkdir -p ${{ secrets.DEPLOY_PATH }}/deployment"
# Copy files explicitly — glob (*) skips dotfiles like .env.production
scp deployment/tmp/deploy.sh ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/deployment/
scp deployment/tmp/.env.production ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/deployment/
if [ -f image.tar ]; then
scp image.tar ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/image.tar
fi
- name: Deploy
run: |
ssh -T ${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} << ENDSSH
set -e
cd ${{ secrets.DEPLOY_PATH }}
if [ -f image.tar ]; then
podman load -i image.tar
rm -f image.tar
fi
if [ ! -f "${{ secrets.FIREBASE_CREDENTIALS_FILE_PATH || './firebase-credentials.json' }}" ]; then
echo "Error: Firebase credentials not found"
exit 1
fi
if [ -n "${{ env.REGISTRY }}" ]; then
echo "${{ secrets.REGISTRY_PASSWORD }}" | podman login "${{ env.REGISTRY }}" -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin --tls-verify=false
fi
cd deployment
./deploy.sh
ENDSSH
- name: Verify deployment
run: |
sleep 5
HEALTH_URL="http://${{ secrets.DEPLOY_HOST }}:${{ secrets.PORT || '8080' }}/api/health"
for i in {1..10}; do
if curl -f -s "$HEALTH_URL" > /dev/null; then
echo "✓ Health check passed"
curl -s "$HEALTH_URL"
exit 0
fi
sleep 3
done
echo "✗ Health check failed"
exit 1