#!/bin/sh set -e # Nadir bootstrap installer. # # Detects the host architecture, fetches the latest release asset from the # Gitea releases API, and installs it as a systemd service: # # curl -fsSL https://:/install.sh | sudo sh # # The binary always comes from the Gitea release (not the source instance), # so the installed copy is guaranteed to be the latest tagged build, # independent of whatever version the serving host happens to be running. # # Everything is wrapped in do_install and invoked on the last line, the same # pattern get.docker.com uses: when piped via `curl | sh`, sh executes while # still receiving bytes from the network. Defining the whole body as a # function before running anything means sh has fully parsed the script # before do_install ever runs. # Where to look for releases. Substituted at request time by the server # (cmd/server/server.go) so the script knows which Gitea instance owns # this deployment. Example: https://gitea.example.com/urania/nadir RELEASE_REPO="__NADIR_RELEASE_REPO__" do_install() { if [ "$(id -u)" -ne 0 ]; then echo "this script must be run as root (try piping through 'sudo sh')" >&2 exit 1 fi if ! command -v curl >/dev/null 2>&1; then echo "curl is required" >&2 exit 1 fi case "$(uname -m)" in x86_64|amd64) arch=amd64 ;; aarch64|arm64) arch=arm64 ;; *) echo "unsupported architecture: $(uname -m) (only amd64 and arm64 are built)" >&2; exit 1 ;; esac api="$RELEASE_REPO/releases/latest" api="${api%/}" # Convert https://host/owner/repo/releases/latest into Gitea API form. # Splits the URL once on '/' between host and path, inserts /api/v1/repos. host=$(echo "$RELEASE_REPO" | awk -F/ '{print $1"//"$3}') path=$(echo "$RELEASE_REPO" | awk -F/ '{print $4"/"$5}') api="$host/api/v1/repos/$path/releases/latest" echo "querying $api ..." asset_url=$(curl -fsSL "$api" \ | grep -o '"browser_download_url":"[^"]*linux-'"$arch"'"' \ | head -n1 \ | cut -d'"' -f4) if [ -z "$asset_url" ]; then echo "no linux-$arch asset found in the latest release at $api" >&2 exit 1 fi echo "downloading $asset_url ..." curl -f --progress-bar -L "$asset_url" -o /usr/local/bin/nadir.tmp asset_name=$(basename "$asset_url") # Verify SHA-256: download the checksums file published alongside the binary # and confirm the hash matches. This catches CDN corruption and (together with # the HTTPS transport) makes tampered binaries detectable. sums_url="$host/api/v1/repos/$path/releases/latest" sums_asset_url=$(curl -fsSL "$sums_url" \ | grep -o '"browser_download_url":"[^"]*sha256sums\.txt"' \ | head -n1 \ | cut -d'"' -f4) if [ -n "$sums_asset_url" ]; then echo "verifying checksum ..." curl -fsSL "$sums_asset_url" -o /tmp/nadir-sha256sums.txt # Extract the expected hash for our asset and compare. expected=$(grep "$asset_name" /tmp/nadir-sha256sums.txt | awk '{print $1}') actual=$(sha256sum /usr/local/bin/nadir.tmp | awk '{print $1}') rm -f /tmp/nadir-sha256sums.txt if [ -z "$expected" ]; then echo "warning: sha256sums.txt does not contain a hash for $asset_name" >&2 echo "proceeding without verification" >&2 elif [ "$expected" != "$actual" ]; then echo "SHA-256 MISMATCH: expected $expected, got $actual" >&2 echo "the downloaded binary may be corrupted or tampered with — aborting" >&2 rm -f /usr/local/bin/nadir.tmp exit 1 else echo "checksum OK ($actual)" fi else echo "warning: no sha256sums.txt in release — skipping verification" >&2 fi mv /usr/local/bin/nadir.tmp /usr/local/bin/nadir chmod +x /usr/local/bin/nadir echo "binary installed at /usr/local/bin/nadir" echo "installing as a systemd service ..." /usr/local/bin/nadir install echo echo "done. check status with: nadir status" } do_install