#!/bin/sh # pqc-spotter installer # # curl -fsSL https://pqc-spotter.qcomply.tech/install.sh | sh # # Downloads a single self-contained Python zipapp and puts a `pqc-spotter` # launcher on your PATH. No dependencies are installed and nothing is compiled. # # Environment: # PQC_SPOTTER_INSTALL_DIR where to install (default: ~/.local/bin, # or /usr/local/bin when running as root) # PQC_SPOTTER_BASE_URL override the download host set -eu BASE_URL="${PQC_SPOTTER_BASE_URL:-https://pqc-spotter.qcomply.tech}" PYZ_URL="${BASE_URL}/pqc-spotter.pyz" SHA_URL="${BASE_URL}/pqc-spotter.pyz.sha256" if [ "$(id -u)" -eq 0 ]; then DEFAULT_DIR="/usr/local/bin" else DEFAULT_DIR="${HOME}/.local/bin" fi INSTALL_DIR="${PQC_SPOTTER_INSTALL_DIR:-$DEFAULT_DIR}" LIB_DIR="${INSTALL_DIR}/../lib/pqc-spotter" say() { printf '%s\n' "$*"; } warn() { printf '%s\n' "$*" >&2; } die() { printf 'error: %s\n' "$*" >&2; exit 1; } # ---- prerequisites ------------------------------------------------------- PYTHON="" for candidate in python3 python; do if command -v "$candidate" >/dev/null 2>&1; then if "$candidate" -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 8) else 1)' 2>/dev/null; then PYTHON="$(command -v "$candidate")" break fi fi done [ -n "$PYTHON" ] || die "Python 3.8 or newer is required but was not found on PATH." if command -v curl >/dev/null 2>&1; then fetch() { curl -fsSL "$1" -o "$2"; } elif command -v wget >/dev/null 2>&1; then fetch() { wget -qO "$2" "$1"; } else die "neither curl nor wget is available." fi # ---- download ------------------------------------------------------------ TMP="$(mktemp -d)" # shellcheck disable=SC2064 trap "rm -rf '$TMP'" EXIT INT TERM say "Downloading pqc-spotter..." fetch "$PYZ_URL" "${TMP}/pqc-spotter.pyz" || die "could not download ${PYZ_URL}" # Verify the checksum when a hashing tool is available. A failed match aborts; # a missing tool only warns, so the install still works on minimal images. if fetch "$SHA_URL" "${TMP}/pqc-spotter.pyz.sha256" 2>/dev/null; then EXPECTED="$(cut -d' ' -f1 < "${TMP}/pqc-spotter.pyz.sha256")" ACTUAL="" if command -v sha256sum >/dev/null 2>&1; then ACTUAL="$(sha256sum "${TMP}/pqc-spotter.pyz" | cut -d' ' -f1)" elif command -v shasum >/dev/null 2>&1; then ACTUAL="$(shasum -a 256 "${TMP}/pqc-spotter.pyz" | cut -d' ' -f1)" fi if [ -n "$ACTUAL" ]; then [ "$ACTUAL" = "$EXPECTED" ] || die "checksum mismatch (expected $EXPECTED, got $ACTUAL)" say "Checksum verified." else warn "warning: no sha256 tool found, skipping checksum verification." fi else warn "warning: checksum file unavailable, skipping verification." fi # ---- install ------------------------------------------------------------- mkdir -p "$INSTALL_DIR" || die "cannot create ${INSTALL_DIR}" mkdir -p "$LIB_DIR" 2>/dev/null || LIB_DIR="$INSTALL_DIR" install -m 0644 "${TMP}/pqc-spotter.pyz" "${LIB_DIR}/pqc-spotter.pyz" 2>/dev/null \ || cp "${TMP}/pqc-spotter.pyz" "${LIB_DIR}/pqc-spotter.pyz" # A launcher rather than a renamed zipapp: this keeps the shebang pointing at # the interpreter we actually validated above. cat > "${INSTALL_DIR}/pqc-spotter" </dev/null || echo 'pqc-spotter')" say "" say " Installed ${VERSION}" say " -> ${INSTALL_DIR}/pqc-spotter" say "" case ":${PATH}:" in *":${INSTALL_DIR}:"*) say " Try it: pqc-spotter ." ;; *) say " ${INSTALL_DIR} is not on your PATH. Add it with:" say "" say " export PATH=\"${INSTALL_DIR}:\$PATH\"" say "" say " Then run: pqc-spotter ." ;; esac say ""