update forgejo update script now that its docker

This commit is contained in:
Sebastian Cabrera 2026-07-08 16:29:51 -04:00
parent d8e62067ba
commit 75dda8f900
Signed by: okseby
GPG key ID: 2DDBFDEE356CF3DE

View file

@ -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."
########################################
# 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
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}" say "Docker Compose detected: $(docker compose version --short 2>/dev/null || docker compose version)"
######################################## if [[ ! -d "$APP_DIR" ]]; then
# Checksum verification (best-effort; Forgejo publishes .sha256 sidecars) err "Directory '${APP_DIR}' not found in $(pwd). Aborting."
########################################
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 exit 1
} fi
say "Checksum verified" say "Found ${APP_DIR}/"
cd "$APP_DIR"
if [[ ! -f "docker-compose.yml" && ! -f "compose.yaml" && ! -f "compose.yml" ]]; then
err "No docker-compose file found in ${APP_DIR}/. Aborting."
exit 1
fi
########################################
# Update
########################################
section "Stopping Forgejo & Database"
docker compose down
say "Stopped"
section "Pulling Latest Images"
docker compose pull
say "Images updated"
section "Starting Forgejo & Database"
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."