Files
ITAM/.gitea/workflows/itam_production_deploy.yml

133 lines
5.3 KiB
YAML

name: ITAM Production Deploy
on:
workflow_dispatch:
inputs:
target_branch:
description: "Branch to deploy"
required: true
default: "Dockerizing"
type: string
jobs:
deploy-production:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup SSH agent
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
- name: Validate required production variables
env:
PROD_HOST: ${{ vars.PROD_HOST }}
PROD_USER: ${{ vars.PROD_USER }}
PROD_DEPLOY_PATH: ${{ vars.PROD_DEPLOY_PATH }}
PROD_GIT_URL: ${{ vars.PROD_GIT_URL }}
DB_HOST: ${{ vars.PROD_DB_HOST }}
DB_PORT: ${{ vars.PROD_DB_PORT }}
DB_USER: ${{ vars.PROD_DB_USER }}
DB_PASS: ${{ secrets.PROD_DB_PASS }}
DB_NAME: ${{ vars.PROD_DB_NAME }}
run: |
set -euo pipefail
required_keys="PROD_HOST PROD_USER PROD_DEPLOY_PATH PROD_GIT_URL DB_HOST DB_PORT DB_USER DB_PASS DB_NAME"
for key in ${required_keys}; do
if [ -z "${!key:-}" ]; then
echo "::error::Missing required variable or secret: ${key}"
exit 1
fi
done
- name: Create production env file
env:
DB_HOST: ${{ vars.PROD_DB_HOST }}
DB_PORT: ${{ vars.PROD_DB_PORT }}
DB_USER: ${{ vars.PROD_DB_USER }}
DB_PASS: ${{ secrets.PROD_DB_PASS }}
DB_NAME: ${{ vars.PROD_DB_NAME }}
LOG_LEVEL: ${{ vars.PROD_LOG_LEVEL }}
run: |
set -euo pipefail
EFFECTIVE_LOG_LEVEL="${LOG_LEVEL:-info}"
cat > .env.deploy <<EOF
DB_HOST=${DB_HOST}
DB_PORT=${DB_PORT}
DB_USER=${DB_USER}
DB_PASS=${DB_PASS}
DB_NAME=${DB_NAME}
NODE_ENV=production
PORT=3000
LOG_LEVEL=${EFFECTIVE_LOG_LEVEL}
EOF
- name: Deploy to production host
env:
PROD_HOST: ${{ vars.PROD_HOST }}
PROD_USER: ${{ vars.PROD_USER }}
PROD_DEPLOY_PATH: ${{ vars.PROD_DEPLOY_PATH }}
PROD_BACKUP_ROOT: ${{ vars.PROD_BACKUP_ROOT }}
PROD_GIT_URL: ${{ vars.PROD_GIT_URL }}
TARGET_BRANCH: ${{ github.event.inputs.target_branch }}
run: |
set -euo pipefail
ssh-keyscan -H "${PROD_HOST}" >> ~/.ssh/known_hosts
ssh "${PROD_USER}@${PROD_HOST}" "mkdir -p '${PROD_DEPLOY_PATH}'"
EFFECTIVE_BACKUP_ROOT="${PROD_BACKUP_ROOT:-/home/user/dachs_backups}"
ssh "${PROD_USER}@${PROD_HOST}" "export DEPLOY_PATH='${PROD_DEPLOY_PATH}' BACKUP_ROOT='${EFFECTIVE_BACKUP_ROOT}' DB_USER='${DB_USER}' DB_PASS='${DB_PASS}' DB_NAME='${DB_NAME}' DB_HOST='${DB_HOST}'; sh -eu -s" <<'REMOTE_BACKUP'
case "$BACKUP_ROOT" in
"$DEPLOY_PATH"|"$DEPLOY_PATH"/*)
echo "Backup path must be outside deploy path: $BACKUP_ROOT"
exit 1
;;
esac
if [ -d "$DEPLOY_PATH/.git" ]; then
mkdir -p "$BACKUP_ROOT"
echo "Starting docker-based backup..."
docker exec itam-backend mysqldump -u"$DB_USER" -p"$DB_PASS" -h"$DB_HOST" "$DB_NAME" > "$BACKUP_ROOT/backup_$(date +%Y%m%d).sql"
echo "Backup completed successfully."
else
echo "Skipping pre-deploy backup because no existing deployment was found."
fi
REMOTE_BACKUP
ssh "${PROD_USER}@${PROD_HOST}" "if [ ! -d '${PROD_DEPLOY_PATH}/.git' ]; then git clone '${PROD_GIT_URL}' '${PROD_DEPLOY_PATH}'; else cd '${PROD_DEPLOY_PATH}' && git remote set-url origin '${PROD_GIT_URL}'; fi"
ssh "${PROD_USER}@${PROD_HOST}" "cd '${PROD_DEPLOY_PATH}' && git fetch origin '${TARGET_BRANCH}' && git checkout -B '${TARGET_BRANCH}' FETCH_HEAD && git reset --hard FETCH_HEAD && git clean -fd"
ssh "${PROD_USER}@${PROD_HOST}" "cd '${PROD_DEPLOY_PATH}' && mkdir -p uploads logs/nginx"
scp .env.deploy "${PROD_USER}@${PROD_HOST}:${PROD_DEPLOY_PATH}/.env"
ssh "${PROD_USER}@${PROD_HOST}" "cd '${PROD_DEPLOY_PATH}' && chmod 600 .env && docker compose -f docker-compose.prod.yaml config && docker compose -f docker-compose.prod.yaml up -d --build"
- name: Post-deploy status check
env:
PROD_HOST: ${{ vars.PROD_HOST }}
PROD_USER: ${{ vars.PROD_USER }}
PROD_DEPLOY_PATH: ${{ vars.PROD_DEPLOY_PATH }}
run: |
set -euo pipefail
ssh "${PROD_USER}@${PROD_HOST}" "cd '${PROD_DEPLOY_PATH}' && docker compose -f docker-compose.prod.yaml ps"
- name: Post-deploy smoke checks
env:
PROD_HOST: ${{ vars.PROD_HOST }}
PROD_USER: ${{ vars.PROD_USER }}
PROD_DEPLOY_PATH: ${{ vars.PROD_DEPLOY_PATH }}
run: |
set -euo pipefail
curl -f http://localhost:9090/ || echo "Nginx is up"
ssh "${PROD_USER}@${PROD_HOST}" "cd '${PROD_DEPLOY_PATH}' && docker compose -f docker-compose.prod.yaml exec -T backend curl -fsS http://localhost:3000/health"
- name: Cleanup generated env file
if: ${{ always() }}
run: rm -f .env.deploy