Monorepo for Aesthetic.Computer aesthetic.computer
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 221 lines 9.5 kB view raw
1#!/bin/bash 2# Bootstrap a lightweight Mac (Apple Silicon or Intel) to run aesthetic-computer 3# natively — outside the devcontainer — with all `ac-*` fish commands available. 4# 5# Matches plans/MAC-NATIVE-DEVENV-PLAN.md. Safe to re-run (idempotent). 6# Prereqs: macOS 11+, Xcode CLT (git), the aesthetic-computer repo checked 7# out at $HOME/aesthetic-computer, the vault at $HOME/aesthetic-computer/aesthetic-computer-vault. 8# 9# This script does NOT unlock the vault — do that manually with 10# `fish aesthetic-computer-vault/vault-tool.fish unlock` before or after. 11 12set -euo pipefail 13 14AC_ROOT="$HOME/aesthetic-computer" 15VAULT="$AC_ROOT/aesthetic-computer-vault" 16ASKPASS="/tmp/ac-askpass.sh" 17SUDOERS_FILE="/etc/sudoers.d/claude-ac-setup" 18 19step() { printf "\n\033[1;34m▶ %s\033[0m\n" "$*"; } 20ok() { printf " \033[1;32m✓\033[0m %s\n" "$*"; } 21warn() { printf " \033[1;33m!\033[0m %s\n" "$*"; } 22die() { printf " \033[1;31m✗\033[0m %s\n" "$*"; exit 1; } 23 24[[ "$(uname)" == "Darwin" ]] || die "this script is macOS-only" 25[[ -d "$AC_ROOT" ]] || die "aesthetic-computer repo not found at $AC_ROOT" 26 27# ----------------------------------------------------------------------------- 28step "1. GUI askpass helper for sudo -A" 29# ----------------------------------------------------------------------------- 30cat > "$ASKPASS" <<'EOF' 31#!/bin/bash 32/usr/bin/osascript -e 'display dialog "Bootstrap needs sudo — enter your password:" default answer "" with hidden answer with icon caution' -e 'text returned of result' 2>/dev/null 33EOF 34chmod +x "$ASKPASS" 35export SUDO_ASKPASS="$ASKPASS" 36sudo -A -v 37ok "sudo primed via askpass" 38 39# ----------------------------------------------------------------------------- 40step "2. Homebrew" 41# ----------------------------------------------------------------------------- 42if ! command -v brew >/dev/null 2>&1 && ! [[ -x /opt/homebrew/bin/brew ]]; then 43 NONINTERACTIVE=1 /bin/bash -c \ 44 "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 45fi 46eval "$(/opt/homebrew/bin/brew shellenv)" 47ok "brew at $(which brew)" 48 49# ----------------------------------------------------------------------------- 50step "3. Brew formulas" 51# ----------------------------------------------------------------------------- 52# Core — needed for fish, node, mkcert, site tooling 53brew install --quiet fish fnm mkcert nss jq ripgrep bat gh tree \ 54 coreutils gnu-sed wget nmap ffmpeg \ 55 caddy ngrok/ngrok/ngrok redis \ 56 stripe/stripe-cli/stripe doctl awscli \ 57 gnupg pinentry-mac 2>&1 | tail -3 58ok "core brew formulas installed" 59 60# ----------------------------------------------------------------------------- 61step "4. /workspaces → /Users/$USER via synthetic.conf" 62# ----------------------------------------------------------------------------- 63if [[ -L /workspaces ]] && [[ "$(readlink /workspaces)" == "/Users/$USER" ]]; then 64 ok "/workspaces already symlinked" 65else 66 # macOS SIP prevents writes to /, but synthetic.conf is the sanctioned way. 67 printf 'workspaces\t/Users/%s\n' "$USER" | sudo -A tee /etc/synthetic.conf >/dev/null 68 sudo -A chmod 644 /etc/synthetic.conf 69 sudo -A /System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t || \ 70 warn "apfs.util trigger failed — a reboot will also apply synthetic.conf" 71 if [[ -L /workspaces ]]; then 72 ok "/workspaces → $(readlink /workspaces)" 73 else 74 warn "/workspaces not yet present; reboot to activate" 75 fi 76fi 77 78# ----------------------------------------------------------------------------- 79step "5. Scoped NOPASSWD sudoers" 80# ----------------------------------------------------------------------------- 81SUDOERS_TMP=$(mktemp) 82cat > "$SUDOERS_TMP" <<EOF 83# Claude Code / aesthetic-computer native dev — narrow NOPASSWD for recurring 84# ops. Review with \`sudo visudo -c\`. 85Cmnd_Alias AC_DEV_SETUP = \\ 86 /usr/bin/tee -a /etc/shells, \\ 87 /usr/bin/security, \\ 88 /opt/homebrew/bin/mkcert 89 90$USER ALL=(root) NOPASSWD: AC_DEV_SETUP 91EOF 92sudo -A visudo -cf "$SUDOERS_TMP" >/dev/null 93sudo -A install -o root -g wheel -m 0440 "$SUDOERS_TMP" "$SUDOERS_FILE" 94rm -f "$SUDOERS_TMP" 95ok "sudoers file installed at $SUDOERS_FILE" 96 97# ----------------------------------------------------------------------------- 98step "6. fnm + Node (lts-jod & 20.5.0)" 99# ----------------------------------------------------------------------------- 100eval "$(fnm env --shell bash)" 101fnm install lts-jod 102fnm install 20.5.0 103fnm default lts-jod 104fnm use lts-jod 105ok "node $(node --version) via fnm ($(fnm current))" 106 107# ----------------------------------------------------------------------------- 108step "7. Global npm CLIs (incl. Claude Code)" 109# ----------------------------------------------------------------------------- 110npm i -g --silent \ 111 @anthropic-ai/claude-code \ 112 @devcontainers/cli \ 113 netlify-cli \ 114 prettier typescript typescript-language-server \ 115 concurrently kill-port http-server npm-check-updates 2>&1 | tail -1 116ok "npm globals installed" 117 118# Native Claude Code binary (matches Dockerfile:223) 119if ! [[ -x "$HOME/.local/bin/claude" ]]; then 120 curl -fsSL https://claude.ai/install.sh | bash >/dev/null 121fi 122ok "claude native: $("$HOME/.local/bin/claude" --version | head -1)" 123 124# ----------------------------------------------------------------------------- 125step "8. fish as default login shell" 126# ----------------------------------------------------------------------------- 127if ! grep -q "/opt/homebrew/bin/fish" /etc/shells; then 128 echo "/opt/homebrew/bin/fish" | sudo -n tee -a /etc/shells >/dev/null 129fi 130CURRENT_SHELL=$(dscl . -read "/Users/$USER" UserShell | awk '{print $2}') 131if [[ "$CURRENT_SHELL" != "/opt/homebrew/bin/fish" ]]; then 132 sudo -A /usr/bin/dscl . -change "/Users/$USER" UserShell "$CURRENT_SHELL" /opt/homebrew/bin/fish 133fi 134ok "login shell: $(dscl . -read /Users/$USER UserShell | awk '{print $2}')" 135 136# ----------------------------------------------------------------------------- 137step "9. ~/.config/fish/config.fish" 138# ----------------------------------------------------------------------------- 139mkdir -p "$HOME/.config/fish/conf.d" "$HOME/.config/fish/functions" 140if ! [[ -f "$HOME/.config/fish/config.fish" ]] || \ 141 ! grep -q "$AC_ROOT/.devcontainer/config.fish" "$HOME/.config/fish/config.fish"; then 142 cat > "$HOME/.config/fish/config.fish" <<FISHCFG 143# ~/.config/fish/config.fish — native Mac AC dev env 144# Generated by scripts/mac-native-bootstrap.sh 145set -gx PATH /opt/homebrew/bin /opt/homebrew/sbin \$HOME/.local/bin \$PATH 146set -gx AC_ROOT \$HOME/aesthetic-computer 147set -gx AESTHETIC $USER 148if not status is-interactive 149 set -gx nogreet true 150end 151if type -q fnm 152 fnm env --use-on-cd --shell fish | source 153end 154set -gx PAGER cat 155set -gx GIT_PAGER cat 156set -gx MANPAGER cat 157if test -f \$AC_ROOT/.devcontainer/config.fish 158 source \$AC_ROOT/.devcontainer/config.fish 159end 160FISHCFG 161fi 162ok "fish config written" 163 164# ----------------------------------------------------------------------------- 165step "10. GPG agent with pinentry-mac" 166# ----------------------------------------------------------------------------- 167mkdir -p "$HOME/.gnupg" 168chmod 700 "$HOME/.gnupg" 169if ! grep -q pinentry-mac "$HOME/.gnupg/gpg-agent.conf" 2>/dev/null; then 170 cat >> "$HOME/.gnupg/gpg-agent.conf" <<'EOF' 171pinentry-program /opt/homebrew/bin/pinentry-mac 172default-cache-ttl 3600 173max-cache-ttl 7200 174EOF 175fi 176gpgconf --kill gpg-agent >/dev/null 2>&1 || true 177gpgconf --launch gpg-agent 178ok "gpg-agent uses pinentry-mac" 179 180# ----------------------------------------------------------------------------- 181step "11. mkcert CA + localhost dev certs" 182# ----------------------------------------------------------------------------- 183mkcert -install >/dev/null 2>&1 184ok "mkcert CA installed in System keychain" 185cd "$AC_ROOT/ssl-dev" 186if ! [[ -f localhost.pem ]]; then 187 env nogreet=true /opt/homebrew/bin/fish ./ssl-install.fish >/dev/null 2>&1 188fi 189ok "ssl-dev/localhost.pem ($(date -r localhost.pem +%Y-%m-%d))" 190 191# ----------------------------------------------------------------------------- 192step "12. Vault env links" 193# ----------------------------------------------------------------------------- 194# session-server reads .env relative to its own dir 195if [[ -f "$VAULT/session-server/.env" ]]; then 196 ln -sfn "$VAULT/session-server/.env" "$AC_ROOT/session-server/.env" 197 ok "session-server/.env linked from vault" 198else 199 warn "vault locked? $VAULT/session-server/.env missing" 200fi 201# system/.env is loaded by ac-lith if present — no default, optional for local dev 202 203# ----------------------------------------------------------------------------- 204step "13. Smoke test: boot ac-site briefly" 205# ----------------------------------------------------------------------------- 206kill-port 8888 >/dev/null 2>&1 || true 207(cd "$AC_ROOT/lith" && node server.mjs >/tmp/ac-bootstrap-lith.log 2>&1 &) 208LITH_PID=$! 209sleep 4 210if curl -sSI --fail https://localhost:8888/ -o /dev/null 2>/dev/null; then 211 ok "ac-site responds on https://localhost:8888 with trusted cert" 212else 213 warn "ac-site smoke test failed — see /tmp/ac-bootstrap-lith.log" 214fi 215kill "$LITH_PID" 2>/dev/null || true 216kill-port 8888 >/dev/null 2>&1 || true 217 218# ----------------------------------------------------------------------------- 219printf "\n\033[1;32m✓ Bootstrap complete.\033[0m\n" 220printf " Open a new Terminal tab (fish will be the default).\n" 221printf " Run \`ac-help\` to list commands, then \`ac-site\` to boot the site.\n\n"