#!/bin/bash # Loadout installer — https://loadout.works # Installs the single-file CLI to ~/.loadout/bin and puts `loadout` on your PATH. # Read this script first if you like: curl -fsSL https://loadout.works/install set -euo pipefail BASE="https://loadout.works" BIN_DIR="$HOME/.loadout/bin" say() { printf ' %s\n' "$*"; } fail() { printf 'loadout install: %s\n' "$*" >&2; exit 1; } [ "$(uname)" = "Darwin" ] || fail "macOS only for now (Windows/Linux are on the roadmap)" command -v python3 >/dev/null || fail "python3 is required — install Xcode Command Line Tools first: xcode-select --install" # Pick a Python that has scrypt + can host cryptography (Apple's /usr/bin/python3 has neither). GOOD_PY="" FALLBACK_PY="" for CAND in "$(command -v python3)" /opt/homebrew/bin/python3 /usr/local/bin/python3 \ /Library/Frameworks/Python.framework/Versions/Current/bin/python3; do [ -x "$CAND" ] || continue "$CAND" -c 'import hashlib; hashlib.scrypt' >/dev/null 2>&1 || continue # prefer a Python whose TLS trust store works out of the box if "$CAND" -c 'import ssl; assert ssl.create_default_context().cert_store_stats()["x509_ca"] > 0' >/dev/null 2>&1; then GOOD_PY="$CAND"; break fi [ -n "$FALLBACK_PY" ] || FALLBACK_PY="$CAND" done # a scrypt-capable Python with an empty CA store still works: the CLI loads # certificates from the system bundle itself [ -n "$GOOD_PY" ] || GOOD_PY="$FALLBACK_PY" [ -n "$GOOD_PY" ] || fail "no suitable Python found — install one with: brew install python3 (Apple's /usr/bin/python3 lacks scrypt)" mkdir -p "$HOME/.loadout" printf '%s\n' "$GOOD_PY" > "$HOME/.loadout/python" PYV=$("$GOOD_PY" -c 'import sys; print("%d.%d" % sys.version_info[:2])') say "using $GOOD_PY ($PYV)" mkdir -p "$BIN_DIR" say "downloading the CLI (one readable Python file)…" curl -fsSL "$BASE/cli" -o "$BIN_DIR/loadout.py" "$GOOD_PY" -m py_compile "$BIN_DIR/loadout.py" || fail "downloaded file failed to compile — please try again or report this" cat > "$BIN_DIR/loadout" </dev/null)" [ -x "\$PY" ] || PY=python3 exec "\$PY" "$BIN_DIR/loadout.py" "\$@" WRAP chmod +x "$BIN_DIR/loadout" # The cryptography package is needed for backup/restore encryption. if ! "$GOOD_PY" -c 'import cryptography' 2>/dev/null; then say "installing the 'cryptography' package (used for AES-256-GCM)…" "$GOOD_PY" -m pip install --quiet --user cryptography 2>/dev/null \ || "$GOOD_PY" -m pip install --quiet --user --break-system-packages cryptography \ || say "warning: could not install 'cryptography' automatically — run: pip3 install cryptography" fi # PATH wiring (idempotent). SHELL_RC="$HOME/.zshrc" case "${SHELL:-}" in */bash) SHELL_RC="$HOME/.bash_profile";; esac if ! command -v loadout >/dev/null 2>&1; then if ! grep -qs 'loadout/bin' "$SHELL_RC" 2>/dev/null; then printf '\n# Loadout CLI\nexport PATH="$HOME/.loadout/bin:$PATH"\n' >> "$SHELL_RC" say "added ~/.loadout/bin to PATH in ${SHELL_RC/#$HOME/~}" fi fi say "" say "installed ✔ (~/.loadout/bin/loadout)" # Build the native Mac app (locally compiled -> no Gatekeeper quarantine). APP_OK=0 if command -v swiftc >/dev/null 2>&1; then say "building the Loadout Mac app (about 30 seconds)..." if "$BIN_DIR/loadout" app --no-launch >/dev/null 2>&1; then APP_OK=1 say "installed Loadout.app" else say "note: app build skipped; run 'loadout app' later" fi fi say "" if [ "$APP_OK" = 1 ]; then say "next step: open Loadout from Spotlight (or your Applications folder)" else say "next step: run 'loadout' — your control panel opens in the browser" fi say "then click Start free — 30 days, no card. Everything else is buttons." say "" say "terminal fans: open a NEW terminal, then 'loadout --help' lists every command"