#!/usr/bin/env bash # setup-dev-environment.sh # Idempotent bootstrap for local Period Vault development. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" INSTALL_MISSING=0 RUN_CHECKS=0 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' usage() { cat <<'EOF' Usage: ./scripts/setup-dev-environment.sh [--install] [--verify] [--help] Options: --install Attempt safe auto-install for supported tools (Homebrew on macOS). --verify Run post-setup verification commands. --help Show this help. Notes: - Script is idempotent and safe to re-run. - Without --install, the script reports actionable install commands. - It never writes credentials/tokens and does not run privileged commands automatically. EOF } log() { printf "${BLUE}[%s]${NC} %s\n" "setup" "$*"; } ok() { printf "${GREEN}[ok]${NC} %s\n" "$*"; } warn() { printf "${YELLOW}[warn]${NC} %s\n" "$*"; } fail() { printf "${RED}[error]${NC} %s\n" "$*" >&2; } for arg in "$@"; do case "$arg" in --install) INSTALL_MISSING=1 ;; --verify) RUN_CHECKS=1 ;; --help|-h) usage; exit 0 ;; *) fail "Unknown option: $arg" usage exit 1 ;; esac done if [[ ! -x "$PROJECT_ROOT/gradlew" ]]; then fail "Missing executable Gradle wrapper at ./gradlew." exit 1 fi OS="$(uname -s)" IS_MAC=0 IS_LINUX=0 case "$OS" in Darwin) IS_MAC=1 ;; Linux) IS_LINUX=1 ;; *) warn "Unsupported OS: $OS. Script will run checks but skip auto-install." ;; esac declare -a REQUIRED_TOOLS declare -a OPTIONAL_TOOLS declare -a MISSING_REQUIRED declare -a MISSING_OPTIONAL declare -a REMEDIATION_HINTS REQUIRED_TOOLS=(git java) OPTIONAL_TOOLS=(gh act adb emulator avdmanager sdkmanager) if [[ $IS_MAC -eq 1 ]]; then REQUIRED_TOOLS+=(xcodebuild xcrun) fi have_cmd() { command -v "$1" >/dev/null 2>&1 } append_unique_hint() { local hint="$1" local existing for existing in "${REMEDIATION_HINTS[@]:-}"; do if [[ "$existing" == "$hint" ]]; then return 0 fi done REMEDIATION_HINTS+=("$hint") } detect_java_major() { local raw version major raw="$(java -version 2>&1 | head -n 1 || true)" version="$(echo "$raw" | sed -E 's/.*"([0-9]+)(\.[0-9]+.*)?".*/\1/' || true)" if [[ -z "$version" ]]; then echo "0" return 0 fi major="$version" echo "$major" } install_with_brew() { local formula="$1" if ! have_cmd brew; then append_unique_hint "Install Homebrew first: https://brew.sh/" return 1 fi if brew list --formula "$formula" >/dev/null 2>&1; then ok "brew formula '$formula' already installed" return 0 fi log "Installing '$formula' via Homebrew..." if brew install "$formula"; then ok "Installed '$formula'" return 0 fi return 1 } try_install_tool() { local tool="$1" if [[ $INSTALL_MISSING -ne 1 ]]; then return 1 fi if [[ $IS_MAC -eq 1 ]]; then case "$tool" in git) install_with_brew git ;; gh) install_with_brew gh ;; act) install_with_brew act ;; java) install_with_brew openjdk@17 append_unique_hint "If needed, configure JAVA_HOME for JDK 17+: export JAVA_HOME=\$(/usr/libexec/java_home -v 17)" ;; *) return 1 ;; esac return $? fi if [[ $IS_LINUX -eq 1 ]]; then append_unique_hint "Install '$tool' using your distro package manager and re-run this script." fi return 1 } tool_hint() { local tool="$1" if [[ $IS_MAC -eq 1 ]]; then case "$tool" in git|gh|act) echo "brew install $tool" ;; java) echo "brew install openjdk@17 && export JAVA_HOME=\$(/usr/libexec/java_home -v 17)" ;; xcodebuild|xcrun) echo "Install Xcode from the App Store and run: sudo xcodebuild -runFirstLaunch" ;; adb|emulator|avdmanager|sdkmanager) echo "Install Android Studio + Android SDK command-line tools, then add platform-tools/emulator/cmdline-tools/latest/bin to PATH." ;; *) echo "Install '$tool' and ensure it is on PATH." ;; esac return 0 fi if [[ $IS_LINUX -eq 1 ]]; then case "$tool" in git) echo "sudo apt-get install -y git" ;; java) echo "sudo apt-get install -y openjdk-17-jdk" ;; gh) echo "Install GitHub CLI from https://cli.github.com/" ;; act) echo "Install act from https://github.com/nektos/act" ;; *) echo "Install '$tool' using your package manager and add it to PATH." ;; esac return 0 fi echo "Install '$tool' and ensure it is on PATH." } log "Checking local development prerequisites..." for tool in "${REQUIRED_TOOLS[@]}"; do if have_cmd "$tool"; then ok "Found required tool: $tool" else warn "Missing required tool: $tool" if try_install_tool "$tool" && have_cmd "$tool"; then ok "Auto-installed required tool: $tool" else MISSING_REQUIRED+=("$tool") append_unique_hint "$(tool_hint "$tool")" fi fi done for tool in "${OPTIONAL_TOOLS[@]}"; do if have_cmd "$tool"; then ok "Found optional tool: $tool" else warn "Missing optional tool: $tool" if try_install_tool "$tool" && have_cmd "$tool"; then ok "Auto-installed optional tool: $tool" else MISSING_OPTIONAL+=("$tool") append_unique_hint "$(tool_hint "$tool")" fi fi done if have_cmd java; then JAVA_MAJOR="$(detect_java_major)" if [[ "$JAVA_MAJOR" =~ ^[0-9]+$ ]] && [[ "$JAVA_MAJOR" -ge 17 ]]; then ok "Java version is compatible (major=$JAVA_MAJOR)" else fail "Java 17+ is required (detected major=$JAVA_MAJOR)." append_unique_hint "$(tool_hint "java")" if [[ ! " ${MISSING_REQUIRED[*]} " =~ " java " ]]; then MISSING_REQUIRED+=("java") fi fi fi log "Installing git hooks (idempotent)..." "$SCRIPT_DIR/install-hooks.sh" ok "Git hooks configured" echo "" echo "================================================" echo "Setup Summary" echo "================================================" if [[ ${#MISSING_REQUIRED[@]} -eq 0 ]]; then ok "All required prerequisites are available." else fail "Missing required prerequisites: ${MISSING_REQUIRED[*]}" fi if [[ ${#MISSING_OPTIONAL[@]} -eq 0 ]]; then ok "All optional developer tools are available." else warn "Missing optional tools: ${MISSING_OPTIONAL[*]}" fi if [[ ${#REMEDIATION_HINTS[@]} -gt 0 ]]; then echo "" echo "Suggested remediation:" for hint in "${REMEDIATION_HINTS[@]}"; do echo " - $hint" done fi echo "" echo "Verification commands:" echo " - ./gradlew shared:jvmTest" echo " - ./scripts/run-emulator-tests.sh android" echo " - ./scripts/run-emulator-tests.sh ios" echo " - ./scripts/verify.sh" if [[ $RUN_CHECKS -eq 1 ]]; then echo "" log "Running lightweight verification commands..." "$PROJECT_ROOT/gradlew" --version >/dev/null ok "Gradle wrapper check passed" if have_cmd gh; then gh --version >/dev/null ok "GitHub CLI check passed" fi if have_cmd xcrun; then xcrun simctl list devices available >/dev/null ok "iOS simulator listing check passed" fi fi if [[ ${#MISSING_REQUIRED[@]} -gt 0 ]]; then exit 1 fi ok "Developer environment bootstrap completed."