#!/bin/sh # Install the windpaint CLI from the GitHub releases of windpaint-ai/cli. # # curl -fsSL https://get.windpaint.ai | sh # curl -fsSL https://get.windpaint.ai | WINDPAINT_VERSION=0.1.0 sh # # Env: # WINDPAINT_VERSION version to install, e.g. 0.1.0 (default: latest release) # WINDPAINT_INSTALL_DIR target directory (default: /usr/local/bin if writable, else ~/.local/bin) set -eu REPO="windpaint-ai/cli" BIN="windpaint" err() { echo "install: $*" >&2; exit 1; } need() { command -v "$1" >/dev/null 2>&1 || err "$1 is required"; } need uname need tar if command -v curl >/dev/null 2>&1; then fetch() { curl -fsSL "$1" -o "$2"; } fetch_stdout() { curl -fsSL "$1"; } final_url() { curl -fsSLI -o /dev/null -w '%{url_effective}' "$1"; } elif command -v wget >/dev/null 2>&1; then fetch() { wget -qO "$2" "$1"; } fetch_stdout() { wget -qO- "$1"; } final_url() { wget -S --spider "$1" 2>&1 | sed -n 's/^ *[Ll]ocation: *\([^ ]*\).*/\1/p' | tail -n1 | tr -d '\r'; } else err "curl or wget is required" fi os=$(uname -s | tr '[:upper:]' '[:lower:]') case "$os" in linux|darwin) ;; *) err "unsupported OS $os (on Windows, download the .zip from https://github.com/$REPO/releases)" ;; esac arch=$(uname -m) case "$arch" in x86_64|amd64) arch=amd64 ;; arm64|aarch64) arch=arm64 ;; *) err "unsupported architecture $arch" ;; esac version="${WINDPAINT_VERSION:-}" if [ -z "$version" ]; then # The releases/latest redirect is not rate limited like the API. latest=$(final_url "https://github.com/$REPO/releases/latest" 2>/dev/null || true) case "$latest" in */releases/tag/*) version="${latest##*/tag/}" ;; esac if [ -z "$version" ]; then version=$(fetch_stdout "https://api.github.com/repos/$REPO/releases/latest" \ | sed -n 's/.*"tag_name": *"v\{0,1\}\([^"]*\)".*/\1/p' | head -n1) fi [ -n "$version" ] || err "could not determine the latest version; set WINDPAINT_VERSION" fi version="${version#v}" archive="${BIN}_${version}_${os}_${arch}.tar.gz" base="https://github.com/$REPO/releases/download/v$version" tmp=$(mktemp -d) trap 'rm -rf "$tmp"' EXIT trap 'exit 130' INT TERM echo "Downloading $BIN $version ($os/$arch)..." >&2 fetch "$base/$archive" "$tmp/$archive" || err "download failed: $base/$archive" fetch "$base/checksums.txt" "$tmp/checksums.txt" || err "download failed: $base/checksums.txt" expected=$(grep " $archive\$" "$tmp/checksums.txt" | cut -d' ' -f1) [ -n "$expected" ] || err "$archive is not listed in checksums.txt" if command -v sha256sum >/dev/null 2>&1; then actual=$(sha256sum "$tmp/$archive" | cut -d' ' -f1) elif command -v shasum >/dev/null 2>&1; then actual=$(shasum -a 256 "$tmp/$archive" | cut -d' ' -f1) else err "sha256sum or shasum is required to verify the download" fi [ "$expected" = "$actual" ] || err "checksum mismatch for $archive" tar -xzf "$tmp/$archive" -C "$tmp" "$BIN" dir="${WINDPAINT_INSTALL_DIR:-}" if [ -z "$dir" ]; then if [ -w /usr/local/bin ]; then dir=/usr/local/bin; else dir="$HOME/.local/bin"; fi fi mkdir -p "$dir" install -m 0755 "$tmp/$BIN" "$dir/$BIN" 2>/dev/null || { cp "$tmp/$BIN" "$dir/$BIN" && chmod 0755 "$dir/$BIN"; } echo "Installed $dir/$BIN" >&2 case ":$PATH:" in *":$dir:"*) ;; *) if [ "$dir" = "$HOME/.local/bin" ]; then echo "Add it to your PATH to run $BIN: export PATH=\"\$HOME/.local/bin:\$PATH\"" >&2 else echo "Add it to your PATH to run $BIN: export PATH=\"$dir:\$PATH\"" >&2 fi ;; esac "$dir/$BIN" version --format text >&2 || true