update forgejo update script now that its docker
This commit is contained in:
parent
d8e62067ba
commit
75dda8f900
1 changed files with 74 additions and 207 deletions
|
|
@ -2,12 +2,12 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# Forgejo Updater
|
# Seby's Forgejo Instance Updater
|
||||||
# curl | bash safe
|
|
||||||
########################################
|
########################################
|
||||||
|
|
||||||
TTY="/dev/tty"
|
########################################
|
||||||
|
# Styling
|
||||||
|
########################################
|
||||||
BOLD="\033[1m"
|
BOLD="\033[1m"
|
||||||
GREEN="\033[32m"
|
GREEN="\033[32m"
|
||||||
YELLOW="\033[33m"
|
YELLOW="\033[33m"
|
||||||
|
|
@ -22,240 +22,107 @@ err() { printf "${RED}✖ %s${RESET}\n" "$*"; }
|
||||||
|
|
||||||
have() { command -v "$1" >/dev/null 2>&1; }
|
have() { command -v "$1" >/dev/null 2>&1; }
|
||||||
|
|
||||||
need_tty() {
|
########################################
|
||||||
if [[ ! -r "$TTY" || ! -w "$TTY" ]]; then
|
# Prompt helper (curl | bash safe)
|
||||||
# No TTY (e.g. non-interactive CI) — that's fine as long as -y was passed.
|
########################################
|
||||||
return 1
|
TTY="/dev/tty"
|
||||||
fi
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt() {
|
prompt() {
|
||||||
local msg="$1" out
|
local msg="$1"
|
||||||
|
local out
|
||||||
printf "${BOLD}%s${RESET}" "$msg" >"$TTY"
|
printf "${BOLD}%s${RESET}" "$msg" >"$TTY"
|
||||||
IFS= read -r out <"$TTY"
|
IFS= read -r out <"$TTY"
|
||||||
printf "%s" "$out"
|
printf "%s" "$out"
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm_default_no() {
|
confirm_default_no() {
|
||||||
local msg="$1" ans
|
local msg="$1"
|
||||||
if $ASSUME_YES; then
|
local ans
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
if ! need_tty; then
|
|
||||||
err "No TTY and -y not given — refusing to guess on: $msg"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
ans="$(prompt "$msg [y/N]: ")"
|
ans="$(prompt "$msg [y/N]: ")"
|
||||||
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]]
|
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
require_root_or_sudo() {
|
########################################
|
||||||
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
|
# Config
|
||||||
if have sudo; then
|
########################################
|
||||||
SUDO="sudo"
|
APP_DIR="forgejo"
|
||||||
else
|
|
||||||
err "Root or sudo required."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
SUDO=""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
run_with_spinner() {
|
|
||||||
local label="$1"; shift
|
|
||||||
("$@") &
|
|
||||||
local pid=$! spin='|/-\' i=0
|
|
||||||
printf "\033[?25l" >"$TTY" 2>/dev/null || true
|
|
||||||
while kill -0 "$pid" 2>/dev/null; do
|
|
||||||
i=$(( (i + 1) % 4 ))
|
|
||||||
printf "\r${BLUE}${BOLD}..${RESET} %s %s" "${spin:$i:1}" "$label" >"$TTY" 2>/dev/null || true
|
|
||||||
sleep 0.12
|
|
||||||
done
|
|
||||||
wait "$pid"; local rc=$?
|
|
||||||
printf "\r\033[K" >"$TTY" 2>/dev/null || true
|
|
||||||
printf "\033[?25h" >"$TTY" 2>/dev/null || true
|
|
||||||
return "$rc"
|
|
||||||
}
|
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# Args
|
# Header
|
||||||
########################################
|
########################################
|
||||||
ASSUME_YES=false
|
clear
|
||||||
INSTALL_PATH="/usr/local/bin/forgejo"
|
cat << "EOF"
|
||||||
SERVICE_NAME="forgejo"
|
|
||||||
VERSION=""
|
|
||||||
|
|
||||||
usage() {
|
________ _
|
||||||
cat <<EOF
|
|_ __ | (_)
|
||||||
Usage: $0 [-y] [-p install_path] [-s service_name] <version>
|
| |_ \_|.--. _ .--. .--./) .---. __ .--.
|
||||||
|
| _| / .'`\ \[ `/'`\]/ /'`\;/ /__\\ [ |/ .'`\ \
|
||||||
|
_| |_ | \__. | | | \ \._//| \__., _ | || \__. |
|
||||||
|
|_____| '.__.' [___] .',__` '.__.'[ \_| | '.__.'
|
||||||
|
( ( __)) \____/
|
||||||
|
|
||||||
-y Assume yes to all prompts (non-interactive)
|
Forgejo Updater | v1.0
|
||||||
-p PATH Install path (default: /usr/local/bin/forgejo)
|
|
||||||
-s NAME systemd service name (default: forgejo)
|
|
||||||
|
|
||||||
Example: $0 11.0.10
|
|
||||||
EOF
|
EOF
|
||||||
}
|
|
||||||
|
|
||||||
while getopts ":yp:s:h" opt; do
|
########################################
|
||||||
case "$opt" in
|
# Checks
|
||||||
y) ASSUME_YES=true ;;
|
########################################
|
||||||
p) INSTALL_PATH="$OPTARG" ;;
|
section "Pre-flight Checks"
|
||||||
s) SERVICE_NAME="$OPTARG" ;;
|
|
||||||
h) usage; exit 0 ;;
|
|
||||||
*) usage; exit 1 ;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
shift $((OPTIND - 1))
|
|
||||||
|
|
||||||
VERSION="${1:-}"
|
if ! have docker; then
|
||||||
if [[ -z "$VERSION" ]]; then
|
err "Docker is not installed. Aborting."
|
||||||
usage
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# Strip an accidental leading "v" so both "11.0.10" and "v11.0.10" work.
|
say "Docker detected: $(docker --version)"
|
||||||
VERSION="${VERSION#v}"
|
|
||||||
|
|
||||||
need_tty || true
|
if ! docker compose version >/dev/null 2>&1; then
|
||||||
require_root_or_sudo
|
err "Docker Compose plugin not found. Aborting."
|
||||||
|
exit 1
|
||||||
########################################
|
fi
|
||||||
# Dependency check
|
say "Docker Compose detected: $(docker compose version --short 2>/dev/null || docker compose version)"
|
||||||
########################################
|
|
||||||
section "Checking dependencies"
|
if [[ ! -d "$APP_DIR" ]]; then
|
||||||
for bin in curl systemctl sha256sum install; do
|
err "Directory '${APP_DIR}' not found in $(pwd). Aborting."
|
||||||
have "$bin" || { err "Required tool '$bin' not found."; exit 1; }
|
exit 1
|
||||||
done
|
fi
|
||||||
say "All required tools present"
|
say "Found ${APP_DIR}/"
|
||||||
|
|
||||||
########################################
|
cd "$APP_DIR"
|
||||||
# Arch detection
|
|
||||||
########################################
|
if [[ ! -f "docker-compose.yml" && ! -f "compose.yaml" && ! -f "compose.yml" ]]; then
|
||||||
case "$(uname -m)" in
|
err "No docker-compose file found in ${APP_DIR}/. Aborting."
|
||||||
x86_64|amd64) ARCH="amd64" ;;
|
|
||||||
aarch64|arm64) ARCH="arm64" ;;
|
|
||||||
armv7l|armv6l) ARCH="arm-6" ;;
|
|
||||||
*)
|
|
||||||
err "Unsupported architecture: $(uname -m)"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
BINARY="forgejo-${VERSION}-linux-${ARCH}"
|
|
||||||
BASE_URL="https://codeberg.org/forgejo/forgejo/releases/download/v${VERSION}"
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Sanity checks before touching anything
|
|
||||||
########################################
|
|
||||||
if ! systemctl list-unit-files "${SERVICE_NAME}.service" >/dev/null 2>&1; then
|
|
||||||
warn "systemd unit '${SERVICE_NAME}.service' not found — will still attempt but stop/start may fail."
|
|
||||||
fi
|
|
||||||
|
|
||||||
WORKDIR="$(mktemp -d)"
|
|
||||||
cleanup() { rm -rf "$WORKDIR"; }
|
|
||||||
trap cleanup EXIT
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Download
|
|
||||||
########################################
|
|
||||||
section "Downloading Forgejo ${VERSION} (${ARCH})"
|
|
||||||
|
|
||||||
if ! run_with_spinner "Downloading binary..." \
|
|
||||||
curl -fL --retry 3 --retry-delay 2 -o "${WORKDIR}/${BINARY}" "${BASE_URL}/${BINARY}"; then
|
|
||||||
err "Download failed. Check the version number and your network connection."
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
say "Downloaded ${BINARY}"
|
|
||||||
|
|
||||||
########################################
|
########################################
|
||||||
# Checksum verification (best-effort; Forgejo publishes .sha256 sidecars)
|
# Update
|
||||||
########################################
|
########################################
|
||||||
section "Verifying checksum"
|
section "Stopping Forgejo & Database"
|
||||||
|
docker compose down
|
||||||
|
say "Stopped"
|
||||||
|
|
||||||
if curl -fsSL -o "${WORKDIR}/${BINARY}.sha256" "${BASE_URL}/${BINARY}.sha256" 2>/dev/null; then
|
section "Pulling Latest Images"
|
||||||
( cd "$WORKDIR" && sha256sum -c "${BINARY}.sha256" ) || {
|
docker compose pull
|
||||||
err "Checksum verification FAILED. Refusing to install a corrupted/tampered binary."
|
say "Images updated"
|
||||||
exit 1
|
|
||||||
}
|
section "Starting Forgejo & Database"
|
||||||
say "Checksum verified"
|
docker compose up -d
|
||||||
|
say "Forgejo is back up"
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Cleanup
|
||||||
|
########################################
|
||||||
|
section "Cleanup"
|
||||||
|
|
||||||
|
if confirm_default_no "Remove old/unused Docker images, containers, and cache?"; then
|
||||||
|
docker image prune -af
|
||||||
|
docker container prune -f
|
||||||
|
docker builder prune -af
|
||||||
|
say "Old Docker cache cleared"
|
||||||
else
|
else
|
||||||
warn "No checksum file found upstream — skipping verification."
|
warn "Skipping cleanup"
|
||||||
if ! confirm_default_no "Continue without checksum verification?"; then
|
|
||||||
err "Aborted by user."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
chmod +x "${WORKDIR}/${BINARY}"
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Validate the binary actually runs before going anywhere near the service
|
|
||||||
########################################
|
|
||||||
section "Validating binary"
|
|
||||||
if ! "${WORKDIR}/${BINARY}" --version >/dev/null 2>&1; then
|
|
||||||
err "Downloaded binary failed to execute (--version check). Aborting before touching the running service."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
say "Binary runs OK: $("${WORKDIR}/${BINARY}" --version 2>&1 | head -n1)"
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Backup current binary
|
|
||||||
########################################
|
|
||||||
BACKUP=""
|
|
||||||
if [[ -f "$INSTALL_PATH" ]]; then
|
|
||||||
BACKUP="${INSTALL_PATH}.bak.$(date +%Y%m%d%H%M%S)"
|
|
||||||
$SUDO cp -a "$INSTALL_PATH" "$BACKUP"
|
|
||||||
say "Backed up existing binary to ${BACKUP}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Confirm before disrupting the service
|
|
||||||
########################################
|
|
||||||
section "Ready to install"
|
|
||||||
echo " Version: ${VERSION}"
|
|
||||||
echo " Path: ${INSTALL_PATH}"
|
|
||||||
echo " Service: ${SERVICE_NAME}"
|
|
||||||
if ! confirm_default_no "Stop '${SERVICE_NAME}', replace the binary, and restart?"; then
|
|
||||||
err "Aborted by user. Nothing was changed."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
########################################
|
|
||||||
# Stop, swap (atomically), start
|
|
||||||
########################################
|
|
||||||
section "Installing"
|
|
||||||
|
|
||||||
$SUDO systemctl stop "${SERVICE_NAME}" || warn "Failed to stop ${SERVICE_NAME} (continuing anyway)"
|
|
||||||
|
|
||||||
# `install` does an atomic rename into place rather than an in-place copy,
|
|
||||||
# so there's never a half-written binary on disk.
|
|
||||||
$SUDO install -m 0755 "${WORKDIR}/${BINARY}" "$INSTALL_PATH"
|
|
||||||
say "Installed new binary to ${INSTALL_PATH}"
|
|
||||||
|
|
||||||
$SUDO systemctl start "${SERVICE_NAME}" || true
|
|
||||||
|
|
||||||
section "Verifying service"
|
|
||||||
sleep 2
|
|
||||||
if $SUDO systemctl is-active --quiet "${SERVICE_NAME}"; then
|
|
||||||
say "${SERVICE_NAME} is active and running"
|
|
||||||
else
|
|
||||||
err "${SERVICE_NAME} failed to start after update!"
|
|
||||||
if [[ -n "$BACKUP" ]]; then
|
|
||||||
warn "Rolling back to previous binary..."
|
|
||||||
$SUDO install -m 0755 "$BACKUP" "$INSTALL_PATH"
|
|
||||||
$SUDO systemctl restart "${SERVICE_NAME}" || true
|
|
||||||
if $SUDO systemctl is-active --quiet "${SERVICE_NAME}"; then
|
|
||||||
say "Rollback successful — service restored to previous version"
|
|
||||||
else
|
|
||||||
err "Rollback also failed — manual intervention required. Backup at: ${BACKUP}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
err "No backup was available to roll back to. Check 'systemctl status ${SERVICE_NAME}' and journalctl."
|
|
||||||
fi
|
|
||||||
exit 1
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
section "Done"
|
section "Done"
|
||||||
say "Forgejo updated to ${VERSION}"
|
say "Forgejo update complete."
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue