mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 09:15:19 +03:00
Flipped after publishing, so there was never a window where the live installer named a tag that did not exist. Verified before committing: all five assets under the new tag return 200, and the installer was run end to end — it fetched the published binary, checked it against SHA256SUMS, and the thing it ran reported 0.3.0.
130 lines
5.1 KiB
Bash
Executable File
130 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env sh
|
|
# nesdoctor installer — https://doctor.nestri.io/install.sh
|
|
#
|
|
# This file is the source of what that URL serves. It lives in the public
|
|
# repository so that anyone about to pipe it into a shell can read it first:
|
|
#
|
|
# https://github.com/nestrilabs/nestri/blob/dev/apps/nesdoctor/install/install.sh
|
|
#
|
|
# What it does, in order: work out your platform, download the matching
|
|
# nesdoctor binary from GitHub Releases, verify it against the published
|
|
# SHA256SUMS, run it, and delete it. It installs nothing permanently, touches
|
|
# no system directory, and never asks for sudo.
|
|
#
|
|
# What nesdoctor itself does is printed when it starts.
|
|
|
|
set -eu
|
|
|
|
REPO="nestrilabs/nestri"
|
|
|
|
# Pinned, and NOT `releases/latest`. This repository ships product releases as
|
|
# well as this tool, so `latest` is whatever went out most recently -- it
|
|
# resolved to a 2024 release while this was being written, and the day a
|
|
# product release goes out it would move again and every install here would
|
|
# 404. Bump this line when cutting a nesdoctor release; `NESDOCTOR_TAG`
|
|
# overrides it for testing.
|
|
DEFAULT_TAG="nesdoctor-v0.3.0"
|
|
TAG="${NESDOCTOR_TAG:-$DEFAULT_TAG}"
|
|
TMP="$(mktemp -d)"
|
|
trap 'rm -rf "$TMP"' EXIT INT TERM
|
|
|
|
say() { printf '%s\n' "$*" >&2; }
|
|
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
|
|
|
|
# --- platform ---------------------------------------------------------------
|
|
case "$(uname -s)" in
|
|
Linux) os=linux ;;
|
|
Darwin) os=macos ;;
|
|
*) die "unsupported OS: $(uname -s). nesdoctor runs on Linux, macOS and Windows." ;;
|
|
esac
|
|
|
|
case "$(uname -m)" in
|
|
x86_64|amd64) arch=x86_64 ;;
|
|
arm64|aarch64) arch=aarch64 ;;
|
|
*) die "unsupported architecture: $(uname -m)" ;;
|
|
esac
|
|
|
|
case "$os/$arch" in
|
|
linux/x86_64) target=x86_64-unknown-linux-musl ;;
|
|
macos/aarch64) target=aarch64-apple-darwin ;;
|
|
macos/x86_64) target=x86_64-apple-darwin ;;
|
|
linux/aarch64)
|
|
die "no aarch64 Linux build yet. Building from source takes a minute:
|
|
git clone --depth 1 https://github.com/$REPO && cd nestri
|
|
cargo run --release -p nesdoctor" ;;
|
|
*) die "no build for $os/$arch" ;;
|
|
esac
|
|
|
|
ASSET="nesdoctor-$target"
|
|
|
|
# --- fetch ------------------------------------------------------------------
|
|
# curl or wget, whichever is present. -f so an HTML error page is never
|
|
# mistaken for a binary.
|
|
if command -v curl >/dev/null 2>&1; then
|
|
get() { curl -fsSL "$1" -o "$2"; }
|
|
elif command -v wget >/dev/null 2>&1; then
|
|
get() { wget -qO "$2" "$1"; }
|
|
else
|
|
die "need curl or wget"
|
|
fi
|
|
|
|
BASE="https://github.com/$REPO/releases/download/$TAG"
|
|
|
|
say "Downloading nesdoctor ($target)…"
|
|
get "$BASE/$ASSET" "$TMP/$ASSET" || die "download failed: $BASE/$ASSET\n If $TAG is not published yet, that is why."
|
|
|
|
# --- verify -----------------------------------------------------------------
|
|
# A checksum we fetch from the same place as the binary is not a security
|
|
# boundary, and pretending otherwise would be worse than saying so: it catches
|
|
# a truncated or corrupted download, which is the failure that actually
|
|
# happens. The signed-release version of this is a later job.
|
|
if get "$BASE/SHA256SUMS" "$TMP/SHA256SUMS" 2>/dev/null; then
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
have="$(sha256sum "$TMP/$ASSET" | cut -d' ' -f1)"
|
|
elif command -v shasum >/dev/null 2>&1; then
|
|
have="$(shasum -a 256 "$TMP/$ASSET" | cut -d' ' -f1)"
|
|
else
|
|
have=""
|
|
fi
|
|
if [ -n "$have" ]; then
|
|
want="$(grep -F "$ASSET" "$TMP/SHA256SUMS" | cut -d' ' -f1 | head -n1)"
|
|
[ -n "$want" ] || die "no checksum for $ASSET in SHA256SUMS"
|
|
[ "$have" = "$want" ] || die "checksum mismatch — do not run this file
|
|
expected $want
|
|
got $have"
|
|
say "Checksum OK."
|
|
else
|
|
say "No sha256 tool found; skipping verification."
|
|
fi
|
|
else
|
|
say "No SHA256SUMS published; skipping verification."
|
|
fi
|
|
|
|
# --- run --------------------------------------------------------------------
|
|
chmod +x "$TMP/$ASSET"
|
|
say ""
|
|
|
|
# Reopen stdin on the terminal before handing over.
|
|
#
|
|
# This matters more than it looks. When this script is run the documented way
|
|
# -- `curl -fsSL url | sh` -- the shell's stdin *is* the pipe, and the pipe is
|
|
# at end-of-file by the time we get here. nesdoctor inherits that, sees a
|
|
# non-terminal stdin, and correctly skips every question. The result is a run
|
|
# that completes, looks fine, and answers nothing: the exact failure the whole
|
|
# install path exists to avoid, and it would have been invisible in testing
|
|
# because running the script from a file works perfectly.
|
|
#
|
|
# /dev/tty is the controlling terminal regardless of what stdin was piped to.
|
|
# Where there is no terminal at all -- CI, a cron job -- the redirect fails and
|
|
# we run without it, which is the right behaviour rather than a fallback.
|
|
# The probe runs in a subshell on purpose. A failing redirection on `exec` is
|
|
# *fatal* to a non-interactive shell rather than merely non-zero, so testing it
|
|
# inline killed this script outright on any machine without a controlling
|
|
# terminal -- measured, not theorised. The parentheses contain that.
|
|
if [ -e /dev/tty ] && (exec 3</dev/tty) 2>/dev/null; then
|
|
exec "$TMP/$ASSET" "$@" < /dev/tty
|
|
else
|
|
say "(no terminal available, so the questions will be skipped)"
|
|
exec "$TMP/$ASSET" "$@"
|
|
fi
|