mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
Both installers fetched from `releases/latest/download`, which is wrong in this repository specifically: it ships product releases as well as this tool, so `latest` is whichever release went out most recently regardless of what it contains. Measured before publishing anything: `releases/latest` resolved to `v0.2.0`, from May 2024, and the asset URL 404'd. Publishing nesdoctor-v0.1.0 would have papered over it by becoming the newest release -- and then the first product release after it would have moved `latest` again and broken every `curl | sh` in the announcement, silently, for everyone, with the tool itself untouched and nothing to point at. Now pinned to a tag that is bumped when a nesdoctor release is cut, with `NESDOCTOR_TAG` still overriding for testing. The download failure message also now names the tag and says outright that an unpublished tag is the likely cause, since that is the one mistake this arrangement invites.
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.1.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
|