#!/bin/sh # keelbox installer: https://get.keelbox.dev # # curl -fsSL https://get.keelbox.dev | sh # curl -fsSL https://get.keelbox.dev | sh -s -- --version v0.1.0 --dir /usr/local/bin # # Installs the keelbox binary for this machine (Linux or macOS, amd64 or # arm64) into ~/.local/bin without sudo. The binary is checked against the # release's SHA256SUMS; when cosign is installed, SHA256SUMS is first # verified as signed by keelbox's release workflow. Safe to rerun. # # Downloads come from wherever the channel index (stable.json) says releases # live. Environment: KEELBOX_VERSION and KEELBOX_INSTALL_DIR are defaults for # --version and --dir; KEELBOX_INDEX_URL points at another index (a mirror or # a test server). set -eu index_url=${KEELBOX_INDEX_URL:-https://get.keelbox.dev/stable.json} signer=https://github.com/keelbox/keelbox/.github/workflows/release.yml@refs/heads/main issuer=https://token.actions.githubusercontent.com # Only HTTPS, redirects included, unless the index itself is plain HTTP. proto='=https' case $index_url in http://*) proto='=https,http' ;; esac usage='usage: install.sh [--version vX.Y.Z] [--dir DIR]' say() { printf 'keelbox: %s\n' "$*"; } die() { printf 'keelbox: %s\n' "$*" >&2; exit 1; } fetch() { # url dest if command -v curl >/dev/null 2>&1; then curl -fsSL --proto "$proto" --retry 3 -o "$2" "$1" elif command -v wget >/dev/null 2>&1; then wget -q -O "$2" "$1" else die "need curl or wget" fi } sha256() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1 else die "need sha256sum or shasum to verify the download" fi } platform() { case $(uname -s) in Linux) os=linux ;; Darwin) os=darwin ;; *) die "unsupported OS $(uname -s); keelbox runs on Linux and macOS" ;; esac case $(uname -m) in x86_64 | amd64) arch=amd64 ;; aarch64 | arm64) arch=arm64 ;; *) die "unsupported architecture $(uname -m)" ;; esac # A shell under Rosetta reports x86_64 on Apple silicon; install native. if [ "$os" = darwin ] && [ "$arch" = amd64 ] && [ "$(sysctl -n sysctl.proc_translated 2>/dev/null)" = 1 ]; then arch=arm64 fi echo "$os-$arch" } main() { version=${KEELBOX_VERSION:-} dir=${KEELBOX_INSTALL_DIR:-$HOME/.local/bin} while [ $# -gt 0 ]; do case $1 in --version) [ $# -ge 2 ] || die "--version needs a value"; version=$2; shift 2 ;; --dir) [ $# -ge 2 ] || die "--dir needs a value"; dir=$2; shift 2 ;; -h | --help) echo "$usage"; exit 0 ;; *) die "unknown option $1; $usage" ;; esac done tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT INT TERM fetch "$index_url" "$tmp/index.json" field() { sed -n "s/^ *\"$1\": *\"\([^\"]*\)\".*/\1/p" "$tmp/index.json"; } releases=$(field releases) [ -n "$releases" ] || die "no releases URL in $index_url" if [ -z "$version" ]; then version=$(field version) [ -n "$version" ] || die "no version in $index_url" fi case $version in v*) ;; *) version=v$version ;; esac plat=$(platform) target=$dir/keelbox if [ -x "$target" ] && "$target" version 2>/dev/null | grep -q "^keelbox $version "; then say "keelbox $version is already installed at $target" exit 0 fi say "downloading keelbox $version for $plat" base=$releases/$version fetch "$base/SHA256SUMS" "$tmp/SHA256SUMS" if command -v cosign >/dev/null 2>&1; then fetch "$base/SHA256SUMS.sigstore.json" "$tmp/SHA256SUMS.sigstore.json" cosign verify-blob --bundle "$tmp/SHA256SUMS.sigstore.json" \ --certificate-identity "$signer" --certificate-oidc-issuer "$issuer" \ "$tmp/SHA256SUMS" >/dev/null 2>&1 || die "SHA256SUMS for $version is not signed by keelbox's release workflow" say "signature verified (cosign)" fi want=$(awk -v f="keelbox-$plat" '$2 == f { print $1 }' "$tmp/SHA256SUMS") [ -n "$want" ] || die "release $version has no keelbox-$plat binary" fetch "$base/keelbox-$plat" "$tmp/keelbox" got=$(sha256 "$tmp/keelbox") [ "$got" = "$want" ] || die "checksum mismatch for keelbox-$plat: got $got, want $want" say "checksum verified" chmod 0755 "$tmp/keelbox" mkdir -p "$dir" # Copy beside the target, then rename: a running keelbox is never half-written. cp "$tmp/keelbox" "$dir/.keelbox.new" mv -f "$dir/.keelbox.new" "$target" say "installed $("$target" version)" say "to $target" # shellcheck disable=SC2016 # the printed $PATH is for the user's shell case :$PATH: in *:"$dir":*) ;; *) printf '\n%s is not on your PATH. Add it:\n export PATH="%s:$PATH"\n' "$dir" "$dir" ;; esac cat <