Created
September 7, 2026 13:36
-
-
Save quonic/7996547a47e935faeb82f6466ef122e8 to your computer and use it in GitHub Desktop.
Forgejo update script for a binary install based on Forgejo's documented install steps.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # | |
| # update-forgejo.sh | |
| # | |
| # Updates a binary installation of Forgejo to the latest release. | |
| # | |
| # Assumptions: | |
| # - Forgejo is already installed and running (or at least was installed at some point). | |
| # - The binary lives at FORGEJO_BIN (default: /usr/local/bin/forgejo). | |
| # - The config file is at FORGEJO_INI (default: /etc/forgejo/app.ini). | |
| # - Managed by systemd as the "forgejo" service. | |
| # - Running as root (or with sudo) so we can manage the service and replace the binary. | |
| # | |
| # Usage: | |
| # sudo ./update-forgejo.sh | |
| # | |
| # Environment overrides: | |
| # FORGEJO_BIN Path to the forgejo binary (default: /usr/local/bin/forgejo) | |
| # FORGEJO_INI Path to app.ini (default: /etc/forgejo/app.ini) | |
| # FORGEJO_SVC systemd unit name (default: forgejo) | |
| # ARCH Target architecture (default: amd64) | |
| # | |
| set -euo pipefail | |
| # --------------------------------------------------------------------------- | |
| # Configuration | |
| # --------------------------------------------------------------------------- | |
| FORGEJO_BIN="${FORGEJO_BIN:-/usr/local/bin/forgejo}" | |
| FORGEJO_INI="${FORGEJO_INI:-/etc/forgejo/app.ini}" | |
| FORGEJO_SVC="${FORGEJO_SVC:-forgejo}" | |
| ARCH="${ARCH:-amd64}" | |
| RELEASE_BASE="https://code.forgejo.org/forgejo/forgejo/releases" | |
| # --------------------------------------------------------------------------- | |
| # Helpers | |
| # --------------------------------------------------------------------------- | |
| die() { | |
| echo "ERROR: $*" >&2 | |
| exit 1 | |
| } | |
| log() { | |
| echo "==> $*" | |
| } | |
| require_root() { | |
| if [[ $(id -u) -ne 0 ]]; then | |
| die "This script must be run as root (try: sudo $0)" | |
| fi | |
| } | |
| require_cmd() { | |
| command -v "$1" &>/dev/null || die "Required command '$1' not found in PATH." | |
| } | |
| # --------------------------------------------------------------------------- | |
| # Pre-flight checks | |
| # --------------------------------------------------------------------------- | |
| require_root | |
| require_cmd curl | |
| require_cmd systemctl | |
| # Ensure the binary exists at the expected path. | |
| if [[ ! -x "$FORGEJO_BIN" ]]; then | |
| die "Forgejo binary not found or not executable at: $FORGEJO_BIN | |
| If you installed it elsewhere, set FORGEJO_BIN and re-run: | |
| FORGEJO_BIN=/path/to/forgejo sudo $0" | |
| fi | |
| # Sanity-check the config file. | |
| if [[ ! -f "$FORGEJO_INI" ]]; then | |
| die "Config file not found at: $FORGEJO_INI | |
| If your config lives elsewhere, set FORGEJO_INI and re-run: | |
| FORGEJO_INI=/path/to/app.ini sudo $0" | |
| fi | |
| log "Current installation:" | |
| log " Binary : $FORGEJO_BIN" | |
| log " Config : $FORGEJO_INI" | |
| log " Service: $FORGEJO_SVC" | |
| CURRENT_VER=$("$FORGEJO_BIN" -c "$FORGEJO_INI" -v 2>/dev/null || echo "unknown") | |
| log " Version: $CURRENT_VER" | |
| # --------------------------------------------------------------------------- | |
| # Determine the latest release tag via the /releases/latest redirect | |
| # --------------------------------------------------------------------------- | |
| log "Resolving latest release tag from $RELEASE_BASE/latest ..." | |
| # -o /dev/null : don't save body | |
| # -w '%{url_effective}' : print the final (post-redirect) URL | |
| # -L : follow redirects | |
| # -s : silent | |
| # -I : HEAD request (we only need the Location header) | |
| LATEST_URL=$(curl -sL -o /dev/null -w '%{url_effective}' -I \ | |
| "${RELEASE_BASE}/latest/") | |
| # The effective URL should look like: | |
| # https://code.forgejo.org/forgejo/forgejo/releases/tag/v9.0.1 | |
| # Extract the tag (last path segment). | |
| TAG=$(basename "$LATEST_URL") | |
| # Strip a leading 'v' if present → v9.0.1 → 9.0.1 | |
| VERSION="${TAG#v}" | |
| if [[ -z "$TAG" || "$TAG" == "$RELEASE_BASE" ]]; then | |
| die "Could not resolve latest release tag. Raw URL: $LATEST_URL" | |
| fi | |
| log "Latest release tag: $TAG (version $VERSION)" | |
| log "Current version : $CURRENT_VER" | |
| # Short-circuit if we're already on the latest. | |
| if [[ "$VERSION" == *"${CURRENT_VER#Forgejo }"* ]]; then | |
| log "Already up to date. Nothing to do." | |
| exit 0 | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Build the download URL | |
| # --------------------------------------------------------------------------- | |
| # Forgejo release assets are named: forgejo-<version>-linux-<arch> | |
| ASSET="forgejo-${VERSION}-linux-${ARCH}" | |
| DOWNLOAD_URL="${RELEASE_BASE}/download/${TAG}/${ASSET}" | |
| log "Downloading: $DOWNLOAD_URL" | |
| TMP_BIN=$(mktemp /tmp/forgejo-update.XXXXXX) | |
| trap 'rm -f "$TMP_BIN"' EXIT | |
| curl -fSL --progress-bar -o "$TMP_BIN" "$DOWNLOAD_URL" \ | |
| || die "Download failed for $DOWNLOAD_URL" | |
| # --------------------------------------------------------------------------- | |
| # Verify the downloaded binary | |
| # --------------------------------------------------------------------------- | |
| chmod +x "$TMP_BIN" | |
| if ! "$TMP_BIN" -c "$FORGEJO_INI" -v &>/dev/null; then | |
| die "The downloaded binary failed its sanity check (could not run -v). Aborting." | |
| fi | |
| log "New binary passes sanity check." | |
| # --------------------------------------------------------------------------- | |
| # Stop the service | |
| # --------------------------------------------------------------------------- | |
| log "Stopping $FORGEJO_SVC service ..." | |
| if systemctl is-active --quiet "$FORGEJO_SVC"; then | |
| systemctl stop "$FORGEJO_SVC" | |
| else | |
| log " Service is not currently running; skipping stop." | |
| fi | |
| log "Installing new binary to $FORGEJO_BIN" | |
| install -m 0755 -o root -g root "$TMP_BIN" "$FORGEJO_BIN" | |
| rm -f "$TMP_BIN" | |
| trap - EXIT # no longer need cleanup | |
| # --------------------------------------------------------------------------- | |
| # Start the service | |
| # --------------------------------------------------------------------------- | |
| log "Starting $FORGEJO_SVC service ..." | |
| systemctl start "$FORGEJO_SVC" | |
| # Give it a moment, then verify. | |
| sleep 2 | |
| if systemctl is-active --quiet "$FORGEJO_SVC"; then | |
| log "Service started successfully." | |
| else | |
| # Roll back | |
| die "Service failed to start after update. Rolling back. | |
| Check journal: systemctl status $FORGEJO_SVC && journalctl -u $FORGEJO_SVC -n 50" | |
| fi | |
| # --------------------------------------------------------------------------- | |
| # Done | |
| # --------------------------------------------------------------------------- | |
| NEW_VER=$("$FORGEJO_BIN" -c "$FORGEJO_INI" -v 2>/dev/null || echo "unknown") | |
| log "Update complete: $CURRENT_VER → $NEW_VER" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment