#!/usr/bin/env sh # park installer. # # Usage: # curl -fsSL https://raw.githubusercontent.com/mr-vaibh/park/main/install.sh | sh # curl -fsSL https://raw.githubusercontent.com/mr-vaibh/park/main/install.sh | sh -s -- --bin-dir ~/.local/bin # # What this does: # 1. Detects your OS and architecture. # 2. Downloads the latest release binary from GitHub. # 3. Drops it into /usr/local/bin (or your chosen --bin-dir), creating # it with sudo only if needed. # 4. Verifies that 'park' is on your PATH and prints a one-line next step. # # If no release binary is available for your platform, the script falls back # to 'go install github.com/mr-vaibh/park/cmd/park@latest' so you still get a # working binary if you have Go. set -eu REPO="mr-vaibh/park" BIN_NAME="park" BIN_DIR="/usr/local/bin" VERSION="" while [ $# -gt 0 ]; do case "$1" in --bin-dir) BIN_DIR="$2"; shift 2 ;; --version) VERSION="$2"; shift 2 ;; -h|--help) sed -n '2,18p' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; *) echo "unknown flag: $1" >&2; exit 2 ;; esac done say() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } warn() { printf '\033[1;33m!!\033[0m %s\n' "$*" >&2; } die() { printf '\033[1;31mxx\033[0m %s\n' "$*" >&2; exit 1; } # ---- detect platform ----------------------------------------------------- uname_s=$(uname -s) uname_m=$(uname -m) case "$uname_s" in Linux) os=linux ;; Darwin) os=darwin ;; *) die "unsupported OS: $uname_s (park supports Linux and macOS)" ;; esac case "$uname_m" in x86_64|amd64) arch=amd64 ;; arm64|aarch64) arch=arm64 ;; *) die "unsupported architecture: $uname_m" ;; esac say "platform: ${os}/${arch}" # Honest warning so users aren't surprised. case "${os}/${arch}" in linux/amd64) : # fully supported ;; darwin/arm64) warn "macOS arm64 (Apple Silicon): full park + resume works for" warn "asyncio frameworks (uvicorn, FastAPI, Flask, python http.server)." warn "Go's net/http and libuv-based servers don't survive resume yet —" warn "for those, use 'park release ' to clean up, or use Linux." ;; *) warn "Your platform (${os}/${arch}) does not have a working live" warn "injection layer. The binary installs fine and 'park list' works," warn "but 'park ' will return ErrUnsupported. Patches welcome." ;; esac # ---- resolve version ----------------------------------------------------- if [ -z "$VERSION" ]; then say "resolving latest release..." if command -v curl >/dev/null 2>&1; then VERSION=$(curl -fsSL "https://api.github.com/repos/${REPO}/releases/latest" 2>/dev/null | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1 || true) elif command -v wget >/dev/null 2>&1; then VERSION=$(wget -qO- "https://api.github.com/repos/${REPO}/releases/latest" 2>/dev/null | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1 || true) else die "need either curl or wget" fi fi # ---- try downloading a release binary ------------------------------------ install_from_release() { [ -n "$VERSION" ] || return 1 asset="park_${VERSION#v}_${os}_${arch}.tar.gz" url="https://github.com/${REPO}/releases/download/${VERSION}/${asset}" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT say "downloading ${asset}" if command -v curl >/dev/null 2>&1; then curl -fsSL "$url" -o "${tmp}/${asset}" || return 1 else wget -q "$url" -O "${tmp}/${asset}" || return 1 fi tar -xzf "${tmp}/${asset}" -C "$tmp" || return 1 [ -f "${tmp}/${BIN_NAME}" ] || return 1 install_binary "${tmp}/${BIN_NAME}" } # ---- fallback: go install ------------------------------------------------ install_from_source() { if ! command -v go >/dev/null 2>&1; then die "no release binary found and Go is not installed. Either: 1. Wait until a release is published for ${os}/${arch}, or 2. Install Go (https://go.dev/dl/) and re-run this script." fi say "no prebuilt binary; falling back to: go install github.com/${REPO}/cmd/park@${VERSION:-latest}" GOBIN=$(mktemp -d) export GOBIN go install "github.com/${REPO}/cmd/park@${VERSION:-latest}" install_binary "${GOBIN}/${BIN_NAME}" rm -rf "$GOBIN" } # ---- copy binary into BIN_DIR (with sudo if necessary) ------------------- install_binary() { src="$1" chmod +x "$src" # On macOS, ad-hoc sign with the debugger entitlement so that # task_for_pid() can attach to processes you own. Without this, park # is functionally useless on macOS — every attach fails. We do this # *before* moving to BIN_DIR so we don't need sudo for the codesign # step. if [ "$os" = "darwin" ] && command -v codesign >/dev/null 2>&1; then say "ad-hoc signing for task_for_pid (macOS)" ent=$(mktemp -t park-entitlements.XXXXXX) cat > "$ent" <<'PLIST' com.apple.security.cs.debugger com.apple.security.get-task-allow PLIST if ! codesign --entitlements "$ent" -fs - "$src" 2>/dev/null; then warn "codesign failed; park will probably hit task_for_pid permission errors." warn "you can re-sign manually: codesign --entitlements -fs - $(command -v park)" fi rm -f "$ent" fi if [ ! -d "$BIN_DIR" ]; then mkdir -p "$BIN_DIR" 2>/dev/null || sudo mkdir -p "$BIN_DIR" fi dst="${BIN_DIR}/${BIN_NAME}" if [ -w "$BIN_DIR" ]; then install -m 0755 "$src" "$dst" else say "writing to ${dst} requires sudo" sudo install -m 0755 "$src" "$dst" fi say "installed ${dst}" } if ! install_from_release; then install_from_source fi # ---- post-install path check --------------------------------------------- if ! command -v park >/dev/null 2>&1; then case ":$PATH:" in *":${BIN_DIR}:"*) ;; *) warn "${BIN_DIR} is not on your PATH. Add it with one of:" warn " echo 'export PATH=\"\$PATH:${BIN_DIR}\"' >> ~/.zshrc && source ~/.zshrc" warn " echo 'export PATH=\"\$PATH:${BIN_DIR}\"' >> ~/.bashrc && source ~/.bashrc" ;; esac fi say "done. try: park --help" if [ "$os" = "linux" ] && [ "$arch" = "amd64" ]; then echo echo " one-time: lower yama ptrace_scope so park can attach to non-descendants" echo " echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope" fi