From 9a3a4576b64df4272ba320270be67996f299a305 Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Thu, 10 Sep 2026 18:50:50 +0300 Subject: [PATCH] Ops: add safe Timeweb production sync --- ops/timeweb/caterium-production-sync.sh | 110 ++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 ops/timeweb/caterium-production-sync.sh diff --git a/ops/timeweb/caterium-production-sync.sh b/ops/timeweb/caterium-production-sync.sh new file mode 100644 index 0000000..644f1f7 --- /dev/null +++ b/ops/timeweb/caterium-production-sync.sh @@ -0,0 +1,110 @@ +#!/bin/sh +set -eu + +REPO_URL="${CATERIUM_REPO_URL:-git@github.com:pavlov346346-source/caterium-app.git}" +BRANCH="${CATERIUM_BRANCH:-production}" +STATE_DIR="${CATERIUM_STATE_DIR:-$HOME/.caterium-deploy}" +REPO_DIR="${CATERIUM_REPO_DIR:-$STATE_DIR/repo}" +BACKUP_DIR="${CATERIUM_BACKUP_DIR:-$STATE_DIR/backups}" +LOCK_DIR="$STATE_DIR/lock" + +log() { + printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" +} + +fail() { + log "ERROR: $*" >&2 + exit 1 +} + +find_web_root() { + if [ -n "${CATERIUM_WEB_ROOT:-}" ]; then + printf '%s\n' "$CATERIUM_WEB_ROOT" + return 0 + fi + + for candidate in \ + "$HOME/caterium.ru/public_html" \ + "$HOME/app.caterium.ru/public_html" + do + if [ -d "$candidate" ] && [ -f "$candidate/index.html" ]; then + printf '%s\n' "$candidate" + return 0 + fi + done + + return 1 +} + +command -v git >/dev/null 2>&1 || fail "git is not available" +command -v rsync >/dev/null 2>&1 || fail "rsync is not available" +command -v tar >/dev/null 2>&1 || fail "tar is not available" + +mkdir -p "$STATE_DIR" "$BACKUP_DIR" + +if ! mkdir "$LOCK_DIR" 2>/dev/null; then + if [ -f "$LOCK_DIR/pid" ]; then + old_pid=$(cat "$LOCK_DIR/pid" 2>/dev/null || true) + if [ -n "$old_pid" ] && kill -0 "$old_pid" 2>/dev/null; then + log "Another deployment is running; skip" + exit 0 + fi + fi + rm -rf "$LOCK_DIR" + mkdir "$LOCK_DIR" 2>/dev/null || { + log "Could not acquire deployment lock; skip" + exit 0 + } +fi +printf '%s\n' "$$" > "$LOCK_DIR/pid" +cleanup_lock() { + rm -rf "$LOCK_DIR" +} +trap cleanup_lock 0 HUP INT TERM + +WEB_ROOT=$(find_web_root) || fail "web root not found; set CATERIUM_WEB_ROOT" +[ -d "$WEB_ROOT" ] || fail "web root does not exist: $WEB_ROOT" + +if [ ! -d "$REPO_DIR/.git" ]; then + log "Initial clone of $BRANCH" + rm -rf "$REPO_DIR" + git clone --quiet --single-branch --branch "$BRANCH" "$REPO_URL" "$REPO_DIR" \ + || fail "initial Git clone failed; verify GitHub SSH key access" +fi + +cd "$REPO_DIR" +log "Fetching $BRANCH" +git fetch --quiet origin "$BRANCH" || fail "git fetch failed" +NEW_SHA=$(git rev-parse FETCH_HEAD) +OLD_SHA=$(cat "$WEB_ROOT/.caterium-release" 2>/dev/null || true) + +if [ "$NEW_SHA" = "$OLD_SHA" ]; then + log "Already current: $NEW_SHA" + exit 0 +fi + +git reset --quiet --hard "$NEW_SHA" || fail "could not switch deploy checkout" +[ -s "$REPO_DIR/public/index.html" ] || fail "release validation failed: public/index.html is missing or empty" + +STAMP=$(date '+%Y%m%d-%H%M%S') +BACKUP_FILE="$BACKUP_DIR/${STAMP}-${OLD_SHA:-pre-marker}.tgz" +log "Backing up current site" +tar -czf "$BACKUP_FILE" -C "$WEB_ROOT" . || fail "backup failed; live files left unchanged" + +log "Deploying $NEW_SHA to $WEB_ROOT" +rsync -a --delete-delay \ + --exclude '.well-known/' \ + --exclude '.caterium-release' \ + "$REPO_DIR/public/" "$WEB_ROOT/" \ + || fail "rsync failed" + +printf '%s\n' "$NEW_SHA" > "$WEB_ROOT/.caterium-release" + +# Keep only the four newest rollback archives. +set +e +ls -1t "$BACKUP_DIR"/*.tgz 2>/dev/null | awk 'NR > 4' | while IFS= read -r old_backup; do + rm -f "$old_backup" +done +set -e + +log "Deployment complete: $NEW_SHA"