#!/usr/bin/env bash # L0b Factory Bootstrap — Linux/macOS # Auto-generated from install-log.md field-test 2026-05-06 set -euo pipefail GITEA_DEFAULT="https://git.kzntsv.site" ORG_DEFAULT="OpeItcLoc03" FACTORY_REPO_DEFAULT="factory" HOME_TOML="$HOME/.config/factory/home.toml" PROJECTS_DIR_DEFAULT="$HOME/projects" step() { echo -e "\n▶ $*"; } ok() { echo "✓ $*"; } skip() { echo "⊘ $*"; } gap() { echo "⚠ $*" >&2; } check_cmd() { command -v "$1" &>/dev/null } # Pre-checks step "L0b Pre-checks" missed=() check_cmd git || missed+=("git (install: brew install git / apt install git)") check_cmd curl || missed+=("curl (install: brew install curl / apt install curl)") check_cmd mise || missed+=("mise (install: curl https://mise.run | bash)") check_cmd claude || missed+=("claude (install: curl https://claude.ai/install.sh | bash)") if [[ ${#missed[@]} -gt 0 ]]; then gap "Missing dependencies:" for m in "${missed[@]}"; do echo " - $m" done echo "" echo "Install missing tools and re-run bootstrap." exit 1 fi ok "Pre-checks passed" # Check if already bootstrapped if [[ -f "$HOME_TOML" ]]; then skip "Already bootstrapped. Re-run mode: verify only." if [[ $(grep -oP "projects_dir\s*=\s*'\K[^']+" "$HOME_TOML" 2>/dev/null) ]]; then existing_dir=$(grep -oP "projects_dir\s*=\s*'\K[^']+" "$HOME_TOML") echo " projects_dir: $existing_dir" if [[ -d "$existing_dir/.factory" ]]; then ok ".factory exists" else gap ".factory missing at $existing_dir/.factory" exit 1 fi fi ok "Bootstrap verified. Exiting (no-op)." exit 0 fi # Interactive: ask for configuration step "Configuration" read -rp "Gitea URL [$GITEA_DEFAULT]: " gitea gitea=${gitea:-$GITEA_DEFAULT} read -rp "Gitea org/user [$ORG_DEFAULT]: " org org=${org:-$ORG_DEFAULT} read -rp "Factory repo name [$FACTORY_REPO_DEFAULT]: " factoryRepo factoryRepo=${factoryRepo:-$FACTORY_REPO_DEFAULT} read -rp "Projects directory [$PROJECTS_DIR_DEFAULT]: " projectsDir projectsDir=${projectsDir:-$PROJECTS_DIR_DEFAULT} read -rp "AI client [claude-code/copilot-cli/gemini-cli] (default: claude-code): " client client=${client:-claude-code} # Validate projects_dir if [[ ! -d "$projectsDir" ]]; then read -rp "Directory '$projectsDir' does not exist. Create? [Y/n] " create if [[ "$create" != "n" && "$create" != "N" ]]; then mkdir -p "$projectsDir" ok "Created $projectsDir" else gap "Cannot continue without projects_dir" exit 1 fi fi # Clone .factory step "Cloning .factory repo" factoryLocal="$projectsDir/.factory" factoryUrl="$gitea/$org/$factoryRepo.git" if [[ -d "$factoryLocal" ]]; then skip ".factory already exists, skipping clone" if [[ ! -d "$factoryLocal/.git" ]]; then gap "$factoryLocal exists but is not a git repo. Remove it and re-run." exit 1 fi else echo "Cloning from: $factoryUrl" # Check if we have credentials for gitea need_auth=false if ! git ls-remote "$factoryUrl" &>/dev/null; then need_auth=true fi if [[ "$need_auth" == true ]]; then echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo " PAT REQUIRED" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "1. Open in browser: $gitea/user/settings/applications" echo "2. Generate PAT with scopes: read:repository, write:repository, read:user" echo "3. Press Enter when ready..." echo "" read -r echo "Cloning with Git Credential Manager prompt..." fi git clone "$factoryUrl" "$factoryLocal" || { gap "Clone failed. Check URL and credentials." exit 1 } ok "Cloned to $factoryLocal" fi # Write home.toml step "Writing ~/.config/factory/home.toml" configDir="$HOME/.config/factory" mkdir -p "$configDir" installed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ") cat > "$configDir/home.toml" <