#!/bin/sh # Voidd installer for macOS and Linux. # curl -fsSL https://voidd.dev/install.sh | sh # Reads download/latest.json, picks the file for this OS and CPU, verifies its SHA-256, installs it. # Env (for tests): VOIDD_BASE (download URL), VOIDD_APPS_DIR (macOS, default /Applications), VOIDD_OS, VOIDD_ARCH. set -eu BASE="${VOIDD_BASE:-https://voidd.dev/download}" say() { printf '==> %s\n' "$*"; } die() { printf 'error: %s\n' "$*" >&2; exit 1; } usage() { cat <<'EOF' Voidd installer (macOS, Linux) curl -fsSL https://voidd.dev/install.sh | sh macOS: copies Voidd.app to /Applications. Linux: installs the .deb with apt when available, otherwise puts an AppImage in ~/.local/bin. Every download is checked against the SHA-256 in latest.json before anything is installed. EOF } sha256_of() { 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 } # Prints "os kind arch name sha256" per file. latest.json is machine-written (one JSON.stringify), so field order is fixed. list_files() { tr -d '\n' | sed 's/}/}\ /g' | sed -n 's/.*"os": *"\([^"]*\)".*"kind": *"\([^"]*\)".*"arch": *"\([^"]*\)".*"name": *"\([^"]*\)".*"sha256": *"\([^"]*\)".*/\1 \2 \3 \4 \5/p' } main() { case "${1:-}" in -h|--help) usage; exit 0 ;; "") ;; *) die "unknown option: $1 (try --help)" ;; esac command -v curl >/dev/null 2>&1 || die "curl is required" os="${VOIDD_OS:-}" if [ -z "$os" ]; then case "$(uname -s)" in Darwin) os=mac ;; Linux) os=linux ;; *) die "unsupported system: $(uname -s). On Windows use the PowerShell command at https://voidd.dev/introduction.html#install" ;; esac fi arch="${VOIDD_ARCH:-}" if [ -z "$arch" ]; then case "$(uname -m)" in arm64|aarch64) arch=arm64 ;; x86_64|amd64) arch=x64 ;; *) die "unsupported CPU: $(uname -m)" ;; esac # A shell running under Rosetta reports x86_64 on Apple silicon; install the native build. if [ "$os" = mac ] && [ "$arch" = x64 ] && [ "$(sysctl -n hw.optional.arm64 2>/dev/null || true)" = 1 ]; then arch=arm64; fi fi work="$(mktemp -d)" trap 'rm -rf "$work"' EXIT INT TERM say "Reading $BASE/latest.json" curl -fsSL "$BASE/latest.json" -o "$work/latest.json" || die "could not download $BASE/latest.json" files="$(list_files < "$work/latest.json")" version="$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' "$work/latest.json" | head -1)" pick() { printf '%s\n' "$files" | awk -v o="$os" -v k="$1" -v a="$arch" '$1==o && $2==k && $3==a { print $4, $5; exit }'; } kind=dmg if [ "$os" = linux ]; then kind=appimage if command -v apt-get >/dev/null 2>&1 && command -v dpkg >/dev/null 2>&1 && [ -n "$(pick deb)" ]; then kind=deb; fi fi choice="$(pick "$kind")" [ -n "$choice" ] || die "no $kind build for $os/$arch in latest.json" name="${choice% *}" want="${choice#* }" say "Downloading Voidd ${version:-} ($name)" curl -fL --progress-bar "$BASE/$name" -o "$work/$name" || die "download failed" say "Verifying SHA-256" got="$(sha256_of "$work/$name")" [ "$got" = "$want" ] || die "checksum mismatch for $name (expected $want, got $got). Nothing was installed." case "$kind" in dmg) apps="${VOIDD_APPS_DIR:-/Applications}" mnt="$work/mnt" mkdir -p "$mnt" say "Mounting disk image" hdiutil attach -nobrowse -readonly -quiet -mountpoint "$mnt" "$work/$name" || die "could not mount $name" if [ ! -d "$mnt/Voidd.app" ]; then hdiutil detach -quiet "$mnt" || true; die "Voidd.app not found in the disk image"; fi say "Installing to $apps" rm -rf "$apps/Voidd.app" ditto "$mnt/Voidd.app" "$apps/Voidd.app" || { hdiutil detach -quiet "$mnt" || true; die "could not copy the app to $apps (try running with write access to it)"; } hdiutil detach -quiet "$mnt" || true # curl does not add the quarantine flag, so Gatekeeper will not prompt; clearing it also covers files copied from a browser download. xattr -cr "$apps/Voidd.app" 2>/dev/null || true say "Done. Open Voidd from $apps. Gatekeeper will not prompt for this install." ;; deb) say "Installing with apt (sudo will ask for your password)" sudo apt-get install -y "$work/$name" || die "apt install failed" say "Done. Launch Voidd from your application menu." ;; appimage) bin="$HOME/.local/bin" apps="$HOME/.local/share/applications" mkdir -p "$bin" "$apps" cp "$work/$name" "$bin/voidd.AppImage" chmod +x "$bin/voidd.AppImage" cat > "$apps/voidd.desktop" <