Last active
February 10, 2026 02:54
-
-
Save fxstein/10693ecc34c80fb7c4cf056d850bddf1 to your computer and use it in GitHub Desktop.
Pi Mac Mini bootstrap — fresh machine to running Pi instance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # ============================================================ | |
| # PI BOOTSTRAP — Set up a fresh Mac Mini as a Pi instance | |
| # Usage: curl -fsSL <gist-url> | bash | |
| # | |
| # Prerequisites: macOS, admin user, internet | |
| # Everything else is installed by this script. | |
| # ============================================================ | |
| set -euo pipefail | |
| echo "🥧 Pi Mac Mini Bootstrap" | |
| echo "========================" | |
| echo "" | |
| # ============================================================= | |
| # 1. HOMEBREW | |
| # ============================================================= | |
| if command -v brew &>/dev/null; then | |
| echo "✅ Homebrew installed" | |
| else | |
| echo "📦 Installing Homebrew..." | |
| /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" | |
| # Add to PATH for Apple Silicon | |
| if [[ -f /opt/homebrew/bin/brew ]]; then | |
| eval "$(/opt/homebrew/bin/brew shellenv)" | |
| echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$HOME/.zprofile" | |
| fi | |
| fi | |
| echo "" | |
| # ============================================================= | |
| # 2. CORE TOOLS | |
| # ============================================================= | |
| echo "📦 Installing tools..." | |
| TOOLS=(git gh cloudflared 1password-cli chezmoi) | |
| CASKS=(orbstack) | |
| for tool in "${TOOLS[@]}"; do | |
| if command -v "$tool" &>/dev/null; then | |
| echo " ✅ $tool" | |
| else | |
| echo " 📦 $tool..." | |
| brew install "$tool" | |
| fi | |
| done | |
| for cask in "${CASKS[@]}"; do | |
| if brew list --cask "$cask" &>/dev/null 2>&1; then | |
| echo " ✅ $cask" | |
| else | |
| echo " 📦 $cask..." | |
| brew install --cask "$cask" | |
| fi | |
| done | |
| echo "" | |
| # ============================================================= | |
| # 3. ENABLE SSH (Remote Login) | |
| # ============================================================= | |
| echo "🔐 Enabling Remote Login (SSH)..." | |
| if sudo systemsetup -getremotelogin 2>/dev/null | grep -q "On"; then | |
| echo " ✅ Remote Login already enabled" | |
| else | |
| sudo systemsetup -setremotelogin on | |
| echo " ✅ Remote Login enabled" | |
| fi | |
| echo "" | |
| # ============================================================= | |
| # 4. GITHUB AUTH | |
| # ============================================================= | |
| echo "🔑 GitHub authentication..." | |
| if gh auth status &>/dev/null 2>&1; then | |
| echo " ✅ Already authenticated as $(gh api user --jq .login)" | |
| else | |
| echo " Please authenticate with GitHub (need access to pionizer org):" | |
| gh auth login | |
| fi | |
| echo "" | |
| # ============================================================= | |
| # 5. CLONE REPOS | |
| # ============================================================= | |
| echo "📂 Cloning repositories..." | |
| OPENCLAW_DIR="$HOME/openclaw" | |
| PIINFRA_DIR="$HOME/pi-infra" | |
| if [[ -d "$OPENCLAW_DIR/.git" ]]; then | |
| echo " ✅ ~/openclaw exists" | |
| else | |
| echo " 📦 Cloning OpenClaw..." | |
| gh repo clone openclaw/openclaw "$OPENCLAW_DIR" | |
| fi | |
| if [[ -d "$PIINFRA_DIR/.git" ]]; then | |
| echo " ✅ ~/pi-infra exists" | |
| cd "$PIINFRA_DIR" && git pull --quiet | |
| else | |
| echo " 📦 Cloning pi-infra..." | |
| gh repo clone pionizer/pi-infra "$PIINFRA_DIR" | |
| fi | |
| echo "" | |
| # ============================================================= | |
| # 6. INSTALL OPENCLAW CLI | |
| # ============================================================= | |
| echo "🔧 Installing openclaw CLI..." | |
| sudo cp "$PIINFRA_DIR/openclaw-wrapper.sh" /usr/local/bin/openclaw | |
| sudo chmod +x /usr/local/bin/openclaw | |
| echo " ✅ /usr/local/bin/openclaw" | |
| echo "" | |
| # ============================================================= | |
| # 7. SECRETS DIRECTORY | |
| # ============================================================= | |
| echo "📁 Setting up directories..." | |
| mkdir -p "$HOME/.openclaw/secrets" | |
| mkdir -p "$HOME/.openclaw/workspace" | |
| echo " ✅ ~/.openclaw/" | |
| echo "" | |
| # ============================================================= | |
| # 8. 1PASSWORD SERVICE ACCOUNT | |
| # ============================================================= | |
| echo "🔐 1Password setup..." | |
| if [[ -n "${OP_SERVICE_ACCOUNT_TOKEN:-}" ]]; then | |
| echo " ✅ OP_SERVICE_ACCOUNT_TOKEN set" | |
| else | |
| echo "" | |
| echo " ⚠️ OP_SERVICE_ACCOUNT_TOKEN not set." | |
| echo " Get it from 1Password → Settings → Service Accounts → pi-infra" | |
| echo "" | |
| read -p " Paste token (or Enter to skip): " -r OP_TOKEN | |
| if [[ -n "$OP_TOKEN" ]]; then | |
| export OP_SERVICE_ACCOUNT_TOKEN="$OP_TOKEN" | |
| # Add to shell profile | |
| if ! grep -q "OP_SERVICE_ACCOUNT_TOKEN" "$HOME/.zshrc" 2>/dev/null; then | |
| echo "export OP_SERVICE_ACCOUNT_TOKEN=\"$OP_TOKEN\"" >> "$HOME/.zshrc" | |
| echo " ✅ Added to ~/.zshrc" | |
| fi | |
| else | |
| echo " ⏭️ Skipped — run 'openclaw setup' after setting it" | |
| fi | |
| fi | |
| echo "" | |
| # ============================================================= | |
| # 9. CHEZMOI — Generate secrets | |
| # ============================================================= | |
| if [[ -n "${OP_SERVICE_ACCOUNT_TOKEN:-}" ]]; then | |
| echo "🔑 Generating secrets via chezmoi..." | |
| rm -rf "$HOME/.config/chezmoi" "$HOME/.local/share/chezmoi" | |
| cp -r "$PIINFRA_DIR/chezmoi" "$HOME/.local/share/chezmoi" | |
| chezmoi init --force | |
| chezmoi apply --force | |
| echo " ✅ ~/.openclaw/.env generated" | |
| else | |
| echo "⏭️ Skipping chezmoi (no 1Password token)" | |
| fi | |
| echo "" | |
| # ============================================================= | |
| # 10. INSTANCE CONFIGURATION | |
| # ============================================================= | |
| echo "🏷️ Instance setup..." | |
| echo "" | |
| echo " Each Pi instance needs an ID (single letter):" | |
| echo " o = Oliver" | |
| echo " m = Markus" | |
| echo " s = Susanne" | |
| echo "" | |
| read -p " Instance ID [o]: " -r INSTANCE_ID | |
| INSTANCE_ID="${INSTANCE_ID:-o}" | |
| echo "" | |
| # Store for later use + set host prompt | |
| echo "export PI_INSTANCE_ID=\"$INSTANCE_ID\"" >> "$HOME/.zshrc" | |
| if ! grep -q 'o-gluon' "$HOME/.zshrc" 2>/dev/null; then | |
| echo "PROMPT=\"🥧 ${INSTANCE_ID}-gluon %1~ \$ \"" >> "$HOME/.zshrc" | |
| echo " ✅ Shell prompt set: 🥧 ${INSTANCE_ID}-gluon" | |
| fi | |
| # ============================================================= | |
| # 11. BUILD & START | |
| # ============================================================= | |
| echo "🏗️ Building Pi..." | |
| echo "" | |
| # OrbStack needs to be running | |
| if ! docker info &>/dev/null 2>&1; then | |
| echo " ⚠️ Docker not running. Start OrbStack first, then run:" | |
| echo " openclaw build" | |
| echo " openclaw tunnel setup --instance $INSTANCE_ID" | |
| echo "" | |
| else | |
| openclaw build | |
| echo "" | |
| # ============================================================= | |
| # 12. TUNNEL | |
| # ============================================================= | |
| echo "🔐 Setting up Cloudflare tunnel..." | |
| echo "" | |
| echo " ⚠️ This needs a tunnel credential file." | |
| echo " If this is a NEW instance, create a tunnel first:" | |
| echo " cloudflared tunnel create pi-${INSTANCE_ID}" | |
| echo "" | |
| read -p " Run tunnel setup now? [y/N]: " -r | |
| if [[ "$REPLY" =~ ^[Yy]$ ]]; then | |
| openclaw tunnel setup --instance "$INSTANCE_ID" | |
| fi | |
| fi | |
| echo "" | |
| echo "============================================" | |
| echo "✅ Pi bootstrap complete!" | |
| echo "============================================" | |
| echo "" | |
| echo " Instance: ${INSTANCE_ID}" | |
| echo " CLI: openclaw help" | |
| echo " Start: openclaw gateway start" | |
| echo " Build: openclaw build" | |
| echo " SSH: openclaw tunnel setup --instance ${INSTANCE_ID}" | |
| echo "" | |
| echo " If you skipped any steps, run: openclaw setup" | |
| echo "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment