#!/usr/bin/env bash set -euo pipefail echo "🚀 Setting up Enterprise + AI Development Environment (Bash Edition)" echo HOME_DIR="$HOME" BASHRC="$HOME_DIR/.bashrc" BASH_PROFILE="$HOME_DIR/.bash_profile" # Dotfiles remote — your local Gitea server DOTFILES_REMOTE="http://172.27.0.35:3000/kenjim/dotfiles" DOTFILES_DIR="$HOME_DIR/dotfiles" # ------------------------------------------------------ # 0ī¸âƒŖ MACHINE PROFILE SELECTION # ------------------------------------------------------ # Override: MACHINE_PROFILE=work ./setup_enterprise_ai_bash.sh # MACHINE_PROFILE=personal ./setup_enterprise_ai_bash.sh MACHINE_PROFILE="${MACHINE_PROFILE:-}" if [[ -z "$MACHINE_PROFILE" ]]; then echo "Select machine profile:" echo " [1] work — OneDrive cloud, work workspace" echo " [2] personal — ProtonDrive + GoogleDrive, personal workspace" echo read -r -p "Profile (1=work / 2=personal) [2]: " _choice case "${_choice:-2}" in 1|work) MACHINE_PROFILE="work" ;; 2|personal) MACHINE_PROFILE="personal" ;; *) MACHINE_PROFILE="personal" ;; esac fi export MACHINE_PROFILE echo " â–ļ Profile: $MACHINE_PROFILE" echo # Derive hostname tag used to tag commits MACHINE_TAG="$(hostname -s)-${MACHINE_PROFILE}" # ------------------------------------------------------ # 1ī¸âƒŖ CREATE DIRECTORY STRUCTURE # ------------------------------------------------------ echo "📁 Creating directory structure..." mkdir -p "$HOME_DIR/workspace/src/"{personal,work,research} mkdir -p "$HOME_DIR/workspace/"{experiments,notebooks,sandboxes,archive} mkdir -p "$HOME_DIR/data/"{raw,processed,embeddings,synthetic} mkdir -p "$HOME_DIR/models/"{huggingface,ollama,fine-tuned} mkdir -p "$HOME_DIR/infra/"{docker,terraform,scripts} mkdir -p "$HOME_DIR/ops" mkdir -p "$HOME_DIR/scripts" mkdir -p "$HOME_DIR/vault" mkdir -p "$HOME_DIR/dotfiles" mkdir -p "$HOME_DIR/dotfiles/.ssh/keys" # Profile-specific cloud directories if [[ "$MACHINE_PROFILE" == "work" ]]; then # OneDrive is managed by the Microsoft OneDrive app and auto-mounts at: # ~/Library/CloudStorage/OneDrive-/ (modern macOS) # We create a stable symlink at ~/OneDrive for convenience. ONEDRIVE_MOUNT=$(find "$HOME_DIR/Library/CloudStorage" -maxdepth 1 -iname 'OneDrive*' -type d 2>/dev/null | head -1 || true) if [[ -n "$ONEDRIVE_MOUNT" ]]; then ln -sfn "$ONEDRIVE_MOUNT" "$HOME_DIR/OneDrive" 2>/dev/null || true echo " Linked ~/OneDrive → $ONEDRIVE_MOUNT" else mkdir -p "$HOME_DIR/OneDrive" echo " Created ~/OneDrive placeholder (link manually once OneDrive app is signed in)" fi else # Personal machine: ProtonDrive + Google Drive mkdir -p "$HOME_DIR/Cloud/"{ProtonDrive,GoogleDrive} fi echo "✅ Directories created." echo # ------------------------------------------------------ # 2ī¸âƒŖ ENSURE .bash_profile LOADS .bashrc (macOS FIX) # ------------------------------------------------------ if ! grep -q "source ~/.bashrc" "$BASH_PROFILE" 2>/dev/null; then cat <<'EOF' >> "$BASH_PROFILE" # Load .bashrc if it exists if [ -f ~/.bashrc ]; then source ~/.bashrc fi EOF echo "✅ .bash_profile updated to load .bashrc" fi # ------------------------------------------------------ # 3ī¸âƒŖ ADD ENTERPRISE AI ENV VARIABLES # ------------------------------------------------------ if ! grep -q "### ENTERPRISE_AI_ENV ###" "$BASHRC" 2>/dev/null; then cat <<'EOF' >> "$BASHRC" ### ENTERPRISE_AI_ENV ### export WORKSPACE="$HOME/workspace" export DATA_ROOT="$HOME/data" export MODEL_ROOT="$HOME/models" # HuggingFace cache location export HF_HOME="$MODEL_ROOT/huggingface" # Ollama model location export OLLAMA_MODELS="$MODEL_ROOT/ollama" # Convenience aliases alias ws='cd $WORKSPACE' alias src='cd $WORKSPACE/src' alias data='cd $DATA_ROOT' alias models='cd $MODEL_ROOT' # Machine-local overrides (cloud paths, work vs personal — not synced via dotfiles) [ -f ~/.bashrc.local ] && source ~/.bashrc.local EOF echo "✅ Environment variables added to .bashrc" else # Ensure .bashrc.local sourcing is present even on existing installs if ! grep -q ".bashrc.local" "$BASHRC" 2>/dev/null; then cat >> "$BASHRC" <<'LOCALEOF' # Machine-local overrides (cloud paths, work vs personal — not synced via dotfiles) [ -f ~/.bashrc.local ] && source ~/.bashrc.local LOCALEOF echo "✅ Added .bashrc.local sourcing to existing .bashrc" fi echo "â„šī¸ Environment already configured." fi # ---- Write machine-specific .bashrc.local (never committed to dotfiles) ---- BASHRC_LOCAL="$HOME_DIR/.bashrc.local" # Preserve any existing custom content below the managed block LOCAL_CUSTOM="" if [ -f "$BASHRC_LOCAL" ] && grep -q "### MACHINE_LOCAL_END ###" "$BASHRC_LOCAL" 2>/dev/null; then LOCAL_CUSTOM=$(awk '/### MACHINE_LOCAL_END ###/{found=1; next} found{print}' "$BASHRC_LOCAL") fi cat > "$BASHRC_LOCAL" <> "$BASHRC_LOCAL" <<'WORKEOF' # --- Work / OneDrive --- export CLOUD_ROOT="$HOME/OneDrive" export ONEDRIVE_ROOT="$HOME/OneDrive" alias cloud='cd $CLOUD_ROOT' alias onedrive='cd $ONEDRIVE_ROOT' WORKEOF else cat >> "$BASHRC_LOCAL" <<'PERSONALEOF' # --- Personal / ProtonDrive + Google Drive --- export CLOUD_ROOT="$HOME/Cloud" export PROTON_ROOT="$HOME/Cloud/ProtonDrive" export GDRIVE_ROOT="$HOME/Cloud/GoogleDrive" alias cloud='cd $CLOUD_ROOT' alias proton='cd $PROTON_ROOT' alias gdrive='cd $GDRIVE_ROOT' PERSONALEOF fi cat >> "$BASHRC_LOCAL" <<'TAILEOF' ### MACHINE_LOCAL_END ### TAILEOF # Re-append any custom content that was below the managed block if [[ -n "$LOCAL_CUSTOM" ]]; then echo "$LOCAL_CUSTOM" >> "$BASHRC_LOCAL" fi echo "✅ .bashrc.local written for profile: $MACHINE_PROFILE" # ---- NFS-aware placement for work servers ---- # Work servers with NFS-mounted home dirs share ~/.bashrc.local across hosts. # To keep per-machine overrides, store the real file in /opt/kenjim/ and symlink. if [[ "$MACHINE_PROFILE" == "work" ]]; then _is_nfs_home=false case "$(hostname -s)" in etqc-*|etbg-*|engtech-dev-*|qnc-kenjim-toby-shell*|qtaas*|bng-kenjim-toby-shell*|btaas*) if df -P "$HOME" 2>/dev/null | tail -1 | grep -qE ':|nfs'; then _is_nfs_home=true elif mount | grep -q "on ${HOME%%/} .*type nfs"; then _is_nfs_home=true fi ;; esac if $_is_nfs_home; then OPT_DIR="/opt/kenjim" OPT_LOCAL="$OPT_DIR/.bashrc.local" echo "🔗 NFS home detected — relocating .bashrc.local to $OPT_LOCAL" if [ ! -d "$OPT_DIR" ]; then echo " Creating $OPT_DIR ..." sudo mkdir -p "$OPT_DIR" sudo chown "$(id -u):$(id -g)" "$OPT_DIR" chmod 700 "$OPT_DIR" fi # If ~/.bashrc.local is already a symlink (previous run), the cat above # wrote through it into the target. Remove the symlink first so mv # doesn't see source and destination as the same file. if [ -L "$BASHRC_LOCAL" ]; then rm -f "$BASHRC_LOCAL" # Content already landed at the symlink target; just ensure perms chmod 600 "$OPT_LOCAL" else mv "$BASHRC_LOCAL" "$OPT_LOCAL" chmod 600 "$OPT_LOCAL" fi ln -sfn "$OPT_LOCAL" "$BASHRC_LOCAL" echo " Linked: ~/.bashrc.local → $OPT_LOCAL" # VS Code Remote stores large caches in ~/.vscode-server and # ~/.vscode-remote-containers — keep them on local disk, not NFS. for _vsdir in .vscode-server .vscode-remote-containers; do _opt_target="$OPT_DIR/$_vsdir" _home_link="$HOME_DIR/$_vsdir" mkdir -p "$_opt_target" if [ -d "$_home_link" ] && [ ! -L "$_home_link" ]; then # Existing real directory — move contents then replace with symlink echo " Moving ~/$_vsdir → $OPT_DIR/$_vsdir ..." rsync -a "$_home_link/" "$_opt_target/" 2>/dev/null || \ cp -a "$_home_link/." "$_opt_target/" 2>/dev/null || true rm -rf "$_home_link" fi ln -sfn "$_opt_target" "$_home_link" echo " Linked: ~/$_vsdir → $_opt_target" done echo "✅ Per-machine .bashrc.local and VS Code dirs stored outside NFS at $OPT_DIR" fi fi # ------------------------------------------------------ # 4ī¸âƒŖ INITIALIZE DOTFILES REPO (with Gitea remote) # ------------------------------------------------------ echo "đŸ“Ļ Setting up dotfiles repo..." # Copy the dotfiles manager into scripts/ if it isn't already there SCRIPTS_DIR="$HOME_DIR/scripts" DFM="$SCRIPTS_DIR/dotfiles_manager.sh" if [ ! -f "$DFM" ]; then SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [ -f "$SELF_DIR/dotfiles_manager.sh" ]; then cp "$SELF_DIR/dotfiles_manager.sh" "$DFM" chmod +x "$DFM" echo "✅ dotfiles_manager.sh copied to $DFM" else echo "âš ī¸ dotfiles_manager.sh not found next to this script — skipping copy." fi fi # Run init via the manager (handles clone-or-init and remote setup) if [ -f "$DFM" ]; then DOTFILES_DIR="$DOTFILES_DIR" DOTFILES_REMOTE="$DOTFILES_REMOTE" bash "$DFM" init else # Minimal fallback if manager isn't available cd "$DOTFILES_DIR" if [ ! -d ".git" ]; then git init git remote add origin "$DOTFILES_REMOTE" echo ".DS_Store" > .gitignore touch README.md git add . git commit -m "Initial dotfiles commit" echo "✅ Dotfiles repo initialized." else echo "â„šī¸ Dotfiles repo already exists." fi fi echo # ------------------------------------------------------ # 5ī¸âƒŖ ADD DOTFILES ALIASES TO .bashrc # ------------------------------------------------------ if ! grep -q "### DOTFILES_ALIASES ###" "$BASHRC" 2>/dev/null; then cat >> "$BASHRC" < "$HOME_DIR/scripts/bootstrap.sh" #!/usr/bin/env bash # bootstrap.sh — Restore Enterprise AI Environment on this machine set -euo pipefail echo "🔄 Restoring Enterprise AI Environment..." # Reload shell config # shellcheck disable=SC1091 [ -f ~/.bashrc ] && source ~/.bashrc [ -f ~/.bash_profile ] && source ~/.bash_profile echo " WORKSPACE : ${WORKSPACE:-not set}" echo " DATA_ROOT : ${DATA_ROOT:-not set}" echo " MODEL_ROOT : ${MODEL_ROOT:-not set}" echo # Sync latest dotfiles from git server if [ -f "$HOME/scripts/dotfiles_manager.sh" ]; then echo "đŸ“Ļ Syncing dotfiles..." bash "$HOME/scripts/dotfiles_manager.sh" sync else echo "âš ī¸ dotfiles_manager.sh not found — run setup_enterprise_ai_bash.sh first." fi echo echo "✅ Bootstrap complete." EOF chmod +x "$HOME_DIR/scripts/bootstrap.sh" echo "✅ Bootstrap script created at ~/scripts/bootstrap.sh" # ------------------------------------------------------ # 7ī¸âƒŖ INITIAL DOTFILES MIGRATION (interactive) # ------------------------------------------------------ echo read -p "Migrate critical shell & config files into dotfiles repo now? (y/n): " MIGRATE_NOW if [[ "$MIGRATE_NOW" == "y" ]] && [ -f "$DFM" ]; then echo echo "📂 Tracking shell config files..." # .bashrc.local is machine-specific — never track it in dotfiles TRACK_FILES=() [ -f "$BASHRC" ] && TRACK_FILES+=("$BASHRC") [ -f "$BASH_PROFILE" ] && TRACK_FILES+=("$BASH_PROFILE") [ -f "$HOME_DIR/.bash_aliases" ] && TRACK_FILES+=("$HOME_DIR/.bash_aliases") [ -f "$HOME_DIR/.inputrc" ] && TRACK_FILES+=("$HOME_DIR/.inputrc") [ -f "$HOME_DIR/.gitconfig" ] && TRACK_FILES+=("$HOME_DIR/.gitconfig") [ -f "$HOME_DIR/.vimrc" ] && TRACK_FILES+=("$HOME_DIR/.vimrc") [ -f "$HOME_DIR/.tmux.conf" ] && TRACK_FILES+=("$HOME_DIR/.tmux.conf") # Scripts — track so they are part of the dotfiles repo and re-bootstrap any machine [ -f "$SCRIPTS_DIR/dotfiles_manager.sh" ] && TRACK_FILES+=("$SCRIPTS_DIR/dotfiles_manager.sh") [ -f "$SCRIPTS_DIR/setup_enterprise_ai_bash.sh" ] && TRACK_FILES+=("$SCRIPTS_DIR/setup_enterprise_ai_bash.sh") [ -f "$SCRIPTS_DIR/bootstrap.sh" ] && TRACK_FILES+=("$SCRIPTS_DIR/bootstrap.sh") # NOTE: ~/.bashrc.local intentionally excluded — it is machine-specific if [ ${#TRACK_FILES[@]} -gt 0 ]; then DOTFILES_DIR="$DOTFILES_DIR" DOTFILES_REMOTE="$DOTFILES_REMOTE" \ bash "$DFM" add "${TRACK_FILES[@]}" else echo "â„šī¸ No standard config files found to track." fi echo read -p "Set up SSH config and keys now? (y/n): " DO_SSH if [[ "$DO_SSH" == "y" ]]; then DOTFILES_DIR="$DOTFILES_DIR" DOTFILES_REMOTE="$DOTFILES_REMOTE" \ bash "$DFM" ssh-setup echo read -p "GPG-encrypt and store private SSH keys in dotfiles? (y/n): " DO_EXPORT if [[ "$DO_EXPORT" == "y" ]]; then DOTFILES_DIR="$DOTFILES_DIR" DOTFILES_REMOTE="$DOTFILES_REMOTE" \ bash "$DFM" ssh-export fi fi echo read -p "Push initial dotfiles to $DOTFILES_REMOTE now? (y/n): " DO_PUSH if [[ "$DO_PUSH" == "y" ]]; then DOTFILES_DIR="$DOTFILES_DIR" DOTFILES_REMOTE="$DOTFILES_REMOTE" \ bash "$DFM" push "initial dotfiles migration from ${MACHINE_TAG}" fi else echo "â„šī¸ Skipped. Run manually:" echo " dotfiles add ~/.bashrc ~/.bash_profile ~/.gitconfig" echo " dotfiles ssh-setup" echo " dotfiles push" fi # ------------------------------------------------------ # 8ī¸âƒŖ OPTIONAL TIME MACHINE EXCLUSIONS # ------------------------------------------------------ echo read -p "Exclude large AI folders from Time Machine? (y/n): " EXCLUDE_TM if [[ "$EXCLUDE_TM" == "y" ]]; then sudo tmutil addexclusion "$HOME_DIR/data/raw" sudo tmutil addexclusion "$HOME_DIR/models" echo "✅ Time Machine exclusions added." else echo "â„šī¸ Skipped Time Machine exclusions." fi # ------------------------------------------------------ # DONE # ------------------------------------------------------ # ------------------------------------------------------ # 9ī¸âƒŖ REMOTE BOOTSTRAP (optional) # ------------------------------------------------------ echo read -r -p "Bootstrap a remote machine over SSH now? (y/n): " DO_REMOTE if [[ "$DO_REMOTE" == "y" ]] && [ -f "$DFM" ]; then read -r -p "Remote target (user@host): " REMOTE_TARGET read -r -p "Profile for remote machine (work/personal) [personal]: " REMOTE_PROFILE REMOTE_PROFILE="${REMOTE_PROFILE:-personal}" DOTFILES_DIR="$DOTFILES_DIR" DOTFILES_REMOTE="$DOTFILES_REMOTE" \ bash "$DFM" remote-bootstrap "$REMOTE_TARGET" --profile "$REMOTE_PROFILE" else echo "â„šī¸ To bootstrap a remote machine later:" echo " dotfiles remote-bootstrap user@hostname" echo " dotfiles remote-bootstrap user@hostname --profile work" fi # ------------------------------------------------------ # DONE # ------------------------------------------------------ echo echo "🎉 Enterprise + AI Bash Environment Setup Complete!" echo " Profile : $MACHINE_PROFILE" echo " Host : $(hostname -s)" echo echo "👉 Run : source ~/.bash_profile" echo "👉 Spaces : ws | src | data | models" if [[ "$MACHINE_PROFILE" == "work" ]]; then echo "👉 Cloud : onedrive" else echo "👉 Cloud : cloud | proton | gdrive" fi echo "👉 Dotfiles: dotfiles status" echo "👉 Sync : dotfiles sync" echo "👉 SSH keys: dotfiles ssh-setup (then: dotfiles ssh-export)" echo "👉 Remote : dotfiles remote-bootstrap user@personal-mac" echo "👉 Restore : git clone $DOTFILES_REMOTE ~/dotfiles && ~/dotfiles/install.sh" echo