#!/bin/sh # shellcheck shell=dash # shellcheck disable=SC2039 # local is non-POSIX # This script downloads and installs foundryup, the Foundry toolchain manager. # It detects the platform, downloads the appropriate binary, and runs it. # It runs on Unix shells like {a,ba,da,k,z}sh. It uses the common `local` # extension. Note: Most shells limit `local` to 1 var per line, contra bash. # Some versions of ksh have no `local` keyword. Alias it to `typeset`, but # beware this makes variables global with f()-style function syntax in ksh93. has_local() { # shellcheck disable=SC2034 # deliberately unused local _has_local } has_local 2>/dev/null || alias local=typeset set -eu FOUNDRYUP_REPO="foundry-rs/foundryup" BASE_DIR="${XDG_CONFIG_HOME:-$HOME}" FOUNDRY_DIR="${FOUNDRY_DIR:-$BASE_DIR/.foundry}" FOUNDRYUP_BIN_DIR="$FOUNDRY_DIR/bin" FOUNDRYUP_IGNORE_VERIFICATION="${FOUNDRYUP_IGNORE_VERIFICATION:-false}" usage() { cat </dev/null || printf '%s' "$_payload_b64" | base64 -D 2>/dev/null || true) if [ -n "$_payload_json" ]; then # Extract SHA256 hash from the payload _expected_hash=$(printf '%s' "$_payload_json" | grep -oE '"sha256"[[:space:]]*:[[:space:]]*"[a-fA-F0-9]{64}"' | head -1 | grep -oE '[a-fA-F0-9]{64}') fi rm -f "$_sigstore_file" fi fi rm -f "$_attestation_file" fi if [ -z "$_expected_hash" ]; then warn "no attestation found for this release, skipping verification" fi fi say "downloading foundryup..." ensure mkdir -p "$_dir" ensure downloader "$_url" "$_file" "$_arch" # Verify the downloaded binary against the attestation hash if [ -n "$_expected_hash" ]; then say "verifying binary integrity..." local _actual_hash _actual_hash=$(compute_sha256 "$_file") if [ "$_actual_hash" != "$_expected_hash" ]; then err "hash verification failed: expected: $_expected_hash actual: $_actual_hash Use --force to skip verification (INSECURE)" fi say "binary verified ✓" fi ensure chmod u+x "$_file" if [ ! -x "$_file" ]; then err "cannot execute $_file (likely because of mounting /tmp as noexec). please copy the file to a location where you can execute binaries and run ./foundryup" fi say "installing foundryup to $FOUNDRYUP_BIN_DIR..." ensure mkdir -p "$FOUNDRYUP_BIN_DIR" ensure cp "$_file" "$FOUNDRYUP_BIN_DIR/foundryup" ensure chmod +x "$FOUNDRYUP_BIN_DIR/foundryup" ignore rm "$_file" ignore rmdir "$_dir" post_install } post_install() { say "" say "foundryup was installed successfully!" say "" # Check if bin dir is in PATH case ":$PATH:" in *":$FOUNDRYUP_BIN_DIR:"*) say "Run 'foundryup' to install Foundry." ;; *) say "To get started, add foundryup to your PATH:" say "" say " export PATH=\"\$PATH:$FOUNDRYUP_BIN_DIR\"" say "" say "Then run 'foundryup' to install Foundry." ;; esac } get_architecture() { local _ostype local _cputype _ostype="$(uname -s)" _cputype="$(uname -m)" case "$_ostype" in Linux) if is_musl; then _ostype="alpine" else _ostype="linux" fi ;; Darwin) _ostype="darwin" ;; MINGW* | MSYS* | CYGWIN* | Windows_NT) _ostype="win32" ;; *) err "unsupported OS: $_ostype" ;; esac case "$_cputype" in x86_64 | x64 | amd64) # Check for Rosetta on macOS if [ "$_ostype" = "darwin" ] && is_rosetta; then _cputype="arm64" else _cputype="amd64" fi ;; aarch64 | arm64) _cputype="arm64" ;; *) err "unsupported architecture: $_cputype" ;; esac RETVAL="${_ostype}_${_cputype}" } get_ext() { case "$1" in win32_*) echo ".exe" ;; *) echo "" ;; esac } is_musl() { if [ -f /etc/os-release ]; then grep -qi "alpine" /etc/os-release 2>/dev/null return $? fi return 1 } is_rosetta() { if [ "$(uname -s)" = "Darwin" ]; then if command -v sysctl >/dev/null 2>&1; then [ "$(sysctl -n sysctl.proc_translated 2>/dev/null)" = "1" ] return $? fi fi return 1 } say() { printf 'foundryup-init: %s\n' "$1" } err() { say "$1" >&2 exit 1 } warn() { say "warning: $1" >&2 } need_cmd() { if ! check_cmd "$1"; then err "need '$1' (command not found)" fi } check_cmd() { command -v "$1" > /dev/null 2>&1 } assert_nz() { if [ -z "$1" ]; then err "assert_nz $2" fi } compute_sha256() { if check_cmd sha256sum; then sha256sum "$1" | cut -d' ' -f1 | sed 's/^\\//' elif check_cmd shasum; then shasum -a 256 "$1" | cut -d' ' -f1 else err "need 'sha256sum' or 'shasum' for verification" fi } # Download without exiting on failure (used for optional files like attestations) try_download() { if check_cmd curl; then curl --proto '=https' --tlsv1.2 --silent --fail --location "$1" --output "$2" 2>/dev/null elif check_cmd wget; then wget --https-only --secure-protocol=TLSv1_2 -q "$1" -O "$2" 2>/dev/null else return 1 fi } ensure() { if ! "$@"; then err "command failed: $*" fi } ignore() { "$@" } downloader() { local _dld local _err local _status if check_cmd curl; then _dld=curl elif check_cmd wget; then _dld=wget else _dld='curl or wget' fi if [ "$1" = --check ]; then need_cmd "$_dld" elif [ "$_dld" = curl ]; then _err=$(curl --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1) _status=$? if [ -n "$_err" ]; then warn "$_err" if echo "$_err" | grep -q 404; then err "binary for platform '$3' not found, this may be unsupported" fi fi return $_status elif [ "$_dld" = wget ]; then _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1) _status=$? if [ -n "$_err" ]; then warn "$_err" if echo "$_err" | grep -q '404'; then err "binary for platform '$3' not found, this may be unsupported" fi fi return $_status else err "unknown downloader" fi } main "$@" || exit 1