Agrego carpeta del source sql de la Base de Datos
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# database/backup/dump.sh
|
||||
#
|
||||
# Dump + comprimir + cifrar + subir a Drive. Implementa US-B04 de
|
||||
# Documentation/Backlog/Backups.md.
|
||||
#
|
||||
# Falla ruidoso a propósito (invariante 3 del backlog): `set -e` corta el
|
||||
# script ante el primer error, así el job de GitHub Actions que lo llama
|
||||
# queda en rojo y GitHub avisa por mail. No hay try/catch que trague nada.
|
||||
#
|
||||
# Variables de entorno requeridas:
|
||||
# DATABASE_URL connection string del SESSION POOLER de Supabase.
|
||||
# El transaction pooler rompe pg_dump (ver US-B03).
|
||||
# BACKUP_PASSPHRASE passphrase simétrica de cifrado (gpg).
|
||||
# RCLONE_REMOTE_NAME nombre del remote configurado en rclone.conf
|
||||
# (ej: "gdrive").
|
||||
# GDRIVE_FOLDER_ID ID de la carpeta de Drive destino (no el nombre).
|
||||
#
|
||||
# Alcance del dump (invariante 6): esquemas public + internal únicamente.
|
||||
# NUNCA auth ni el resto de los esquemas internos de Supabase — por eso se
|
||||
# usa --schema explícito en vez de un dump completo.
|
||||
|
||||
: "${DATABASE_URL:?Falta DATABASE_URL (session pooler de Supabase)}"
|
||||
: "${BACKUP_PASSPHRASE:?Falta BACKUP_PASSPHRASE}"
|
||||
: "${RCLONE_REMOTE_NAME:?Falta RCLONE_REMOTE_NAME (ej: gdrive)}"
|
||||
: "${GDRIVE_FOLDER_ID:?Falta GDRIVE_FOLDER_ID}"
|
||||
|
||||
FECHA="$(date -u +%Y-%m-%d)"
|
||||
BASE="soma-backup-${FECHA}"
|
||||
SQL="${BASE}.sql"
|
||||
GZ="${SQL}.gz"
|
||||
ENC="${GZ}.gpg"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$SQL" "$GZ" "$ENC"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
echo "== Dump de public + internal (sin auth) -> ${SQL} =="
|
||||
pg_dump "$DATABASE_URL" --schema=public --schema=internal --file="$SQL"
|
||||
|
||||
echo "== Comprimiendo -> ${GZ} =="
|
||||
gzip -f "$SQL"
|
||||
|
||||
echo "== Cifrando (AES256 simétrico) -> ${ENC} =="
|
||||
gpg --batch --yes --pinentry-mode loopback \
|
||||
--passphrase "$BACKUP_PASSPHRASE" \
|
||||
--symmetric --cipher-algo AES256 \
|
||||
--output "$ENC" "$GZ"
|
||||
|
||||
echo "== Subiendo ${ENC} a ${RCLONE_REMOTE_NAME}: (carpeta ${GDRIVE_FOLDER_ID}) =="
|
||||
rclone --drive-root-folder-id="$GDRIVE_FOLDER_ID" copy "$ENC" "${RCLONE_REMOTE_NAME}:"
|
||||
|
||||
echo "OK: ${ENC} subido y verificado."
|
||||
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# database/backup/rotate.sh
|
||||
#
|
||||
# Rotación sin estado de los backups en Drive. Implementa US-B05 de
|
||||
# Documentation/Backlog/Backups.md §3.
|
||||
#
|
||||
# Esquema de retención (recalculado desde cero en cada corrida, sin leer
|
||||
# ni escribir ningún registro de "qué es ancla" — invariante 5):
|
||||
# - Los 3 backups más recientes, sin importar la fecha.
|
||||
# - Un ancla mensual por cada uno de los últimos 6 meses cerrados (el
|
||||
# backup más nuevo de ese mes).
|
||||
# Todo lo que no entra en ese conjunto se borra.
|
||||
#
|
||||
# Invariante 4: solo se tocan archivos que matchean exactamente el patrón
|
||||
# soma-backup-AAAA-MM-DD.sql.gz.gpg dentro de la carpeta indicada. Nunca
|
||||
# se borra nada más.
|
||||
#
|
||||
# Variables de entorno requeridas:
|
||||
# RCLONE_REMOTE_NAME nombre del remote configurado en rclone.conf.
|
||||
# GDRIVE_FOLDER_ID ID de la carpeta de Drive a rotar.
|
||||
|
||||
: "${RCLONE_REMOTE_NAME:?Falta RCLONE_REMOTE_NAME (ej: gdrive)}"
|
||||
: "${GDRIVE_FOLDER_ID:?Falta GDRIVE_FOLDER_ID}"
|
||||
|
||||
REMOTE="${RCLONE_REMOTE_NAME}:"
|
||||
PATTERN='^soma-backup-([0-9]{4}-[0-9]{2}-[0-9]{2})\.sql\.gz\.gpg$'
|
||||
|
||||
mapfile -t files < <(rclone --drive-root-folder-id="$GDRIVE_FOLDER_ID" lsf "$REMOTE" --files-only)
|
||||
|
||||
declare -A file_by_date=()
|
||||
dates=()
|
||||
for f in "${files[@]}"; do
|
||||
if [[ "$f" =~ $PATTERN ]]; then
|
||||
d="${BASH_REMATCH[1]}"
|
||||
file_by_date["$d"]="$f"
|
||||
dates+=("$d")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#dates[@]}" -eq 0 ]; then
|
||||
echo "No hay backups con el patrón esperado en ${REMOTE} (carpeta ${GDRIVE_FOLDER_ID}). Nada que rotar."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Fechas ordenadas descendente (más nueva primero).
|
||||
IFS=$'\n' sorted=($(printf '%s\n' "${dates[@]}" | sort -r)); unset IFS
|
||||
|
||||
declare -A keep=()
|
||||
|
||||
# Los 3 backups más recientes.
|
||||
for i in 0 1 2; do
|
||||
d="${sorted[$i]:-}"
|
||||
[ -n "$d" ] && keep["$d"]=1
|
||||
done
|
||||
|
||||
# Ancla mensual de cada uno de los últimos 6 meses cerrados (el actual no
|
||||
# cuenta: todavía no cerró).
|
||||
hoy_mes_uno="$(date -u +%Y-%m-01)"
|
||||
for i in 1 2 3 4 5 6; do
|
||||
ym="$(date -u -d "${hoy_mes_uno} -${i} month" +%Y-%m)"
|
||||
for d in "${sorted[@]}"; do
|
||||
if [[ "$d" == "${ym}"* ]]; then
|
||||
keep["$d"]=1 # sorted desc: el primer match del mes es el más nuevo
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
echo "== Conservar (${#keep[@]} de ${#sorted[@]}) =="
|
||||
for d in "${sorted[@]}"; do
|
||||
[ -n "${keep[$d]:-}" ] && echo " ${file_by_date[$d]}"
|
||||
done
|
||||
|
||||
echo "== Borrar =="
|
||||
borrados=0
|
||||
for d in "${sorted[@]}"; do
|
||||
if [ -z "${keep[$d]:-}" ]; then
|
||||
echo " ${file_by_date[$d]}"
|
||||
rclone --drive-root-folder-id="$GDRIVE_FOLDER_ID" deletefile "${REMOTE}${file_by_date[$d]}"
|
||||
borrados=$((borrados + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "OK: ${borrados} archivo(s) borrado(s), ${#keep[@]} conservado(s)."
|
||||
Reference in New Issue
Block a user