diff --git a/.gitea/workflows/itam_production_deploy.yml b/.gitea/workflows/itam_production_deploy.yml index 3529cb9..e66532d 100644 --- a/.gitea/workflows/itam_production_deploy.yml +++ b/.gitea/workflows/itam_production_deploy.yml @@ -71,6 +71,11 @@ jobs: PROD_DEPLOY_PATH: ${{ vars.PROD_DEPLOY_PATH }} PROD_BACKUP_ROOT: ${{ vars.PROD_BACKUP_ROOT }} 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 }} TARGET_BRANCH: ${{ github.event.inputs.target_branch }} run: | set -euo pipefail @@ -80,7 +85,7 @@ jobs: 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' + ssh "${PROD_USER}@${PROD_HOST}" "export DEPLOY_PATH='${PROD_DEPLOY_PATH}' BACKUP_ROOT='${EFFECTIVE_BACKUP_ROOT}'; sh -eu -s" <<'REMOTE_BACKUP' case "$BACKUP_ROOT" in "$DEPLOY_PATH"|"$DEPLOY_PATH"/*) echo "Backup path must be outside deploy path: $BACKUP_ROOT" @@ -90,10 +95,13 @@ jobs: 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." + echo "Starting pre-deploy backup..." + cd "$DEPLOY_PATH" + if [ -f Makefile ] && [ -f scripts/backup.sh ]; then + make predeploy-backup ENV_FILE=.env BACKUP_ROOT="$BACKUP_ROOT" + else + echo "Skipping pre-deploy backup because current deployed revision does not contain backup tooling." + fi else echo "Skipping pre-deploy backup because no existing deployment was found." fi @@ -125,6 +133,9 @@ jobs: PROD_DEPLOY_PATH: ${{ vars.PROD_DEPLOY_PATH }} run: | set -euo pipefail + ssh "${PROD_USER}@${PROD_HOST}" "curl -fsS http://localhost:9090/health" 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 diff --git a/Makefile b/Makefile index d08a69a..b781027 100644 --- a/Makefile +++ b/Makefile @@ -26,8 +26,8 @@ files-backup: full-backup: @ENV_FILE="$(ENV_FILE)" BACKUP_ROOT="$(BACKUP_ROOT)" sh "$(BACKUP_SCRIPT)" full -# predeploy-backup: -# @ENV_FILE="$(ENV_FILE)" BACKUP_ROOT="$(BACKUP_ROOT)" sh "$(BACKUP_SCRIPT)" full +predeploy-backup: + @ENV_FILE="$(ENV_FILE)" BACKUP_ROOT="$(BACKUP_ROOT)" sh "$(BACKUP_SCRIPT)" full cleanup-backups: @BACKUP_ROOT="$(BACKUP_ROOT)" RETENTION_DAYS="$(RETENTION_DAYS)" sh "$(BACKUP_SCRIPT)" cleanup \ No newline at end of file diff --git a/scripts/backup.sh b/scripts/backup.sh index c4b1794..7e21e7e 100644 --- a/scripts/backup.sh +++ b/scripts/backup.sh @@ -21,6 +21,10 @@ require_command() { command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1" } +has_command() { + command -v "$1" >/dev/null 2>&1 +} + load_env() { [ -f "$ENV_FILE" ] || fail "Env file not found: $ENV_FILE" @@ -37,7 +41,6 @@ load_env() { } db_dump() { - require_command mysqldump require_command gzip load_env @@ -45,15 +48,22 @@ db_dump() { output_path="$BACKUP_ROOT/db/${DB_NAME}_${TIMESTAMP}.sql.gz" log "Creating DB dump: $output_path" - MYSQL_PWD="$DB_PASS" mysqldump \ - --host="$DB_HOST" \ - --port="$DB_PORT" \ - --user="$DB_USER" \ - --single-transaction \ - --quick \ - --routines \ - --triggers \ - "$DB_NAME" | gzip > "$output_path" + + if has_command mysqldump; then + MYSQL_PWD="$DB_PASS" mysqldump \ + --host="$DB_HOST" \ + --port="$DB_PORT" \ + --user="$DB_USER" \ + --single-transaction \ + --quick \ + --routines \ + --triggers \ + "$DB_NAME" | gzip > "$output_path" + elif has_command docker; then + docker exec itam-backend sh -lc "MYSQL_PWD=\"$DB_PASS\" exec mysqldump --host=\"$DB_HOST\" --port=\"$DB_PORT\" --user=\"$DB_USER\" --single-transaction --quick --routines --triggers \"$DB_NAME\"" | gzip > "$output_path" + else + fail "Required command not found: mysqldump (and docker fallback unavailable)" + fi log "DB dump completed: $output_path" }