project-nomad/install/run_updater_fixes.sh
2026-03-05 04:06:56 +00:00

231 lines
9.3 KiB
Bash

#!/bin/bash
# Project N.O.M.A.D. - One-Time Updater Fix Script
#
# Script | Project N.O.M.A.D. One-Time Updater Fix Script
# Version | 1.0.0
# Author | Crosstalk Solutions, LLC
# Website | https://crosstalksolutions.com
#
# PURPOSE:
# This is a one-time migration script. It deploys two fixes to the sidecar
# updater that cannot be applied through the normal in-app update mechanism:
#
# Fix 1 — Sidecar volume write access
# Removes the :ro (read-only) flag from the sidecar's /opt/project-nomad
# volume mount in compose.yml. The sidecar must be able to write to
# compose.yml so it can set the correct Docker image tag when installing
# RC or stable versions.
#
# Fix 2 — RC-aware sidecar watcher
# Downloads the updated sidecar Dockerfile (adds jq) and update-watcher.sh
# (reads target_tag from the update request and applies it to compose.yml
# before pulling images), then rebuilds and restarts the sidecar container.
#
# NOTE: The companion fix in the admin service (system_update_service.ts,
# which writes the target_tag into the update request) ships in the GHCR
# image and will take effect automatically on the next normal app update.
###############################################################################
# Color Codes
###############################################################################
RESET='\033[0m'
YELLOW='\033[1;33m'
RED='\033[1;31m'
GREEN='\033[1;32m'
WHITE_R='\033[39m'
###############################################################################
# Constants
###############################################################################
NOMAD_DIR="/opt/project-nomad"
COMPOSE_FILE="${NOMAD_DIR}/compose.yml"
SIDECAR_DIR="${NOMAD_DIR}/sidecar-updater"
COMPOSE_PROJECT_NAME="project-nomad"
SIDECAR_DOCKERFILE_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/sidecar-updater/Dockerfile"
SIDECAR_SCRIPT_URL="https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/sidecar-updater/update-watcher.sh"
###############################################################################
# Pre-flight Checks
###############################################################################
check_is_bash() {
if [[ -z "$BASH_VERSION" ]]; then
echo -e "${RED}#${RESET} This script must be run with bash."
echo -e "${RED}#${RESET} Example: bash $(basename "$0")"
exit 1
fi
echo -e "${GREEN}#${RESET} Running in bash.\n"
}
check_confirmation() {
echo -e "${YELLOW}#${RESET} This is a very specific fix script for a very specific issue. You probably don't need to run this unless you were specifically directed to by the N.O.M.A.D. team."
echo -e "${YELLOW}#${RESET} Please ensure you have a backup of your data before proceeding."
read -rp "Do you want to continue? (y/N) " response
if [[ ! "$response" =~ ^[Yy]$ ]]; then
echo -e "${RED}#${RESET} Aborting. No changes have been made."
exit 0
fi
echo -e "${GREEN}#${RESET} Confirmation received. Proceeding with fixes...\n"
}
check_has_sudo() {
if sudo -n true 2>/dev/null; then
echo -e "${GREEN}#${RESET} Sudo permissions confirmed.\n"
else
echo -e "${RED}#${RESET} This script requires sudo permissions."
echo -e "${RED}#${RESET} Example: sudo bash $(basename "$0")"
exit 1
fi
}
check_docker_running() {
if ! command -v docker &>/dev/null; then
echo -e "${RED}#${RESET} Docker is not installed. Cannot proceed."
exit 1
fi
if ! systemctl is-active --quiet docker; then
echo -e "${RED}#${RESET} Docker is not running. Please start Docker and try again."
exit 1
fi
echo -e "${GREEN}#${RESET} Docker is running.\n"
}
check_compose_file() {
if [[ ! -f "$COMPOSE_FILE" ]]; then
echo -e "${RED}#${RESET} compose.yml not found at ${COMPOSE_FILE}."
echo -e "${RED}#${RESET} Please ensure Project N.O.M.A.D. is installed before running this script."
exit 1
fi
echo -e "${GREEN}#${RESET} Found compose.yml at ${COMPOSE_FILE}.\n"
}
check_sidecar_dir() {
if [[ ! -d "$SIDECAR_DIR" ]]; then
echo -e "${RED}#${RESET} Sidecar directory not found at ${SIDECAR_DIR}."
echo -e "${RED}#${RESET} Please ensure Project N.O.M.A.D. is installed before running this script."
exit 1
fi
echo -e "${GREEN}#${RESET} Found sidecar directory at ${SIDECAR_DIR}.\n"
}
###############################################################################
# Fix 1 — Remove :ro from sidecar volume mount
###############################################################################
backup_compose_file() {
local backup="${COMPOSE_FILE}.bak.$(date +%Y%m%d%H%M%S)"
echo -e "${YELLOW}#${RESET} Backing up compose.yml to ${backup}..."
if cp "$COMPOSE_FILE" "$backup"; then
echo -e "${GREEN}#${RESET} Backup created at ${backup}.\n"
else
echo -e "${RED}#${RESET} Failed to create backup. Aborting."
exit 1
fi
}
fix_sidecar_volume_mount() {
# Idempotent: skip if :ro is already absent from the sidecar mount line
if ! grep -q '/opt/project-nomad:/opt/project-nomad:ro' "$COMPOSE_FILE"; then
echo -e "${GREEN}#${RESET} Sidecar volume mount is already writable — no change needed.\n"
return 0
fi
echo -e "${YELLOW}#${RESET} Removing :ro restriction from sidecar volume mount in compose.yml..."
sed -i 's|/opt/project-nomad:/opt/project-nomad:ro.*|/opt/project-nomad:/opt/project-nomad # Writable access required so the updater can set the correct image tag in compose.yml|' "$COMPOSE_FILE"
if grep -q '/opt/project-nomad:/opt/project-nomad:ro' "$COMPOSE_FILE"; then
echo -e "${RED}#${RESET} Failed to remove :ro from compose.yml. Please update it manually:"
echo -e "${WHITE_R} - /opt/project-nomad:/opt/project-nomad:ro${RESET}${WHITE_R}- /opt/project-nomad:/opt/project-nomad${RESET}"
exit 1
fi
echo -e "${GREEN}#${RESET} Sidecar volume mount updated successfully.\n"
}
###############################################################################
# Fix 2 — Download updated sidecar files and rebuild
###############################################################################
download_updated_sidecar_files() {
echo -e "${YELLOW}#${RESET} Downloading updated sidecar Dockerfile..."
if ! curl -fsSL "$SIDECAR_DOCKERFILE_URL" -o "${SIDECAR_DIR}/Dockerfile"; then
echo -e "${RED}#${RESET} Failed to download sidecar Dockerfile. Check your network connection."
exit 1
fi
echo -e "${GREEN}#${RESET} Sidecar Dockerfile updated.\n"
echo -e "${YELLOW}#${RESET} Downloading updated update-watcher.sh..."
if ! curl -fsSL "$SIDECAR_SCRIPT_URL" -o "${SIDECAR_DIR}/update-watcher.sh"; then
echo -e "${RED}#${RESET} Failed to download update-watcher.sh. Check your network connection."
exit 1
fi
chmod +x "${SIDECAR_DIR}/update-watcher.sh"
echo -e "${GREEN}#${RESET} update-watcher.sh updated.\n"
}
rebuild_sidecar() {
echo -e "${YELLOW}#${RESET} Rebuilding the updater container (this may take a moment)..."
if ! docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" build updater; then
echo -e "${RED}#${RESET} Failed to rebuild the updater container. See output above for details."
exit 1
fi
echo -e "${GREEN}#${RESET} Updater container rebuilt successfully.\n"
}
restart_sidecar() {
echo -e "${YELLOW}#${RESET} Restarting the updater container..."
if ! docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" up -d --force-recreate updater; then
echo -e "${RED}#${RESET} Failed to restart the updater container."
exit 1
fi
echo -e "${GREEN}#${RESET} Updater container restarted.\n"
}
verify_sidecar_running() {
sleep 3
if docker ps --filter "name=nomad_updater" --filter "status=running" --format '{{.Names}}' | grep -q "nomad_updater"; then
echo -e "${GREEN}#${RESET} Updater container is running.\n"
else
echo -e "${RED}#${RESET} Updater container does not appear to be running."
echo -e "${RED}#${RESET} Check its logs with: docker logs nomad_updater"
exit 1
fi
}
###############################################################################
# Main
###############################################################################
echo -e "${GREEN}#########################################################################${RESET}"
echo -e "${GREEN}#${RESET} Project N.O.M.A.D. — One-Time Updater Fix Script ${GREEN}#${RESET}"
echo -e "${GREEN}#########################################################################${RESET}\n"
check_is_bash
check_has_sudo
chech_confirmation
check_docker_running
check_compose_file
check_sidecar_dir
echo -e "${YELLOW}#${RESET} Starting Fix 1: Sidecar volume write access...\n"
backup_compose_file
fix_sidecar_volume_mount
echo -e "${YELLOW}#${RESET} Starting Fix 2: RC-aware sidecar watcher...\n"
download_updated_sidecar_files
rebuild_sidecar
restart_sidecar
verify_sidecar_running
echo -e "${GREEN}#########################################################################${RESET}"
echo -e "${GREEN}#${RESET} All fixes applied successfully!"
echo -e "${GREEN}#${RESET}"
echo -e "${GREEN}#${RESET} The updater sidecar can now install RC and stable versions correctly."
echo -e "${GREEN}#${RESET} The remaining fix (admin service target_tag support) will apply"
echo -e "${GREEN}#${RESET} automatically the next time you update N.O.M.A.D. via the UI."
echo -e "${GREEN}#########################################################################${RESET}\n"