diff --git a/bin/forgejo_latest.sh b/bin/forgejo_latest.sh new file mode 100755 index 0000000..d501a5c --- /dev/null +++ b/bin/forgejo_latest.sh @@ -0,0 +1,157 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2164,SC2181 +# +# Upgrade forgejo binary +# - run this under sudo as it optionally replaces and restarts forgejo +# - "forgejo" is a symlink to the numbered codeberg download binary +# - allows for quick rollback if needed +# - e.g.: 'ln -s forgejo-7.0.5-linux-amd64 forgejo' +# +# Exit codes +# 0 = Success (already newest or upgrade worked) +# 1 = curl failed to download new version +# 2 = sha256sum check failed on download +# 3 = "forgejo" wasn't a symlink +# 4 = upgrade version check failed, forgejo not restarted +# 5 = could not change directory to run sha256sum +# 6 = cannot determine forgejo version info +# 20 = not running under sudo (see FJO_SUDO) +# 21 = forgejo upgraded but not restarted (see FJO_HUP) +# 22 = forgejo upgrade downloaded only (see FJO_LINK) +# +# Requires: curl, awk, grep, sha256sum +# Debug: bash -x /path/to/script.sh +# +# SPDX-License-Identifier: MIT + +_VERSION="0.0.1" + +# symlink name used to run forgejo (e.g. "forgejo") +FJO_SYM="forgejo" +# where the binary is located (e.g. "/usr/local/bin") +FJO_DIR="/var/xyzzy/bin" +# 0 = verbose status, 1 = silent and rely on exit codes +FJO_QUIET=0 + +# require running this script under sudo, 0 to disable +FJO_SUDO=1 +# restart forgejo using FJO_CMD, 0 to disable +FJO_HUP=0 +# replace symlink, 0 to disable (download only, implies FJO_HUP=0) +FJO_LINK=1 +# command to restart forgejo (e.g. "systemctl restart forgejo") +FJO_CMD="systemctl restart forgejo" + +# codeberg API endpoint to get latest version +FJO_API="https://codeberg.org/api/v1/repos/forgejo/forgejo/releases/latest" +# codeberg download base URL to prepend with version info +FJO_DLB="https://codeberg.org/forgejo/forgejo/releases/download" +# architecture being used, matches download name +FJO_ARCH="linux-amd64" + +function noise() { + if [[ ${FJO_QUIET} -eq 0 ]]; then + echo "$*" + fi +} + +if [[ ${FJO_SUDO} -eq 1 ]]; then + if [[ $(id -u) -ne 0 ]]; then + noise "Run this script as root (sudo)" + exit 20 + fi +fi + +# disable curl download progress if in quiet mode +_COPT="" +if [[ ${FJO_QUIET} -eq 1 ]]; then + _COPT+=" -s" +fi + +noise "Checking forgejo..." + +# get installed version +_LOCAL="" +if [[ -L "${FJO_DIR}/${FJO_SYM}" ]]; then + # The version is reported with the gitea base, e.g. "7.0.4+gitea-1.21.11" + # The API / downloads do not include this extra metadata + _LOCAL=$("${FJO_DIR}/${FJO_SYM}" -version | awk '{print $3}') + _LOCAL=${_LOCAL%+*} +else + noise "Forgejo symlink is missing" + exit 3 +fi +# get latest version, strip leading "v" (v1.55.1 -> 1.55.1) +## payload example: {"id":2149345,"tag_name":"v7.0.5",... +_REMOTE=$(curl -X 'GET' -H 'accept: application/json' -s "${FJO_API}" \ + | grep -Po '"tag_name":"\K.*?(?=")') +_REMOTE=${_REMOTE#v} + +# API failed, can't run local binary, etc. - something went wrong +if [[ -z "${_LOCAL}" || -z "${_REMOTE}" ]]; then + noise "Cannot determine forgejo version information" + exit 6 +fi + +# bash doesn't see versions as numbers, but as strings +if [[ "${_LOCAL}" != "${_REMOTE}" ]]; then + _FJO_NAME="forgejo-${_REMOTE}-${FJO_ARCH}" + noise "Upgrading forgejo - installed ${_LOCAL}, latest ${_REMOTE}" + # curl will handle a failure being able to write to output dir, etc. + # shellcheck disable=SC2086 + curl ${_COPT} -L --output-dir "${FJO_DIR}" --remote-name-all \ + "${FJO_DLB}/v${_REMOTE}/${_FJO_NAME}" \ + "${FJO_DLB}/v${_REMOTE}/${_FJO_NAME}.sha256" + if [[ $? -eq 0 ]]; then + # downloads were successful and written to disk + pushd "$(pwd)" >/dev/null + cd "${FJO_DIR}" || (noise "Cannot cd to ${FJO_DIR}"; exit 5) + noise "Checking sha256sum..." + sha256sum --status -c "${_FJO_NAME}.sha256" + if [[ $? -eq 0 ]]; then + # sha256sum check passed + chmod +x "${_FJO_NAME}" + if [[ ${FJO_LINK} -eq 1 ]]; then + # user requested replacing symlink + if [[ -h "${FJO_SYM}" ]]; then + noise "Replacing symlink..." + rm -f "${FJO_SYM}" + ln -s "${_FJO_NAME}" "${FJO_SYM}" + # trust, but verify + _FJO_NEW=$("${FJO_DIR}/${FJO_SYM}" -version | awk '{print $3}') + _FJO_NEW=${_FJO_NEW%+*} + if [[ "${_FJO_NEW}" == "${_REMOTE}" ]]; then + noise "Forgejo binary/symlink upgraded to ${_FJO_NEW}" + if [[ ${FJO_HUP} -eq 1 ]]; then + # user requested restart + noise "Restarting forgejo..." + ${FJO_CMD} + else + noise "Forgejo needs restarted" + exit 21 + fi + else + noise "Upgrade failed, not restarting forgejo" + exit 4 + fi + else + noise "${FJO_SYM} is not a symlink, not overwriting" + exit 3 + fi + else + noise "Forgejo ${_FJO_NAME} downloaded, ready to upgrade" + exit 22 + fi + else + noise "Download of ${_FJO_NAME} failed sha256sum, not upgrading" + exit 2 + fi + popd >/dev/null + else + noise "Download of ${_FJO_NAME} and sha256 failed, not upgrading" + exit 1 + fi +else + noise "Installed forgejo is the latest - ${_LOCAL}" +fi +