#!/bin/bash # Little monitoring script to check that the version of a # Forgejo instance is up to date. # # Licence: WTFPL # Copyright: chl-dev@bugness.org 2023 PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' ) REVISION="0.1" # Default values DNS_RECORD_RELEASES="release.forgejo.org" RELEASE_MEDIUM="dns" URL_RSS_RELEASES="https://codeberg.org/forgejo/forgejo/releases.rss" URL_LOCAL_HOSTNAME="localhost:3000" URL_LOCAL_VERSION_TEMPLATE="%s://%s/api/v1/version" URL_LOCAL_PROTOCOL="http" # Include check_range() # Not needed at the moment #. $PROGPATH/utils.sh STATE_OK=0 STATE_WARNING=1 STATE_CRITICAL=2 STATE_UNKNOWN=3 STATE_DEPENDENT=4 # Stop at first uncaught error set -e # Wrapper to use whatever is available to make HTTP queries fetch_with_curl_wget_or_whatever () { if which "wget" >/dev/null 2>&1; then wget -q -O - "$1" elif which "curl" >/dev/null 2>&1 ; then curl -s "$1" else echo "UNKNOWN: no wget/curl/whatever available to make HTTP queries." exit $STATE_UNKNOWN fi } # Wrapper to use whatever is available to make DNS queries fetch_with_dig_or_whatever () { if which "dig" >/dev/null 2>&1; then dig +short "$1" "$2" elif which "nslookup" >/dev/null 2>&1; then nslookup -query="$2" "$1" | sed -n "/$1/p" else echo "UNKNOWN: no dig/nslookup/whatever available to make DNS queries." exit $STATE_UNKNOWN fi } # Sort the multiple contents and print the last one. # ex: 1.21.1-5 # Note: Gitea has some spurious v1.22-dev/-rc releases, we ignore them. get_latest_version_number_from_rss () { fetch_with_curl_wget_or_whatever "$1" | while rdom; do # In RSS streams, we only want the <title> in <item> # In Atom streams, it's <entry> -> <title> case "$E" in "item"|"entry") INSIDE_ITEM=1;; "/item"|"/entry") INSIDE_ITEM="";; esac if [ -n "$INSIDE_ITEM" ] && [ "$E" == "title" ]; then printf "%s\n" "$C" fi done | sed -e 's/^v//' -e '/-dev/d' -e '/-rc/d' | sort --version-sort | tail -n 1 } # Sort the multiple "forgejo_versions=x,y,z" and print the last one. # ex: 1.21.1-5 get_latest_version_number_from_dns () { fetch_with_dig_or_whatever "$1" "TXT" | \ sed -e 's/.*=\(.*\)".*/\1/' -e 's/,/\n/g' | \ sort --version-sort | \ tail -n 1 } print_url_local_version () { printf "$URL_LOCAL_VERSION_TEMPLATE" "$URL_LOCAL_PROTOCOL" "$URL_LOCAL_HOSTNAME" } # Quick 'n dirty function to parse XML without having to install # a real parser (not necessary at the moment here, but may be # needed in the future) # The tag will be stored in $E and the content in $C # note: this function is not POSIX-compliant (needs bash) rdom () { local IFS=\> read -d \< E C } # Help function usage() { cat <<EOF Usage : $0 [-H hostname] [-u local_URL] [-sS] [-D] [-d release_dns_record] [-R] [-r release_rss_URL] -D Use the DNS to determine the last release available (default) -R Use the RSS to determine the last release available -s Use HTTP to query the hostname (default) -S Use HTTPS to query the hostname Example : ./check_forgejo_version.sh -H forge.example.net (seems to somewhat work for Gitea too :) ./check_forgejo_version.sh -R -r https://github.com/go-gitea/gitea/releases.atom Default values: local_URL: $( print_url_local_version ) release_dns_record: $DNS_RECORD_RELEASES release_rss_URL: $URL_RSS_RELEASES EOF } # Loop on parameters while getopts hDH:r:RsSu: f; do case "$f" in 'h') usage exit ;; 'D') RELEASE_MEDIUM="dns" ;; 'H') URL_LOCAL_HOSTNAME="$OPTARG" ;; 'r') URL_RSS_RELEASES="$OPTARG" ;; 'R') RELEASE_MEDIUM="rss" ;; 's') URL_LOCAL_PROTOCOL="http" ;; 'S') URL_LOCAL_PROTOCOL="https" ;; 'u') URL_LOCAL_VERSION="$OPTARG" ;; ?) usage exit 1 ;; esac done # If the local URL hasn't been overridden, let's compute it. if [ -z "$URL_LOCAL_VERSION" ]; then URL_LOCAL_VERSION="$( print_url_local_version )" fi # Query the upstream and local versions... case "$RELEASE_MEDIUM" in 'rss') UPSTREAM_VERSION="$( get_latest_version_number_from_rss "$URL_RSS_RELEASES" )" ;; 'dns') UPSTREAM_VERSION="$( get_latest_version_number_from_dns "$DNS_RECORD_RELEASES" )" ;; esac # note : the API seems to switch 1.21.1-0 to 1.21.1+0. We dumbly switch back ? LOCAL_VERSION="$( fetch_with_curl_wget_or_whatever "$URL_LOCAL_VERSION" | sed -n 's/{"version":"\([^"]\+\)"}/\1/p' | sed 's/+/-/' )" # ...and check if they match. if [ "$UPSTREAM_VERSION" = "$LOCAL_VERSION" ]; then echo "OK ($LOCAL_VERSION / $UPSTREAM_VERSION)" exit $STATE_OK else # we are not yet able to distinguish security releases from simple upgrades # so, to avoid alert fatigue, we chose to stay at "warning". echo "WARNING ($LOCAL_VERSION / $UPSTREAM_VERSION)" exit $STATE_WARNING fi