update forgejo update script
This commit is contained in:
parent
cf61608b21
commit
75feeb3206
1 changed files with 251 additions and 17 deletions
|
|
@ -1,27 +1,261 @@
|
||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
########################################
|
||||||
echo "Usage: $0 <version>"
|
# Forgejo Updater
|
||||||
echo "Example: $0 11.0.10"
|
# curl | bash safe
|
||||||
|
########################################
|
||||||
|
|
||||||
|
TTY="/dev/tty"
|
||||||
|
|
||||||
|
BOLD="\033[1m"
|
||||||
|
GREEN="\033[32m"
|
||||||
|
YELLOW="\033[33m"
|
||||||
|
RED="\033[31m"
|
||||||
|
BLUE="\033[34m"
|
||||||
|
RESET="\033[0m"
|
||||||
|
|
||||||
|
section() { printf "\n${BLUE}${BOLD}==> %s${RESET}\n" "$*"; }
|
||||||
|
say() { printf "${GREEN}✔ %s${RESET}\n" "$*"; }
|
||||||
|
warn() { printf "${YELLOW}⚠ %s${RESET}\n" "$*"; }
|
||||||
|
err() { printf "${RED}✖ %s${RESET}\n" "$*"; }
|
||||||
|
|
||||||
|
have() { command -v "$1" >/dev/null 2>&1; }
|
||||||
|
|
||||||
|
need_tty() {
|
||||||
|
if [[ ! -r "$TTY" || ! -w "$TTY" ]]; then
|
||||||
|
# No TTY (e.g. non-interactive CI) — that's fine as long as -y was passed.
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt() {
|
||||||
|
local msg="$1" out
|
||||||
|
printf "${BOLD}%s${RESET}" "$msg" >"$TTY"
|
||||||
|
IFS= read -r out <"$TTY"
|
||||||
|
printf "%s" "$out"
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_default_no() {
|
||||||
|
local msg="$1" ans
|
||||||
|
if $ASSUME_YES; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if ! need_tty; then
|
||||||
|
err "No TTY and -y not given — refusing to guess on: $msg"
|
||||||
exit 1
|
exit 1
|
||||||
|
fi
|
||||||
|
ans="$(prompt "$msg [y/N]: ")"
|
||||||
|
[[ "${ans,,}" == "y" || "${ans,,}" == "yes" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
require_root_or_sudo() {
|
||||||
|
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
|
||||||
|
if have sudo; then
|
||||||
|
SUDO="sudo"
|
||||||
|
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
|
||||||
|
########################################
|
||||||
|
ASSUME_YES=false
|
||||||
|
INSTALL_PATH="/usr/local/bin/forgejo"
|
||||||
|
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)
|
||||||
|
-p PATH Install path (default: /usr/local/bin/forgejo)
|
||||||
|
-s NAME systemd service name (default: forgejo)
|
||||||
|
|
||||||
|
Example: $0 11.0.10
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while getopts ":yp:s:h" opt; do
|
||||||
|
case "$opt" in
|
||||||
|
y) ASSUME_YES=true ;;
|
||||||
|
p) INSTALL_PATH="$OPTARG" ;;
|
||||||
|
s) SERVICE_NAME="$OPTARG" ;;
|
||||||
|
h) usage; exit 0 ;;
|
||||||
|
*) usage; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
|
VERSION="${1:-}"
|
||||||
|
if [[ -z "$VERSION" ]]; then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
# Strip an accidental leading "v" so both "11.0.10" and "v11.0.10" work.
|
||||||
|
VERSION="${VERSION#v}"
|
||||||
|
|
||||||
VERSION="$1"
|
need_tty || true
|
||||||
|
require_root_or_sudo
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Dependency check
|
||||||
|
########################################
|
||||||
|
section "Checking dependencies"
|
||||||
|
for bin in curl systemctl sha256sum install; do
|
||||||
|
have "$bin" || { err "Required tool '$bin' not found."; exit 1; }
|
||||||
|
done
|
||||||
|
say "All required tools present"
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Arch detection
|
||||||
|
########################################
|
||||||
case "$(uname -m)" in
|
case "$(uname -m)" in
|
||||||
x86_64) ARCH="amd64" ;;
|
x86_64|amd64) ARCH="amd64" ;;
|
||||||
aarch64) ARCH="arm64" ;;
|
aarch64|arm64) ARCH="arm64" ;;
|
||||||
armv7l) ARCH="arm-6" ;;
|
armv7l|armv6l) ARCH="arm-6" ;;
|
||||||
*)
|
*)
|
||||||
echo "Unsupported architecture: $(uname -m)"
|
err "Unsupported architecture: $(uname -m)"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
BINARY="forgejo-${VERSION}-linux-${ARCH}"
|
BINARY="forgejo-${VERSION}-linux-${ARCH}"
|
||||||
|
BASE_URL="https://codeberg.org/forgejo/forgejo/releases/download/v${VERSION}"
|
||||||
|
|
||||||
wget "https://codeberg.org/forgejo/forgejo/releases/download/v${VERSION}/${BINARY}"
|
########################################
|
||||||
chmod +x "$BINARY"
|
# Sanity checks before touching anything
|
||||||
systemctl stop forgejo
|
########################################
|
||||||
cp "$BINARY" /usr/local/bin/forgejo
|
if ! systemctl list-unit-files "${SERVICE_NAME}.service" >/dev/null 2>&1; then
|
||||||
systemctl start forgejo.service
|
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
|
||||||
|
fi
|
||||||
|
say "Downloaded ${BINARY}"
|
||||||
|
|
||||||
|
########################################
|
||||||
|
# Checksum verification (best-effort; Forgejo publishes .sha256 sidecars)
|
||||||
|
########################################
|
||||||
|
section "Verifying checksum"
|
||||||
|
|
||||||
|
if curl -fsSL -o "${WORKDIR}/${BINARY}.sha256" "${BASE_URL}/${BINARY}.sha256" 2>/dev/null; then
|
||||||
|
( cd "$WORKDIR" && sha256sum -c "${BINARY}.sha256" ) || {
|
||||||
|
err "Checksum verification FAILED. Refusing to install a corrupted/tampered binary."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
say "Checksum verified"
|
||||||
|
else
|
||||||
|
warn "No checksum file found upstream — skipping verification."
|
||||||
|
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
|
||||||
|
|
||||||
|
section "Done"
|
||||||
|
say "Forgejo updated to ${VERSION}"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue