#!/usr/bin/env bash # # One-time setup to install stoke via apt on Debian/Ubuntu. # # Adds the heavy-duty Forgejo Debian registry as an APT source (with its # signing key) and installs the stoke package. Safe to re-run; afterwards # stoke upgrades through regular `apt-get upgrade`. # # Usage: # ./scripts/install-apt.sh # FORGE_URL=... OWNER=... ./scripts/install-apt.sh # non-default instance # # Run as root or as a user with sudo. set -euo pipefail FORGE_URL="${FORGE_URL:-https://forgejo.heavyduty.builders}" OWNER="${OWNER:-heavy-duty}" DISTRIBUTION="${DISTRIBUTION:-stable}" COMPONENT="${COMPONENT:-main}" FORGE_USER="${FORGE_USER:-}" FORGE_TOKEN="${FORGE_TOKEN:-}" # Where apt configuration lives; overridable so tests can run against a # throwaway directory instead of the real /etc/apt. APT_ETC="${STOKE_APT_ETC:-/etc/apt}" KEYRING="$APT_ETC/keyrings/forgejo-$OWNER.asc" LIST="$APT_ETC/sources.list.d/forgejo-$OWNER.list" AUTH="$APT_ETC/auth.conf.d/forgejo-$OWNER.conf" if { [ -n "$FORGE_USER" ] && [ -z "$FORGE_TOKEN" ]; } \ || { [ -z "$FORGE_USER" ] && [ -n "$FORGE_TOKEN" ]; }; then echo "error: FORGE_USER and FORGE_TOKEN must be set together" >&2 exit 1 fi SUDO="" if [ "$(id -u)" -ne 0 ]; then command -v sudo >/dev/null 2>&1 || { echo "error: run as root or install sudo" >&2; exit 1; } SUDO="sudo" fi CURL_AUTH=() if [ -n "$FORGE_USER" ] && [ -n "$FORGE_TOKEN" ]; then forge_host="${FORGE_URL#*://}" forge_host="${forge_host%%/*}" $SUDO install -d -m 0755 "$APT_ETC/auth.conf.d" printf 'machine %s\nlogin %s\npassword %s\n' \ "$forge_host" "$FORGE_USER" "$FORGE_TOKEN" \ | $SUDO tee "$AUTH" >/dev/null $SUDO chmod 0600 "$AUTH" CURL_AUTH=(--netrc-file "$AUTH") fi update_only_source() { $SUDO apt-get update \ -o Dir::Etc::sourcelist="$1" \ -o Dir::Etc::sourceparts=/dev/null \ -o APT::Get::List-Cleanup=0 } # stoke needs Node.js >= 22.12 (commander 15), but the distro archives of # Debian 13 (nodejs 20.x) and Ubuntu 24.04 (nodejs 18.x) cannot satisfy # that, which would make `apt-get install stoke` fail with an unmet # dependency. When no configured source offers a new-enough nodejs, add the # NodeSource repository for Node 22 so the dependency resolves. NODE_MIN="22.12" node_candidate_ok() { local candidate # LC_ALL=C: the "Candidate:" label is localized. candidate="$(LC_ALL=C apt-cache policy nodejs 2>/dev/null | sed -n 's/^ Candidate: //p')" [ -n "$candidate" ] && [ "$candidate" != "(none)" ] || return 1 dpkg --compare-versions "${candidate#*:}" ge "$NODE_MIN" } ensure_nodejs_source() { node_candidate_ok && return 0 # The verdict may just be stale package lists — refresh (best effort, a # transient failure of an unrelated source must not abort) and re-check # before adding anything. echo "No apt source seems to provide nodejs >= $NODE_MIN; refreshing apt metadata ..." $SUDO apt-get update || true node_candidate_ok && return 0 local ns_keyring="$APT_ETC/keyrings/nodesource.asc" local ns_list="$APT_ETC/sources.list.d/nodesource.list" if [ -e "$ns_list" ]; then echo "error: even after refreshing apt metadata, no source provides nodejs >= $NODE_MIN," >&2 echo "and $ns_list already exists; refusing to overwrite it." >&2 echo "Point it at a Node >= 22 release (e.g. node_22.x) and re-run." >&2 exit 1 fi echo "Adding NodeSource (Node 22) ..." curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | $SUDO tee "$ns_keyring" >/dev/null echo "deb [signed-by=$ns_keyring] https://deb.nodesource.com/node_22.x nodistro main" \ | $SUDO tee "$ns_list" >/dev/null # tee inherits our umask; apt's unprivileged _apt user must be able to # read these. $SUDO chmod 0644 "$ns_keyring" "$ns_list" update_only_source "$ns_list" node_candidate_ok || { echo "error: still no nodejs >= $NODE_MIN available after adding NodeSource" >&2; exit 1; } } echo "Adding APT source for $FORGE_URL/$OWNER ..." $SUDO install -d -m 0755 "$APT_ETC/keyrings" $SUDO curl "${CURL_AUTH[@]}" -fsSL "$FORGE_URL/api/packages/$OWNER/debian/repository.key" | $SUDO tee "$KEYRING" >/dev/null echo "deb [signed-by=$KEYRING] $FORGE_URL/api/packages/$OWNER/debian $DISTRIBUTION $COMPONENT" \ | $SUDO tee "$LIST" >/dev/null # tee inherits our umask; apt's unprivileged _apt user must be able to # read these. $SUDO chmod 0644 "$KEYRING" "$LIST" # Fail fast with a clear message when the registry has no package published # yet: without a Release file, `apt-get update` would only fail with a # generic "repository does not have a Release file" error. A definitive 404 # is fatal; any other curl outcome (e.g. a network hiccup) is left for # apt-get update to report. RELEASE_URL="$FORGE_URL/api/packages/$OWNER/debian/dists/$DISTRIBUTION/Release" if [ "$($SUDO curl "${CURL_AUTH[@]}" -sSL -o /dev/null -w '%{http_code}' "$RELEASE_URL" || true)" = "404" ]; then echo "error: no stoke package has been published to the $OWNER Debian registry yet" >&2 echo "($RELEASE_URL returned 404)." >&2 echo "Install stoke via npm or manually instead — see the README." >&2 exit 1 fi # Newer apt verifies with sqv (Sequoia), which rejects the signature Forgejo # currently produces for its Debian registry (malformed Ed25519 MPI encoding # in the upstream signing library). Try the properly signed source first so # this heals automatically once the forge is fixed. Only that signature-error # class permits the compatibility fallback; auth, network, and other failures # must leave verification enabled and retain apt's original diagnostic. if update_output="$(update_only_source "$LIST" 2>&1)"; then printf '%s\n' "$update_output" else update_status=$? if ! grep -Eiq \ 'NO_PUBKEY|EXPKEYSIG|BADSIG|signatures? (could not|couldn.t) be verified|signature (verification )?(failed|failure|error|invalid)|repository .*not signed|is not signed' \ <<<"$update_output"; then printf '%s\n' "$update_output" >&2 exit "$update_status" fi echo echo "WARNING: signature verification failed (known Forgejo registry issue" >&2 echo "with sqv-based apt). Falling back to [trusted=yes]; transport" >&2 echo "security is provided by HTTPS to $FORGE_URL." >&2 echo echo "deb [trusted=yes] $FORGE_URL/api/packages/$OWNER/debian $DISTRIBUTION $COMPONENT" \ | $SUDO tee "$LIST" >/dev/null $SUDO chmod 0644 "$LIST" update_only_source "$LIST" fi ensure_nodejs_source $SUDO apt-get install -y stoke echo stoke --version >/dev/null && echo "stoke $(stoke --version) installed. Run: stoke auth login"