#!/bin/sh # Installs the peep CLI. # # curl -fsSL https://peep.noise.cam/install | sh # # PEEP_VERSION version to install, for example 0.2.2 or v0.2.2 (default: latest) # PEEP_INSTALL install directory (default: ~/.local/bin) # # Release layout, all under https://peep.noise.cam: # /releases/latest.json {"version": "X.Y.Z", ...} # /releases/vX.Y.Z/peep--.tar.gz one file, `peep` # /releases/vX.Y.Z/SHA256SUMS ` peep--.tar.gz` # is linux or darwin; is x64 or arm64. # # The download is verified against SHA256SUMS before anything is installed; # the installer refuses to continue if no SHA-256 tool is available. # # PEEP_BASE_URL (mirrors and tests) replaces https://peep.noise.cam. It must be # https://, or http://localhost / http://127.0.0.1 / file:// for local testing. set -eu die() { echo "peep: $*" >&2 exit 1 } # The whole script is wrapped in a function so a truncated download never runs # a partial command sequence. main() { base="${PEEP_BASE_URL:-https://peep.noise.cam}" base="${base%/}" case "$base" in https://?*) ;; http://localhost | http://localhost[:/]* | http://127.0.0.1 | http://127.0.0.1[:/]* | file://?*) ;; *) die "PEEP_BASE_URL must be an https:// URL" ;; esac case "$(uname -s)" in Linux) os="linux" ;; Darwin) os="darwin" ;; *) die "unsupported operating system $(uname -s)" ;; esac case "$(uname -m)" in x86_64 | amd64) arch="x64" ;; arm64 | aarch64) arch="arm64" ;; *) die "unsupported architecture $(uname -m)" ;; esac # A shell running under Rosetta reports x86_64 on Apple silicon. if [ "$os" = "darwin" ] && [ "$arch" = "x64" ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null || true)" = "1" ]; then arch="arm64" fi if command -v curl >/dev/null 2>&1; then fetch() { curl -fsSL --retry 2 --connect-timeout 15 -o "$2" "$1"; } elif command -v wget >/dev/null 2>&1; then fetch() { wget -q -O "$2" "$1"; } else die "curl or wget is required" fi if command -v sha256sum >/dev/null 2>&1; then sha256() { sha256sum "$1" | cut -d ' ' -f 1; } elif command -v shasum >/dev/null 2>&1; then sha256() { shasum -a 256 "$1" | cut -d ' ' -f 1; } elif command -v openssl >/dev/null 2>&1; then sha256() { openssl dgst -sha256 "$1" | sed 's/^.*= *//'; } else die "sha256sum, shasum, or openssl is required to verify the download" fi dir="${PEEP_INSTALL:-$HOME/.local/bin}" tmp="$(mktemp -d "${TMPDIR:-/tmp}/peep-install.XXXXXX")" || die "could not create a temporary directory" trap 'rm -rf "$tmp"' EXIT trap 'exit 130' INT trap 'exit 143' TERM HUP version="${PEEP_VERSION:-}" version="${version#v}" if [ -z "$version" ]; then echo "Looking up the latest peep release..." fetch "$base/releases/latest.json" "$tmp/latest.json" || die "could not download $base/releases/latest.json" version="$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$tmp/latest.json" | head -n 1)" [ -n "$version" ] || die "$base/releases/latest.json has no version" fi # The version becomes part of a URL and a path; accept only release numbers. case "$version" in *[!0-9A-Za-z.+-]* | "" | -* | .*) die "invalid version '$version'" ;; esac case "$version" in [0-9]*.[0-9]*.[0-9]*) ;; *) die "invalid version '$version'" ;; esac asset="peep-$os-$arch.tar.gz" release="$base/releases/v$version" echo "Downloading peep $version for $os-$arch..." fetch "$release/$asset" "$tmp/$asset" || die "could not download $release/$asset (does version $version exist for $os-$arch?)" fetch "$release/SHA256SUMS" "$tmp/SHA256SUMS" || die "could not download $release/SHA256SUMS" expected="$(awk -v name="$asset" '$2 == name || $2 == "*" name { print $1; exit }' "$tmp/SHA256SUMS")" [ -n "$expected" ] || die "SHA256SUMS has no entry for $asset" actual="$(sha256 "$tmp/$asset")" if [ "$actual" != "$expected" ]; then die "checksum mismatch for $asset: expected $expected, got $actual; nothing was installed" fi # The archive must contain exactly one file, `peep`. listing="$(tar -tzf "$tmp/$asset")" || die "$asset is not a valid archive" [ "$listing" = "peep" ] || [ "$listing" = "./peep" ] || die "$asset has unexpected contents; nothing was installed" mkdir "$tmp/extract" tar -xzf "$tmp/$asset" -C "$tmp/extract" [ -f "$tmp/extract/peep" ] && [ ! -L "$tmp/extract/peep" ] || die "$asset does not contain a peep binary" chmod 755 "$tmp/extract/peep" # Prove the binary runs on this machine before replacing a working install. installed_version="$("$tmp/extract/peep" --version)" || die "the downloaded peep binary does not run on this system" mkdir -p "$dir" || die "could not create $dir" # Stage next to the destination so the final rename is atomic and a running # peep is never overwritten in place. stage="$dir/.peep.$$" cp "$tmp/extract/peep" "$stage" || die "could not write to $dir" chmod 755 "$stage" mv -f "$stage" "$dir/peep" || { rm -f "$stage" die "could not install to $dir/peep" } echo "Installed $installed_version to $dir/peep" case ":$PATH:" in *":$dir:"*) ;; *) echo "Add $dir to your PATH to run peep, for example:" echo " export PATH=\"$dir:\$PATH\"" ;; esac echo "Get started: peep route add tom 3000" } main "$@"