initial dotfiles migration from zet-personal
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
|
||||
# Load .bashrc if it exists
|
||||
if [ -f ~/.bashrc ]; then
|
||||
source ~/.bashrc
|
||||
fi
|
||||
1
.bash_profile
Symbolic link
1
.bash_profile
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/kenjim/dotfiles/dotfiles/.bash_profile
|
||||
159
.bashrc
159
.bashrc
@@ -1,159 +0,0 @@
|
||||
# ~/.bashrc: executed by bash(1) for non-login shells.
|
||||
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
|
||||
# for examples
|
||||
|
||||
# If not running interactively, don't do anything
|
||||
case $- in
|
||||
*i*) ;;
|
||||
*) return;;
|
||||
esac
|
||||
|
||||
# don't put duplicate lines or lines starting with space in the history.
|
||||
# See bash(1) for more options
|
||||
HISTCONTROL=ignoreboth
|
||||
|
||||
# append to the history file, don't overwrite it
|
||||
shopt -s histappend
|
||||
|
||||
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
|
||||
HISTSIZE=1000
|
||||
HISTFILESIZE=2000
|
||||
|
||||
# check the window size after each command and, if necessary,
|
||||
# update the values of LINES and COLUMNS.
|
||||
shopt -s checkwinsize
|
||||
|
||||
# If set, the pattern "**" used in a pathname expansion context will
|
||||
# match all files and zero or more directories and subdirectories.
|
||||
#shopt -s globstar
|
||||
|
||||
# make less more friendly for non-text input files, see lesspipe(1)
|
||||
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
|
||||
|
||||
# set variable identifying the chroot you work in (used in the prompt below)
|
||||
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
|
||||
debian_chroot=$(cat /etc/debian_chroot)
|
||||
fi
|
||||
|
||||
# set a fancy prompt (non-color, unless we know we "want" color)
|
||||
case "$TERM" in
|
||||
xterm-color|*-256color) color_prompt=yes;;
|
||||
esac
|
||||
|
||||
# uncomment for a colored prompt, if the terminal has the capability; turned
|
||||
# off by default to not distract the user: the focus in a terminal window
|
||||
# should be on the output of commands, not on the prompt
|
||||
#force_color_prompt=yes
|
||||
|
||||
if [ -n "$force_color_prompt" ]; then
|
||||
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
|
||||
# We have color support; assume it's compliant with Ecma-48
|
||||
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
|
||||
# a case would tend to support setf rather than setaf.)
|
||||
color_prompt=yes
|
||||
else
|
||||
color_prompt=
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$color_prompt" = yes ]; then
|
||||
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
||||
else
|
||||
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
||||
fi
|
||||
unset color_prompt force_color_prompt
|
||||
|
||||
# If this is an xterm set the title to user@host:dir
|
||||
case "$TERM" in
|
||||
xterm*|rxvt*)
|
||||
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
# enable color support of ls and also add handy aliases
|
||||
if [ -x /usr/bin/dircolors ]; then
|
||||
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
|
||||
alias ls='ls --color=auto'
|
||||
#alias dir='dir --color=auto'
|
||||
#alias vdir='vdir --color=auto'
|
||||
|
||||
alias grep='grep --color=auto'
|
||||
alias fgrep='fgrep --color=auto'
|
||||
alias egrep='egrep --color=auto'
|
||||
fi
|
||||
|
||||
# colored GCC warnings and errors
|
||||
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
|
||||
|
||||
# some more ls aliases
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
|
||||
# Add an "alert" alias for long running commands. Use like so:
|
||||
# sleep 10; alert
|
||||
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
|
||||
|
||||
# Alias definitions.
|
||||
# You may want to put all your additions into a separate file like
|
||||
# ~/.bash_aliases, instead of adding them here directly.
|
||||
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
|
||||
|
||||
if [ -f ~/.bash_aliases ]; then
|
||||
. ~/.bash_aliases
|
||||
fi
|
||||
|
||||
# enable programmable completion features (you don't need to enable
|
||||
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
|
||||
# sources /etc/bash.bashrc).
|
||||
if ! shopt -oq posix; then
|
||||
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
||||
. /usr/share/bash-completion/bash_completion
|
||||
elif [ -f /etc/bash_completion ]; then
|
||||
. /etc/bash_completion
|
||||
fi
|
||||
fi
|
||||
|
||||
function dbash(){
|
||||
docker exec -it $1 bash
|
||||
}
|
||||
|
||||
# Created by `pipx` on 2025-09-18 03:06:34
|
||||
export PATH="$PATH:/home/kenjim/.local/bin"
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
### DOTFILES_ALIASES ###
|
||||
export DOTFILES_DIR="$HOME/dotfiles"
|
||||
export DOTFILES_REMOTE="http://172.27.0.35:3000/kenjim/dotfiles"
|
||||
|
||||
# Dotfiles manager shortcut
|
||||
alias dotfiles='bash $HOME/scripts/dotfiles_manager.sh'
|
||||
alias dot='bash $HOME/scripts/dotfiles_manager.sh'
|
||||
|
||||
# Quick sync alias
|
||||
alias dots-sync='bash $HOME/scripts/dotfiles_manager.sh sync'
|
||||
alias dots-push='bash $HOME/scripts/dotfiles_manager.sh push'
|
||||
alias dots-status='bash $HOME/scripts/dotfiles_manager.sh status'
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
.bash_profile
|
||||
.bashrc
|
||||
dotfiles/.bash_profile
|
||||
dotfiles/.bashrc
|
||||
dotfiles/.gitconfig
|
||||
dotfiles/scripts/bootstrap.sh
|
||||
dotfiles/scripts/dotfiles_manager.sh
|
||||
dotfiles/scripts/setup_enterprise_ai_bash.sh
|
||||
.gitconfig
|
||||
scripts/bootstrap.sh
|
||||
scripts/dotfiles_manager.sh
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[user]
|
||||
name = Kenji M
|
||||
email = kenji@kenjim.com
|
||||
1
.gitconfig
Symbolic link
1
.gitconfig
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/kenjim/dotfiles/dotfiles/.gitconfig
|
||||
5
dotfiles/.bash_profile
Normal file
5
dotfiles/.bash_profile
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
# Load .bashrc if it exists
|
||||
if [ -f ~/.bashrc ]; then
|
||||
source ~/.bashrc
|
||||
fi
|
||||
159
dotfiles/.bashrc
Normal file
159
dotfiles/.bashrc
Normal file
@@ -0,0 +1,159 @@
|
||||
# ~/.bashrc: executed by bash(1) for non-login shells.
|
||||
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
|
||||
# for examples
|
||||
|
||||
# If not running interactively, don't do anything
|
||||
case $- in
|
||||
*i*) ;;
|
||||
*) return;;
|
||||
esac
|
||||
|
||||
# don't put duplicate lines or lines starting with space in the history.
|
||||
# See bash(1) for more options
|
||||
HISTCONTROL=ignoreboth
|
||||
|
||||
# append to the history file, don't overwrite it
|
||||
shopt -s histappend
|
||||
|
||||
# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
|
||||
HISTSIZE=1000
|
||||
HISTFILESIZE=2000
|
||||
|
||||
# check the window size after each command and, if necessary,
|
||||
# update the values of LINES and COLUMNS.
|
||||
shopt -s checkwinsize
|
||||
|
||||
# If set, the pattern "**" used in a pathname expansion context will
|
||||
# match all files and zero or more directories and subdirectories.
|
||||
#shopt -s globstar
|
||||
|
||||
# make less more friendly for non-text input files, see lesspipe(1)
|
||||
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
|
||||
|
||||
# set variable identifying the chroot you work in (used in the prompt below)
|
||||
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
|
||||
debian_chroot=$(cat /etc/debian_chroot)
|
||||
fi
|
||||
|
||||
# set a fancy prompt (non-color, unless we know we "want" color)
|
||||
case "$TERM" in
|
||||
xterm-color|*-256color) color_prompt=yes;;
|
||||
esac
|
||||
|
||||
# uncomment for a colored prompt, if the terminal has the capability; turned
|
||||
# off by default to not distract the user: the focus in a terminal window
|
||||
# should be on the output of commands, not on the prompt
|
||||
#force_color_prompt=yes
|
||||
|
||||
if [ -n "$force_color_prompt" ]; then
|
||||
if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
|
||||
# We have color support; assume it's compliant with Ecma-48
|
||||
# (ISO/IEC-6429). (Lack of such support is extremely rare, and such
|
||||
# a case would tend to support setf rather than setaf.)
|
||||
color_prompt=yes
|
||||
else
|
||||
color_prompt=
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$color_prompt" = yes ]; then
|
||||
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
||||
else
|
||||
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
|
||||
fi
|
||||
unset color_prompt force_color_prompt
|
||||
|
||||
# If this is an xterm set the title to user@host:dir
|
||||
case "$TERM" in
|
||||
xterm*|rxvt*)
|
||||
PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
# enable color support of ls and also add handy aliases
|
||||
if [ -x /usr/bin/dircolors ]; then
|
||||
test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
|
||||
alias ls='ls --color=auto'
|
||||
#alias dir='dir --color=auto'
|
||||
#alias vdir='vdir --color=auto'
|
||||
|
||||
alias grep='grep --color=auto'
|
||||
alias fgrep='fgrep --color=auto'
|
||||
alias egrep='egrep --color=auto'
|
||||
fi
|
||||
|
||||
# colored GCC warnings and errors
|
||||
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
|
||||
|
||||
# some more ls aliases
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
|
||||
# Add an "alert" alias for long running commands. Use like so:
|
||||
# sleep 10; alert
|
||||
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'
|
||||
|
||||
# Alias definitions.
|
||||
# You may want to put all your additions into a separate file like
|
||||
# ~/.bash_aliases, instead of adding them here directly.
|
||||
# See /usr/share/doc/bash-doc/examples in the bash-doc package.
|
||||
|
||||
if [ -f ~/.bash_aliases ]; then
|
||||
. ~/.bash_aliases
|
||||
fi
|
||||
|
||||
# enable programmable completion features (you don't need to enable
|
||||
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
|
||||
# sources /etc/bash.bashrc).
|
||||
if ! shopt -oq posix; then
|
||||
if [ -f /usr/share/bash-completion/bash_completion ]; then
|
||||
. /usr/share/bash-completion/bash_completion
|
||||
elif [ -f /etc/bash_completion ]; then
|
||||
. /etc/bash_completion
|
||||
fi
|
||||
fi
|
||||
|
||||
function dbash(){
|
||||
docker exec -it $1 bash
|
||||
}
|
||||
|
||||
# Created by `pipx` on 2025-09-18 03:06:34
|
||||
export PATH="$PATH:/home/kenjim/.local/bin"
|
||||
|
||||
### 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
|
||||
|
||||
|
||||
### DOTFILES_ALIASES ###
|
||||
export DOTFILES_DIR="$HOME/dotfiles"
|
||||
export DOTFILES_REMOTE="http://172.27.0.35:3000/kenjim/dotfiles"
|
||||
|
||||
# Dotfiles manager shortcut
|
||||
alias dotfiles='bash $HOME/scripts/dotfiles_manager.sh'
|
||||
alias dot='bash $HOME/scripts/dotfiles_manager.sh'
|
||||
|
||||
# Quick sync alias
|
||||
alias dots-sync='bash $HOME/scripts/dotfiles_manager.sh sync'
|
||||
alias dots-push='bash $HOME/scripts/dotfiles_manager.sh push'
|
||||
alias dots-status='bash $HOME/scripts/dotfiles_manager.sh status'
|
||||
|
||||
3
dotfiles/.gitconfig
Normal file
3
dotfiles/.gitconfig
Normal file
@@ -0,0 +1,3 @@
|
||||
[user]
|
||||
name = Kenji M
|
||||
email = kenji@kenjim.com
|
||||
26
dotfiles/scripts/bootstrap.sh
Executable file
26
dotfiles/scripts/bootstrap.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/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."
|
||||
1328
dotfiles/scripts/dotfiles_manager.sh
Executable file
1328
dotfiles/scripts/dotfiles_manager.sh
Executable file
File diff suppressed because it is too large
Load Diff
473
dotfiles/scripts/setup_enterprise_ai_bash.sh
Executable file
473
dotfiles/scripts/setup_enterprise_ai_bash.sh
Executable file
@@ -0,0 +1,473 @@
|
||||
#!/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-<OrgName>/ (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" <<LOCALEOF
|
||||
### MACHINE_LOCAL — managed by setup_enterprise_ai_bash.sh — DO NOT SYNC ###
|
||||
# Profile : ${MACHINE_PROFILE}
|
||||
# Host : $(hostname -s)
|
||||
# Generated: $(date)
|
||||
export MACHINE_PROFILE="${MACHINE_PROFILE}"
|
||||
export MACHINE_HOST="$(hostname -s)"
|
||||
LOCALEOF
|
||||
|
||||
if [[ "$MACHINE_PROFILE" == "work" ]]; then
|
||||
cat >> "$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*|kenjim-taas*)
|
||||
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" <<BASHEOF
|
||||
|
||||
### DOTFILES_ALIASES ###
|
||||
export DOTFILES_DIR="\$HOME/dotfiles"
|
||||
export DOTFILES_REMOTE="$DOTFILES_REMOTE"
|
||||
|
||||
# Dotfiles manager shortcut
|
||||
alias dotfiles='bash \$HOME/scripts/dotfiles_manager.sh'
|
||||
alias dot='bash \$HOME/scripts/dotfiles_manager.sh'
|
||||
|
||||
# Quick sync alias
|
||||
alias dots-sync='bash \$HOME/scripts/dotfiles_manager.sh sync'
|
||||
alias dots-push='bash \$HOME/scripts/dotfiles_manager.sh push'
|
||||
alias dots-status='bash \$HOME/scripts/dotfiles_manager.sh status'
|
||||
|
||||
BASHEOF
|
||||
echo "✅ Dotfiles aliases added to .bashrc"
|
||||
else
|
||||
echo "ℹ️ Dotfiles aliases already configured."
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------
|
||||
# 6️⃣ CREATE MACHINE BOOTSTRAP SCRIPT
|
||||
# ------------------------------------------------------
|
||||
|
||||
cat <<'EOF' > "$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
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/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."
|
||||
1
scripts/bootstrap.sh
Symbolic link
1
scripts/bootstrap.sh
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/kenjim/dotfiles/dotfiles/scripts/bootstrap.sh
|
||||
File diff suppressed because it is too large
Load Diff
1
scripts/dotfiles_manager.sh
Symbolic link
1
scripts/dotfiles_manager.sh
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/kenjim/dotfiles/dotfiles/scripts/dotfiles_manager.sh
|
||||
@@ -1,473 +0,0 @@
|
||||
#!/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-<OrgName>/ (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" <<LOCALEOF
|
||||
### MACHINE_LOCAL — managed by setup_enterprise_ai_bash.sh — DO NOT SYNC ###
|
||||
# Profile : ${MACHINE_PROFILE}
|
||||
# Host : $(hostname -s)
|
||||
# Generated: $(date)
|
||||
export MACHINE_PROFILE="${MACHINE_PROFILE}"
|
||||
export MACHINE_HOST="$(hostname -s)"
|
||||
LOCALEOF
|
||||
|
||||
if [[ "$MACHINE_PROFILE" == "work" ]]; then
|
||||
cat >> "$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*|kenjim-taas*)
|
||||
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" <<BASHEOF
|
||||
|
||||
### DOTFILES_ALIASES ###
|
||||
export DOTFILES_DIR="\$HOME/dotfiles"
|
||||
export DOTFILES_REMOTE="$DOTFILES_REMOTE"
|
||||
|
||||
# Dotfiles manager shortcut
|
||||
alias dotfiles='bash \$HOME/scripts/dotfiles_manager.sh'
|
||||
alias dot='bash \$HOME/scripts/dotfiles_manager.sh'
|
||||
|
||||
# Quick sync alias
|
||||
alias dots-sync='bash \$HOME/scripts/dotfiles_manager.sh sync'
|
||||
alias dots-push='bash \$HOME/scripts/dotfiles_manager.sh push'
|
||||
alias dots-status='bash \$HOME/scripts/dotfiles_manager.sh status'
|
||||
|
||||
BASHEOF
|
||||
echo "✅ Dotfiles aliases added to .bashrc"
|
||||
else
|
||||
echo "ℹ️ Dotfiles aliases already configured."
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------
|
||||
# 6️⃣ CREATE MACHINE BOOTSTRAP SCRIPT
|
||||
# ------------------------------------------------------
|
||||
|
||||
cat <<'EOF' > "$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
|
||||
|
||||
1
scripts/setup_enterprise_ai_bash.sh
Symbolic link
1
scripts/setup_enterprise_ai_bash.sh
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/kenjim/dotfiles/dotfiles/scripts/setup_enterprise_ai_bash.sh
|
||||
Reference in New Issue
Block a user