mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-10-30 23:51:57 +01:00
The version check already worked for an installation running the published
image - CI freezes the version before building, so version_frozen.py ships
inside it - but "Update now" never appeared, because the webhook behind it
was installed from scripts/, a folder such an installation does not have.
And update.sh could not have served it anyway: it does git pull + rebuild.
- both installers accept being piped through bash (curl | sudo bash). They
then download what they need into /opt/mc-webui-{updater,watchdog} and
find the instance by looking for docker-compose.yml. A checkout install
keeps running its scripts from the checkout, unchanged
- updater.py picks the update path per request: git checkout -> update.sh,
bare docker-compose.yml -> the new update-image.sh (pull + recreate).
Resolved per request, so cloning the repo later flips it without a restart
- both update scripts end with MC_UPDATE_RESULT=updated|no_change, which
the webhook parses and /api/updater/status passes on. The UI used to
watch only for the version to change and then claim it timed out after
two minutes - the normal outcome right after a push, while the image for
that commit is still building. It now says so as soon as the run ends,
in a neutral state rather than a red cross
- the marker prints only when stdout is not a terminal, so mcupdate output
stays as it was
Also corrects the claim, in the README and in updater.py's own docstring,
that the webhook listens on localhost. It binds 0.0.0.0 - the container
reaches it over the Docker bridge - and sends no token, so port 5050 is
worth keeping off untrusted networks.
Verified: update-image.sh reports updated then no_change against a real
docker compose project; the standalone installer installs, runs and
uninstalls cleanly under systemd (WSL), with the app reaching it through
host.docker.internal and a triggered run coming back changed=false; the
resulting modal checked in a browser in both en and pl.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
255 lines
8.1 KiB
Bash
Executable File
255 lines
8.1 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# mc-webui Update Webhook Installer
|
|
#
|
|
# This script installs the update webhook service that allows
|
|
# remote updates from the mc-webui GUI.
|
|
#
|
|
# Two ways to run it:
|
|
#
|
|
# From a git checkout:
|
|
# sudo ./install.sh
|
|
#
|
|
# Without the repository (Docker Hub installations) - run this from the
|
|
# folder holding your docker-compose.yml, it downloads what it needs:
|
|
# curl -fsSL https://raw.githubusercontent.com/MarekWo/mc-webui/main/scripts/updater/install.sh | sudo bash
|
|
#
|
|
# Uninstall:
|
|
# sudo ./install.sh --uninstall
|
|
# curl -fsSL <same url> | sudo bash -s -- --uninstall
|
|
#
|
|
# Environment:
|
|
# MCWEBUI_DIR - your mc-webui folder, if it cannot be found automatically
|
|
# MC_BRANCH - branch to download from in standalone mode (default: main)
|
|
# MC_RAW_BASE - download from somewhere else entirely (fork or mirror)
|
|
#
|
|
|
|
set -e
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; }
|
|
|
|
# Check if running as root
|
|
if [ "$EUID" -ne 0 ]; then
|
|
error "Please run as root: sudo $0"
|
|
fi
|
|
|
|
SERVICE_NAME="mc-webui-updater"
|
|
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
|
STANDALONE_DIR="/opt/mc-webui-updater"
|
|
MC_BRANCH="${MC_BRANCH:-main}"
|
|
# MC_RAW_BASE lets a fork, a mirror, or a test run point the download
|
|
# somewhere other than this repository.
|
|
RAW_BASE="${MC_RAW_BASE:-https://raw.githubusercontent.com/MarekWo/mc-webui/${MC_BRANCH}/scripts/updater}"
|
|
|
|
# Uninstall
|
|
if [ "$1" == "--uninstall" ]; then
|
|
info "Uninstalling ${SERVICE_NAME}..."
|
|
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
systemctl stop "$SERVICE_NAME"
|
|
info "Service stopped"
|
|
fi
|
|
|
|
if systemctl is-enabled --quiet "$SERVICE_NAME" 2>/dev/null; then
|
|
systemctl disable "$SERVICE_NAME"
|
|
info "Service disabled"
|
|
fi
|
|
|
|
if [ -f "$SERVICE_FILE" ]; then
|
|
rm "$SERVICE_FILE"
|
|
systemctl daemon-reload
|
|
info "Service file removed"
|
|
fi
|
|
|
|
# Only ever created by a standalone install, so it is safe to remove.
|
|
# A repository install keeps its scripts where the checkout has them.
|
|
if [ -d "$STANDALONE_DIR" ]; then
|
|
rm -rf "$STANDALONE_DIR"
|
|
info "Removed $STANDALONE_DIR"
|
|
fi
|
|
|
|
echo -e "${GREEN}Uninstallation complete!${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Where are we running from, and what are we updating?
|
|
# ---------------------------------------------------------------------------
|
|
|
|
# Piped through bash (curl | sudo bash) leaves BASH_SOURCE pointing at stdin,
|
|
# so the presence of updater.py next to us is what tells the two apart.
|
|
SELF_DIR=""
|
|
if [ -n "${BASH_SOURCE[0]:-}" ] && [ -f "${BASH_SOURCE[0]}" ]; then
|
|
SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
fi
|
|
|
|
# Find the folder holding docker-compose.yml - that is the mc-webui instance
|
|
find_instance_dir() {
|
|
local candidates=("$@")
|
|
local d
|
|
for d in "${candidates[@]}"; do
|
|
[ -n "$d" ] || continue
|
|
if [ -f "$d/docker-compose.yml" ] || [ -f "$d/docker-compose.yaml" ]; then
|
|
(cd "$d" && pwd)
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
fetch() { # fetch <url> <destination>
|
|
if command -v curl >/dev/null 2>&1; then
|
|
curl -fsSL "$1" -o "$2"
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
wget -qO "$2" "$1"
|
|
else
|
|
error "Neither curl nor wget is available - cannot download $1"
|
|
fi
|
|
}
|
|
|
|
if [ -n "$SELF_DIR" ] && [ -f "$SELF_DIR/updater.py" ]; then
|
|
# Repository install: run the scripts straight out of the checkout, so
|
|
# they stay in step with the rest of the code on every git pull.
|
|
INSTALL_MODE="repository"
|
|
SCRIPT_DIR="$SELF_DIR"
|
|
MCWEBUI_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
|
else
|
|
INSTALL_MODE="standalone"
|
|
SCRIPT_DIR="$STANDALONE_DIR"
|
|
|
|
# $HOME is root's under sudo, so the invoking user's home is the one worth
|
|
# looking in - that is where the README tells people to put mc-webui.
|
|
SUDO_HOME=""
|
|
if [ -n "${SUDO_USER:-}" ]; then
|
|
SUDO_HOME="$(getent passwd "$SUDO_USER" | cut -d: -f6)"
|
|
fi
|
|
|
|
MCWEBUI_DIR="$(find_instance_dir \
|
|
"${MCWEBUI_DIR:-}" \
|
|
"$(pwd)" \
|
|
"${SUDO_HOME:+$SUDO_HOME/mc-webui}" \
|
|
"$HOME/mc-webui" \
|
|
"/opt/mc-webui")" || error \
|
|
"Cannot find your mc-webui folder (no docker-compose.yml). Run this from that folder, or set MCWEBUI_DIR=/path/to/mc-webui."
|
|
fi
|
|
|
|
# Install
|
|
info "Installing ${SERVICE_NAME}..."
|
|
info " Install mode: $INSTALL_MODE"
|
|
info " mc-webui directory: $MCWEBUI_DIR"
|
|
|
|
if [ "$INSTALL_MODE" == "standalone" ]; then
|
|
info " Downloading from branch: $MC_BRANCH"
|
|
mkdir -p "$SCRIPT_DIR"
|
|
|
|
# Into a temp file first, so a failed download cannot truncate a working
|
|
# install of the service that is running right now.
|
|
for f in updater.py update-image.sh; do
|
|
tmp="$(mktemp)"
|
|
fetch "$RAW_BASE/$f" "$tmp" || error "Failed to download $f from $RAW_BASE"
|
|
[ -s "$tmp" ] || error "Downloaded $f is empty"
|
|
mv "$tmp" "$SCRIPT_DIR/$f"
|
|
chmod 644 "$SCRIPT_DIR/$f"
|
|
info " Downloaded $f"
|
|
done
|
|
chmod +x "$SCRIPT_DIR/update-image.sh"
|
|
fi
|
|
|
|
# Check if updater.py exists
|
|
if [ ! -f "$SCRIPT_DIR/updater.py" ]; then
|
|
error "updater.py not found in $SCRIPT_DIR"
|
|
fi
|
|
|
|
# Sanity-check that there is something here to update at all
|
|
if [ ! -d "$MCWEBUI_DIR/.git" ] \
|
|
&& [ ! -f "$MCWEBUI_DIR/docker-compose.yml" ] \
|
|
&& [ ! -f "$MCWEBUI_DIR/docker-compose.yaml" ]; then
|
|
error "$MCWEBUI_DIR is neither a git checkout nor a docker-compose folder"
|
|
fi
|
|
|
|
# Configure git safe.directory for root (required since service runs as root).
|
|
# Only relevant to a checkout - a Docker Hub install has no repository.
|
|
if [ -d "$MCWEBUI_DIR/.git" ]; then
|
|
info "Configuring git safe.directory..."
|
|
git config --global --add safe.directory "$MCWEBUI_DIR" 2>/dev/null || true
|
|
fi
|
|
|
|
# Create service file with correct paths
|
|
info "Creating systemd service file..."
|
|
cat > "$SERVICE_FILE" << EOF
|
|
[Unit]
|
|
Description=mc-webui Update Webhook Server
|
|
Documentation=https://github.com/MarekWo/mc-webui
|
|
After=network.target docker.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=root
|
|
Environment=MCWEBUI_DIR=${MCWEBUI_DIR}
|
|
Environment=UPDATER_TOKEN=
|
|
ExecStart=/usr/bin/python3 -u ${SCRIPT_DIR}/updater.py
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
|
|
info "Reloading systemd..."
|
|
systemctl daemon-reload
|
|
|
|
info "Enabling service..."
|
|
systemctl enable "$SERVICE_NAME"
|
|
|
|
# restart, not start: re-running the installer is how you upgrade a
|
|
# standalone install, and a running service would otherwise keep the old code
|
|
info "Starting service..."
|
|
systemctl restart "$SERVICE_NAME"
|
|
|
|
# Wait a moment for service to start
|
|
sleep 2
|
|
|
|
# Check if service is running
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
info "Service is running!"
|
|
|
|
# Test health endpoint
|
|
if command -v curl &> /dev/null; then
|
|
HEALTH=$(curl -s http://127.0.0.1:5050/health 2>/dev/null || echo "")
|
|
if echo "$HEALTH" | grep -q '"status": *"ok"'; then
|
|
info "Health check passed!"
|
|
# Which update path did it settle on? Worth showing - it is the
|
|
# difference between "git pull + rebuild" and "docker compose pull".
|
|
DETECTED=$(echo "$HEALTH" | grep -o '"mode": *"[^"]*"' | cut -d'"' -f4)
|
|
[ -n "$DETECTED" ] && info "Detected update mode: $DETECTED"
|
|
else
|
|
warn "Health check failed - service may still be starting"
|
|
fi
|
|
fi
|
|
else
|
|
error "Service failed to start. Check: journalctl -u $SERVICE_NAME"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${GREEN}Installation complete!${NC}"
|
|
echo ""
|
|
echo "The update webhook is now running on port 5050."
|
|
echo "You can now use the 'Update' button in mc-webui GUI."
|
|
echo ""
|
|
echo "Useful commands:"
|
|
echo " systemctl status $SERVICE_NAME # Check status"
|
|
echo " sudo journalctl -u $SERVICE_NAME -f # View logs"
|
|
if [ "$INSTALL_MODE" == "standalone" ]; then
|
|
echo " curl -fsSL $RAW_BASE/install.sh | sudo bash -s -- --uninstall"
|
|
else
|
|
echo " sudo $0 --uninstall # Uninstall"
|
|
fi
|