mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-10-17 05:14:19 +02:00
Retire the dotted "N.O.M.A.D." styling everywhere in favor of "Project NOMAD" (no periods) ahead of the trademark filing, and remove the "Node for Offline Media, Archives, and Data" backronym from all copy except a single origin-story line in the About page (corrected to "Maps" rather than "Media"). Scope is display strings only: docs, admin UI labels/titles, install script output, Dockerfile labels, package.json description, and issue templates. Code identifiers, container/service names, env vars, CSS tokens, URLs, and the project-nomad slug are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
241 lines
9.9 KiB
Bash
241 lines
9.9 KiB
Bash
#!/bin/bash
|
|
|
|
# Project NOMAD - One-Time Updater Fix Script
|
|
#
|
|
# Script | Project NOMAD 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 NOMAD 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 NOMAD 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 NOMAD 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} Stopping and removing existing updater containers..."
|
|
|
|
# Stop and remove via compose first (handles the compose-tracked container)
|
|
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" stop updater >> /dev/null 2>&1 || true
|
|
docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" rm -f updater >> /dev/null 2>&1 || true
|
|
|
|
# Force-remove any stale container still holding the name (e.g. hash-prefixed remnants)
|
|
docker rm -f nomad_updater >> /dev/null 2>&1 || true
|
|
|
|
echo -e "${YELLOW}#${RESET} Starting the updated updater container..."
|
|
if ! docker compose -p "$COMPOSE_PROJECT_NAME" -f "$COMPOSE_FILE" up -d updater; then
|
|
echo -e "${RED}#${RESET} Failed to start the updater container."
|
|
exit 1
|
|
fi
|
|
echo -e "${GREEN}#${RESET} Updater container started.\n"
|
|
}
|
|
|
|
verify_sidecar_running() {
|
|
sleep 3
|
|
# Use exact name match to avoid false positives from hash-prefixed stale containers
|
|
if docker ps --filter "name=^nomad_updater$" --filter "status=running" --format '{{.Names}}' | grep -qx "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 NOMAD — One-Time Updater Fix Script ${GREEN}#${RESET}"
|
|
echo -e "${GREEN}#########################################################################${RESET}\n"
|
|
|
|
check_is_bash
|
|
check_has_sudo
|
|
check_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 NOMAD via the UI."
|
|
echo -e "${GREEN}#########################################################################${RESET}\n"
|