#!/bin/sh # YAAR — Update script # ───────────────────────────────────────────────────────────────────────────── # Pulls the latest code from master, redeploys the app, and restarts the # service. Runs INSIDE the Alpine LXC container as root. Does not touch the # Proxmox host. # # Usage (inside the container): # sh /opt/yaar/update.sh # update code + restart service # sh /opt/yaar/update.sh --deps # also upgrade yt-dlp / Flask / Gunicorn # sh /opt/yaar/update.sh --reboot # reboot the whole container after update # yaar-update # convenience alias (installed by setup.sh) # # One-liner (always fetches the newest update logic too): # curl -fsSL https://gogs.av2x.dev/av2x/yaar/raw/master/update.sh | sh # # Safety: the current app.py and index.html are backed up before overwriting. # If the service fails to start on the new code, the previous version is # automatically restored and the service restarted, so YAAR is never left down. # ───────────────────────────────────────────────────────────────────────────── set -e GOGS_ARCHIVE="https://gogs.av2x.dev/av2x/yaar/archive/master.tar.gz" YAAR_DIR="/opt/yaar" WEB_DIR="${YAAR_DIR}/web" VENV_DIR="${YAAR_DIR}/venv" LOG_DIR="${YAAR_DIR}/logs" BACKUP_DIR="${YAAR_DIR}/backups" PORT=7474 RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[0;33m'; NC='\033[0m' info() { printf "${CYAN}[YAAR]${NC} %s\n" "$*"; } success() { printf "${GREEN}[OK]${NC} %s\n" "$*"; } warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$*"; } die() { printf "${RED}[ERR]${NC} %s\n" "$*"; exit 1; } # ── Flags ───────────────────────────────────────────────────────────────────── DO_DEPS=0 DO_REBOOT=0 for arg in "$@"; do case "$arg" in --deps) DO_DEPS=1 ;; --reboot) DO_REBOOT=1 ;; -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; *) warn "Unknown flag: $arg (ignored)" ;; esac done # ── Preflight ───────────────────────────────────────────────────────────────── [ "$(id -u)" -eq 0 ] || die "Run as root: sh /opt/yaar/update.sh" command -v pveversion >/dev/null 2>&1 && \ die "Run this INSIDE the container, not on the Proxmox host." [ -d "${YAAR_DIR}" ] || die "${YAAR_DIR} not found — is YAAR installed? Run setup.sh first." command -v curl >/dev/null 2>&1 || { info "Installing curl..."; apk add --no-cache curl >/dev/null; } # ── 1. Download latest ──────────────────────────────────────────────────────── TMP_DIR="$(mktemp -d /tmp/yaar-update.XXXXXX)" trap 'rm -rf "${TMP_DIR}"' EXIT info "Downloading latest code from master..." curl -fsSL "${GOGS_ARCHIVE}" -o "${TMP_DIR}/yaar.tar.gz" \ || die "Download failed. Check the URL and that the repo is reachable." tar -xzf "${TMP_DIR}/yaar.tar.gz" -C "${TMP_DIR}" # Locate the extracted source root (the folder containing web/app.py). SRC="$(dirname "$(find "${TMP_DIR}" -type f -path '*/web/app.py' | head -1)")/.." SRC="$(cd "${SRC}" 2>/dev/null && pwd || true)" [ -n "${SRC}" ] && [ -f "${SRC}/web/app.py" ] \ || die "Could not find web/app.py in the downloaded archive." [ -f "${SRC}/web/templates/index.html" ] \ || die "Could not find web/templates/index.html in the downloaded archive." success "Downloaded and extracted to ${SRC}" # ── 2. Back up current code ─────────────────────────────────────────────────── STAMP="$(date -u +%Y-%m-%d_%H-%M-%S)" BACKUP="${BACKUP_DIR}/${STAMP}" mkdir -p "${BACKUP}/web/templates" info "Backing up current code to ${BACKUP}..." [ -f "${WEB_DIR}/app.py" ] && cp "${WEB_DIR}/app.py" "${BACKUP}/web/app.py" [ -f "${WEB_DIR}/templates/index.html" ] && cp "${WEB_DIR}/templates/index.html" "${BACKUP}/web/templates/index.html" success "Backup saved" # Keep only the 5 most recent backups. if [ -d "${BACKUP_DIR}" ]; then ls -1dt "${BACKUP_DIR}"/*/ 2>/dev/null | tail -n +6 | while read -r old; do rm -rf "${old}" done fi # ── 3. Deploy new code ──────────────────────────────────────────────────────── info "Deploying new code..." cp "${SRC}/web/app.py" "${WEB_DIR}/app.py" cp "${SRC}/web/templates/index.html" "${WEB_DIR}/templates/index.html" # Refresh the update script itself if it changed (so next run uses newest logic). [ -f "${SRC}/update.sh" ] && cp "${SRC}/update.sh" "${YAAR_DIR}/update.sh" && chmod +x "${YAAR_DIR}/update.sh" success "New code deployed" # ── 4. Optional dependency upgrade ──────────────────────────────────────────── if [ "${DO_DEPS}" = "1" ]; then info "Upgrading Python dependencies (yt-dlp, Flask, Gunicorn)..." "${VENV_DIR}/bin/pip" install --quiet --upgrade yt-dlp flask gunicorn success "Dependencies upgraded — yt-dlp $(${VENV_DIR}/bin/yt-dlp --version)" fi # ── 5. Restart service with auto-rollback ───────────────────────────────────── info "Restarting YAAR service..." rc-service yaar restart >/dev/null 2>&1 || true sleep 2 # Health check — is the service actually listening on the port? healthy=0 if rc-service yaar status 2>/dev/null | grep -q "started"; then healthy=1 fi # Secondary check: HTTP response on the port if command -v curl >/dev/null 2>&1; then if curl -fsS -o /dev/null --max-time 5 "http://127.0.0.1:${PORT}/" 2>/dev/null; then healthy=1 fi fi if [ "${healthy}" = "1" ]; then success "Service restarted and healthy on port ${PORT}" else warn "Service did not come up cleanly — rolling back to previous version..." [ -f "${BACKUP}/web/app.py" ] && cp "${BACKUP}/web/app.py" "${WEB_DIR}/app.py" [ -f "${BACKUP}/web/templates/index.html" ] && cp "${BACKUP}/web/templates/index.html" "${WEB_DIR}/templates/index.html" rc-service yaar restart >/dev/null 2>&1 || true sleep 2 if rc-service yaar status 2>/dev/null | grep -q "started"; then warn "Rolled back to previous version. YAAR is running on the OLD code." warn "Check ${LOG_DIR}/gunicorn.log for why the new code failed." exit 1 else die "Rollback also failed to start. Inspect ${LOG_DIR}/gunicorn.log manually." fi fi # ── 6. Optional container reboot ────────────────────────────────────────────── if [ "${DO_REBOOT}" = "1" ]; then warn "Rebooting the container in 3 seconds (Ctrl+C to cancel)..." sleep 3 # Inside an LXC this signals Proxmox to restart the container. reboot exit 0 fi # ── Done ────────────────────────────────────────────────────────────────────── HOST_IP=$(ip route get 1.1.1.1 2>/dev/null | awk '/src/{print $7}' | head -1 || echo "") printf "\n" printf "${GREEN}╔══════════════════════════════════════════════════════╗${NC}\n" printf "${GREEN}║ YAAR updated successfully ║${NC}\n" printf "${GREEN}╠══════════════════════════════════════════════════════╣${NC}\n" printf "${GREEN}║ Web UI: http://%-34s║${NC}\n" "${HOST_IP}:${PORT}" printf "${GREEN}║ Backup: %-42s║${NC}\n" "${BACKUP}" printf "${GREEN}║ Rollback: cp ${BACKUP}/web/* back, then restart ║${NC}\n" printf "${GREEN}╚══════════════════════════════════════════════════════╝${NC}\n" printf "\n" info "Hard-refresh your browser (Ctrl+Shift+R) to load the new UI."