diff --git a/.bash_profile b/.bash_profile new file mode 100644 index 0000000..c005597 --- /dev/null +++ b/.bash_profile @@ -0,0 +1,16 @@ +. ~/.bashrc + +[[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ]] && . "/opt/homebrew/etc/profile.d/bash_completion.sh" + +test -e "${HOME}/.iterm2_shell_integration.bash" && source "${HOME}/.iterm2_shell_integration.bash" || true + +export BASH_SILENCE_DEPRECATION_WARNING=1 + +eval "$(/opt/homebrew/bin/brew shellenv)" + +export DOCKER_DEFAULT_PLATFORM=linux/amd64 + +# Load .bashrc if it exists +if [ -f ~/.bashrc ]; then + source ~/.bashrc +fi diff --git a/.bashrc b/.bashrc new file mode 100644 index 0000000..7228745 --- /dev/null +++ b/.bashrc @@ -0,0 +1,558 @@ +# Kenji's .bashrc file +# Should be symlinked to DBX/projects/unix/.bashrc +# Also should be synced to /homes/kenjim/.bashrc + +if [ -f /etc/profile ]; then source /etc/profile; fi +if [ -f /etc/bashrc ]; then source /etc/bashrc; fi + +export PS1='\h:\w\$ ' +export PS2='> ' +export FTP_PASSIVE_MODE=YES +export RSYNC_RSH=ssh + +OSTYPE=$(uname -s) +HOSTNAME=$(hostname -f) +HOSTDOMAIN=$(hostname -d) +EDITOR=vim +set -o vi + +alias gitresetpw='git config --global' +alias giturl='git config --get remote.origin.url' + +# OS specific #################### +case $OSTYPE in +Linux) + alias ls='ls -F' + alias pst='ps axjf' + # specify default shell tools ########### + if type $EDITOR >/dev/null 2>&1; then + export EDITOR=$EDITOR + alias vi=${EDITOR} + else + export EDITOR=vi + fi + + if type less >/dev/null 2>&1; then + export PAGER='less' + else + export PAGER=more + fi + + function yaml2json { + python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read())))' + } + + ;; +Darwin) + # pstree install with brew + alias pst='pstree' + ;; +FreeBSD) + export CLICOLOR=1 + LSCOLORS=fxgxcxdxbxegedabagacad + ;; +esac + +# terminal specific ############### +case $TERM in +xterm*) + export LS_COLORS='no=00:fi=00:di=00;35:ln=00;36:pi=40;33:so=00;35:bd=40;33;01:cd=40;33;01:or=01;05;37;41:mi=01;05;37;41:ex=00;31:*.cmd=00;32:*.exe=00;32:*.com=00;32:*.btm=00;32:*.bat=00;32:*.sh=00;32:*.csh=00;32:*.tar=00;31:*.tgz=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.zip=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.bz=00;31:*.tz=00;31:*.rpm=00;31:*.cpio=00;31:*.jpg=00;35:*.gif=00;35:*.bmp=00;35:*.xbm=00;35:*.xpm=00;35:*.png=00;35:*.tif=00;35:*.pl=00;32:' + alias ls='ls -F --color' + #PROMPT_COMMAND='echo -ne "\033]0;${HOSTNAME}: ${PWD}\007"' + #HOSTNAMESHORT="${HOSTNAME%%.*}" + #export PROMPT_COMMAND='echo -ne "\033]0;${HOSTNAMESHORT}: ${PWD##*/}\007"' + #export PROMPT_COMMAND='echo -ne "\033];${PWD##*/}\007"; ':"$PROMPT_COMMAND"; + ;; +esac + +# Domain specific environment ################# +case $HOSTDOMAIN in +juniper.net | jnpr.net) + # juniper.net hosts + export PATH=$PATH:/volume/labtools/bin + export PGUSER=ddladmin + export PGHOST=localhost + + function setawx() { + echo -n "AWX Host: " + read TOWER_HOST + echo -n "AWX Username: " + read TOWER_USERNAME + echo -n "AWX Password: " + read TOWER_PASSWORD + awx -k -f human login + awx config -f human | grep base_url + } + + alias awxjobs='awx jobs list -f human --filter id,name,limit,failed --all' + + alias j_proxy_on='proxy_on qcwebproxylb.juniper.net:3128' + + case $OSTYPE in + Linux) + alias pstree='/usr/bin/pstree -c -l -a -n -p -u' + ;; + FreeBSD) ;; + esac + + case $HOSTNAME in + # Special configuration specific for Juniper hosts + etqc-kenjim* | engtech-dev*) + # applies to both workstations + #export MAIL=${HOME}/Mail/mbox + #export MAILCHECK=600 + PATH=${HOME}/bin:${PATH} + alias awx='~/.local/bin/awx' + + source /usr/share/bash-completion/bash_completion + + ;; + esac + ;; +esac + +#alias realias='$EDITOR ~/.aliases; source ~/.aliases; PWDSAVE=${PWD}; cd ~/cvs/kenjim; cvs -d kenji@rad.kenjim.com:/home/cvs commit -m "updated" .aliases; cd ${PWDSAVE}' +#alias rebash='$EDITOR ~/.bashrc; source ~/.bashrc; PWDSAVE=${PWD}; cd ~/cvs/kenjim; cvs -d kenji@rad.kenjim.com:/home/cvs commit -m "updated" .bashrc; cd ${PWDSAVE}' + +# Host specific configurations +case $HOSTNAME in +kenjim-mbp | etqc-kenjim-* | engtech-dev-01*) + # default aliases to override whatever is in .aliases + source $HOME/k8configs/engtech_k8_env.sh + + # Add RVM to PATH for scripting + # /usr/texbin for MacTex/Latex stuff + #PATH=$PATH:/usr/local/sbin:$HOME/bin:$HOME/.rvm/bin:/usr/texbin + PATH=$HOME/bin:~/.local/bin:$PATH + #PATH=/Users/kenjim/bin/Sencha/Cmd/5.1.2.52:$PATH + #export SENCHA_CMD_3_0_0="/Users/kenjim/bin/Sencha/Cmd/5.1.2.52" + export CLICOLOR=1 + + export RAILS_ENV=development + #export TIM_SSH_USER_PASS='B3k1nD+' + #export TIM_EK_R_LP='emccolganoansarikenjimjhayespsen' + export SN_USERNAME='_integ-soap-read' + export SN_PASSWORD='e3Qw&%b7' + + #export PYENV_VERSION='3.8.1' + #export ANSIBLE_CONFIG='/Users/kenjim/Juniper/git-m16/infra-ansible/ansible_cli.cfg' + #export KUBECONFIG='/Users/kenjim/Juniper/K8_Configs/kenjim-k8-config' + + # Personal Shortcuts + alias l='less' + alias t='tail -f' + alias psg='pstree | grep' + alias sl='screen -list' + alias sr='screen -r' + #alias conncount='netstat -ant | awk "{print $6}" | sort | uniq -c | sort -n' + alias a='alias' + alias h='history 25' + alias la='ls -a' + alias lf='ls -FA' + alias ll='ls -lAh|more' + alias lt='ls -tlAh|more' + alias tl='tail -f /var/log/messages' + alias sl='screen -list' + alias sr='screen -r' + alias ps-m='ps aux --sort -rss' + + #alias sa='ssh-add /export/kenjim/.ssh/juniper_kenjim_rsa_id' + #alias lair='ssh-keygen -f "/home/kenjim/.ssh/known_hosts" -R lair.kenjim.com; ssh -f -N -R 11322:localhost:22 lair' + #alias bar='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o ServerAliveInterval=540 -o Compression=no -f -N -R 11322:localhost:22 root@192.241.205.115' + #alias sa='ssh-add /export/kenjim/.ssh/juniper_kenjim_rsa_id' + + alias rvmi='source "$HOME/.rvm/scripts/rvm"; echo "rvm initialized..."' + #alias psql='/Applications/Postgres.app/Contents/Versions/9.6/bin/psql' + #alias ctags='/usr/local/bin/ctags' + #alias rtags='/usr/local/bin/ctags -R -f .tags --languages=ruby --exclude=.git --exclude=log . $(bundle list --paths)' + + alias pstat='passenger status' + alias pstart='passenger start' + alias pstop='passenger stop' + alias prest='passenger stop; sleep 2; passenger start' + alias renv='env|sort|egrep -i "(gem|ruby|rvm|rails|tim)"' + + # Postgres Logins + alias pgtimdev='psql -U tim_api tim_api_dev' + alias pgtimqdev='psql -U tim_api -h ttqc-tim-mmonit-02 kenjim_devel' + alias pgtimqdev2='psql -U tim_api -h ttqc-tim-mmonit-02 kenjim_devel2' + alias pgtimtest='psql -U tim_api tim_api_test' + alias pgtimint='psql -h ttqc-tim-mm-02.juniper.net -U tim_api tim_api_int' + alias pgtimint2='psql -h ttsv-db-03.juniper.net -U tim_api tim_api_int2' + alias pgtimprod='psql -h ttqc-tim-mm-02.juniper.net -p 5432 -U tim_api tim' + alias pgtimloc='psql -U tim_api kenjim_tim' + alias pgtimweekly='psql -h ttqc-testdb-01 -U tim_api weekly_systest_live' + alias pgtimqmm='psql -h ttqc-tim-mmonit-02 -U mmonit mmonit' + alias pgtimbmm='psql -h ttbg-tim-mmonit-01 -U mmonit mmonit' + alias pgbdrqc='psql -h ttqc-bdr-db01.juniper.net -U tim_api systest_live' + alias pgbdrbg='psql -h ttbg-bdr-db01.juniper.net -U tim_api systest_live' + alias pgdlm='psql -h ttqc-dlm-db-ms.ttglb.juniper.net -U ddladmin systest_live' + + alias jira='jira-cli --v2 view --oneline --search-jql "assignee=kenjim and status!=closed and status!=resolved order by priority desc, created asc"' + + # retired + #alias tim='GLI_DEBUG=true bundle exec bin/timcl' + + #alias ap='ansible-playbook -i inventory/engtech-prod' + #alias ai='ANSIBLE_CONFIG=/Users/kenjim/Juniper/git-m16/infra-ansible/ansible_cli.cfg ansible-inventory -i inventory/cmdb/now.py -i inventory/engtech_prod' + + alias ai='ANSIBLE_CONFIG=/Users/kenjim/Juniper/git-m16/infra-ansible/ansible_cli.cfg ansible-inventory -i inventory/cmdb_rendered/engtech.yml' + alias ans='ANSIBLE_CONFIG=/Users/kenjim/Juniper/git-m16/infra-ansible/ansible_cli.cfg ansible -i ~/ansible/inventory/cmdb/now.py -i ~/ansible/inventory/engtech_prod' + #alias apy='rm ~/ansible.log ; ANSIBLE_CONFIG=/Users/kenjim/Juniper/git/infra-ansible/ansible_cli.cfg ANSIBLE_STDOUT_CALLBACK=yaml ansible-playbook -i inventory/cmdb/now.py -i inventory/engtech_prod' + alias apy='rm ~/ansible.log ; ANSIBLE_CONFIG=/Users/kenjim/Juniper/git/infra-ansible/ansible_cli.cfg ANSIBLE_STDOUT_CALLBACK=yaml ansible-playbook -i inventory/cmdb_rendered/engtech.yml --vault-id vault_engtech_default' + alias aps='rm ~/ansible.log ; ANSIBLE_CONFIG=/Users/kenjim/Juniper/git/infra-ansible/ansible_cli.cfg ANSIBLE_STDOUT_CALLBACK=selective ansible-playbook -i inventory/cmdb_rendered/engtech.yml --vault-id vault_engtech_default' + alias apa='rm ~/ansible.log ; ANSIBLE_CONFIG=/Users/kenjim/Juniper/git/infra-ansible/ansible_cli.cfg ANSIBLE_STDOUT_CALLBACK=actionable ansible-playbook -i inventory/cmdb_rendered/engtech.yml --vault-id vault_engtech_default' + alias apd='rm ~/ansible.log ; ANSIBLE_CONFIG=/Users/kenjim/Juniper/git/infra-ansible/ansible_cli.cfg ANSIBLE_STDOUT_CALLBACK=debug ansible-playbook -i inventory/cmdb/now.py -i inventory/engtech_prod' + alias apj='rm ~/ansible.log ; ANSIBLE_CONFIG=/Users/kenjim/Juniper/git/infra-ansible/ansible_cli.cfg ANSIBLE_STDOUT_CALLBACK=json ansible-playbook -i inventory/cmdb/now.py -i inventory/engtech_prod' + alias giturl='git config --get remote.origin.url' + alias gitresetpw='git config --global credential.helper osxkeychain' + + alias d='docker' + alias di='docker images' + alias dp='docker ps' + alias dr='docker run' + alias db='docker build' + alias dbash='docker run -it --entrypoint /bin/bash' + + function tmls { ssh $1 ls /etc/monit.d; } + function tmlog { ssh $1 tail -f /var/log/monit.log; } + + function tdiffstat { svn diff -x --ignore-eol-style -x -w https://tt-svn.juniper.net/svn/adp/projects/tim/tags/$1/rails-api https://tt-svn.juniper.net/svn/adp/projects/tim/trunk/rails-api | diffstat; } + function tdiff { svn diff -x --ignore-eol-style -x -w https://tt-svn.juniper.net/svn/adp/projects/tim/tags/$1/rails-api https://tt-svn.juniper.net/svn/adp/projects/tim/trunk/rails-api; } + + function timsvncopy { svn copy --parents https://tt-svn.juniper.net/svn/adp/projects/tim/trunk/rails-api https://tt-svn.juniper.net/svn/adp/projects/tim/tags/$1/rails-api -m "$1 new branch"; } + + function curl_time { curl -w "@curl-format.txt" -o /dev/null -s "$1"; } + + # copies my protected ssh pub key + function copy_pub_key { cat ~/.ssh/authorized_keys.kenjim-protected | ssh $1 'umask 0077; mkdir -p .ssh; cat >> .ssh/authorized_keys && echo "Key copied"'; } + + function ldaps() { + ldapsearch -LLL -x -b 'dc=jnpr,dc=net' -h ldap-eqx-lb.jnpr.net -D 'CN=_eng_jira_bind_new,OU=Service Accounts,OU=Misc,OU=Common,DC=jnpr,DC=net' -w 'xqYzhL%lLe!FIr!67LJX%7a^PWOWY0' sAMAccountName=$1 | perl -p00e 's/\r?\n //g' + } + + function ldaps2() { + ldapsearch -LLL -x -b 'dc=jnpr,dc=net' -h ldap-qnc-lb.jnpr.net -D 'CN=_eng_labrsrcmgr_bind,OU=Service Accounts,OU=Misc,OU=Common,DC=jnpr,DC=net' -w 'tF#w3St@nGqq36XZDym#857U)v4xKw' sAMAccountName=$1 | perl -p00e 's/\r?\n //g' + } + + function netseg() { + curl -k https://netseg.juniper.net/api/find_ip?hostname=$1 | jq '.' + } + + function dbash() { + docker exec -it $1 bash + } + + # Kubectl Autocomplete + source <(kubectl completion bash) + alias k=kubectl + complete -o default -F __start_kubectl k + + alias kc='kubectl config view' + alias kd='kubectl describe' + alias ke='kubectl explain' + alias kg='kubectl get' + alias kl='kubectl logs -f' + alias kp='kubectl get pods -o wide' + alias kq='kubectl describe quota' + alias ks='kubectl get services' + alias kuc='kubectl config use-context' + alias kpl='kubectl get pods --show-labels' + alias kdp='kubectl describe pod' + alias kgs='kubectl get secrets' + + # Kubectl aliases + alias kx='f() { [ "$1" ] && kubectl config use-context $1 || kubectl config current-context ; } ; f' + alias kn='f() { [ "$1" ] && kubectl config set-context --current --namespace $1 || kubectl config view --minify | grep namespace | cut -d" " -f6 ; } ; f' + + #export NVM_DIR="$HOME/.nvm" + #[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm + #[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion + + #export COPYFILE_DISABLE=1 + #export GRAPHVIZ_DOT=/usr/local/bin/dot + ;; +esac + +case $HOSTNAME in +################################################################################ +kenjim-mbp*) + #export DOCKER_DEFAULT_PLATFORM=linux/amd64 + + # add MacTex Path, installer unable to add symlink in /usr/texbin + #PATH=/usr/local/opt/libiconv/bin:/Library/TeX/texbin:$PATH + #PATH=/usr/local/opt/libiconv/bin:/Library/TeX/texbin:$PATH + #PATH=/opt/homebrew/bin:$PATH + + #export LDFLAGS="-L/usr/local/opt/zlib/lib" + #export CPPFLAGS="-I/usr/local/opt/zlib/include" + #source ~/perl5/perlbrew/etc/bashrc + + if command -v pyenv 1>/dev/null 2>&1; then + export PYENV_ROOT="$HOME/.pyenv" + export PATH="$PYENV_ROOT/bin:$PATH" + eval "$(pyenv init --path)" + eval "$(pyenv init -)" + fi + + export AWS_PROFILE=kenjim-qnc + # make connection to k ssh config first localhost forward: LocalForward 8888 qnc-engdata5vs3.juniper.net:80 + #alias aws='aws --endpoint-url http://localhost:8888' + alias aws-pgdb-qnc-du="aws --profile pgdb-qnc s3 ls s3://engtech-pgdb-s3-qnc/ --recursive | awk 'BEGIN {total=0}{total+=\$3}END{print total/1024/1024/1024\" GB\"}'" + alias aws-pgdb-bng-du="aws --profile pgdb-bng s3 ls s3://engtech-pgdb-s3-bng/ --recursive | awk 'BEGIN {total=0}{total+=\$3}END{print total/1024/1024/1024\" GB\"}'" + alias aws-pgdb-qnc-ls="aws --profile pgdb-qnc s3 ls s3://engtech-pgdb-s3-qnc/ --recursive" + alias aws-pgdb-bng-ls="aws --profile pgdb-bng s3 ls s3://engtech-pgdb-s3-bng/ --recursive" + export AWS_SDK_LOAD_CONFIG=1 + + #export KUBECONFIG='/Users/kenjim/.kube/config' + + function k8proxy { + export KUBECONFIG='/Users/kenjim/Juniper/K8_Configs/kenjim-k8-config' + export no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com" + export http_proxy="socks5://localhost:3133/" + export https_proxy=$http_proxy + export ftp_proxy=$http_proxy + export rsync_proxy=$http_proxy + } + + function killcrashplan() { + sudo launchctl unload /Library/LaunchDaemons/com.crashplan.engine.plist + } + ;; + +etqc-kenjim-* | engtech-dev-01*) + # Make symlink git checkout location + # ~k8configs -> ~/EngTech/Infra/k8configs + source $HOME/k8configs/engtech_k8_env.sh + export AWS_PROFILE=pgdb-qnc + # make connection to k8dev ssh config first localhost forward: LocalForward 8888 qnc-engdata5vs3.juniper.net:80 + alias aws='aws --endpoint-url http://qnc-engdata5vs3.juniper.net' + alias aws-pgdb-qnc-du="aws s3 ls s3://engtech-pgdb-s3-qnc/ --recursive | awk 'BEGIN {total=0}{total+=\$3}END{print total/1024/1024/1024\" GB\"}'" + alias aws-pgdb-bng-du="aws s3 ls s3://engtech-pgdb-s3-bng/ --recursive | awk 'BEGIN {total=0}{total+=\$3}END{print total/1024/1024/1024\" GB\"}'" + alias aws-pgdb-qnc-ls="aws s3 ls s3://engtech-pgdb-s3-qnc/ --recursive" + alias aws-pgdb-bng-ls="aws s3 ls s3://engtech-pgdb-s3-bng/ --recursive" + export AWS_SDK_LOAD_CONFIG=1 + ;; + +################################################################################ +# akira-mt*) +# #export CVSROOT=ace.kenjim.com:/cvs +# alias rvmi='source "$HOME/.rvm/scripts/rvm"; echo "rvm initialized..."' +# alias kl='ssh kenjim-lnx-tun' +# #dispwin -d1 -I ~/projects/monitor_calibration/GTX660_VP2770_C60_B29_D65.icm +# #dispwin -d2 -I ~/projects/monitor_calibration/GTX660_HDMI_23EA63_C83_B26_D65.icm +# export RAILS_ENV=kenjim_local + +# ;; +################################################################################ +esac + +function proxy_on() { + export no_proxy="localhost,127.0.0.1,localaddress,.localdomain.com" + + if (($# > 0)); then + #valid=$(echo $@ | sed -n 's/\([0-9]\{1,3\}.\)\{4\}:\([0-9]\+\)/&/p') + #if [[ $valid != $@ ]]; then + # >&2 echo "Invalid address" + # return 1 + #fi + + export http_proxy="http://$1/" + export https_proxy=$http_proxy + export ftp_proxy=$http_proxy + export rsync_proxy=$http_proxy + echo "Proxy environment variable set." + return 0 + fi + + echo -n "username: " + read username + if [[ $username != "" ]]; then + echo -n "password: " + read -es password + local pre="$username:$password@" + fi + + echo -n "server: " + read server + echo -n "port: " + read port + + export http_proxy="http://$pre$server:$port/" + export https_proxy=$http_proxy + export ftp_proxy=$http_proxy + export rsync_proxy=$http_proxy + export HTTP_PROXY=$http_proxy + export HTTPS_PROXY=$http_proxy + export FTP_PROXY=$http_proxy + export RSYNC_PROXY=$http_proxy +} + +function proxy_off() { + unset http_proxy + unset https_proxy + unset ftp_proxy + unset rsync_proxy + echo -e "Proxy environment variable removed." +} + +function unified-hub-login() { + UNIFIED_HUB_URL="unified-hub.juniper.net" + UNIFIED_HUB_USERNAME="kenjim@juniper.net" + UNIFIED_HUB_PASSWORD="cmVmdGtuOjAxOjE3NDA3ODI3NDk6OGt6WUJvcFJwa29RdEhKWWE1U1MzNkJnZXVE" + echo $UNIFIED_HUB_PASSWORD | docker login -u "$UNIFIED_HUB_USERNAME" --password-stdin $UNIFIED_HUB_URL +} + +unified-hub-engtech-bin-upload() { + local file="$1" + local remote_path="$2" + + # Validation + if [[ -z "$file" ]] || [[ -z "$remote_path" ]]; then + echo "Usage: unified-hub-engtech-bin-upload " + echo "" + echo "Example:" + echo " unified-hub-engtech-bin-upload kubectl dl.k8s.io/release/v1.28.4/bin/linux/amd64/kubectl" + echo "" + echo "Uploads to: https://unified-hub.juniper.net/artifactory/engtech-binaries/" + return 1 + fi + + if [[ ! -f "$file" ]]; then + echo "Error: File '$file' not found in current directory" + return 1 + fi + + # Configuration + local username="kenjim@juniper.net" + local token="cmVmdGtuOjAxOjE3NzY5NzgzNDY6Nm4xZU9uOEZvUzVLM1RNQkxXY2ZVdThBOVFY" + local base_url="https://unified-hub.juniper.net/artifactory/engtech-binaries" + local full_url="${base_url}/${remote_path}" + + echo "đŸ“Ļ Uploading: $file" + echo "📍 To: $full_url" + echo "" + + # Upload with progress + if curl -u "${username}:${token}" \ + -T "$file" \ + --progress-bar \ + "$full_url"; then + echo "" + echo "✅ Upload successful!" + echo "🔗 URL: $full_url" + else + echo "" + echo "❌ Upload failed!" + return 1 + fi +} + +# Add this to your ~/.bashrc or ~/.zshrc + +git-prune-branches() { + echo "đŸŒŋ Git Branch Cleanup" + echo "====================" + echo "" + + # Check if we're in a git repo + if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo "❌ Error: Not in a git repository" + return 1 + fi + + # Get main branch name (main or master) + local main_branch=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@') + if [[ -z "$main_branch" ]]; then + main_branch="main" + fi + + echo "📡 Fetching latest from remote and pruning..." + git fetch --all --prune + echo "" + + # Show merged branches + echo "đŸ—‘ī¸ Local branches merged into $main_branch:" + local merged_branches=$(git branch --merged "$main_branch" | grep -v "^\*" | grep -v "$main_branch" | grep -v "master") + if [[ -z "$merged_branches" ]]; then + echo " (none)" + else + echo "$merged_branches" + echo "" + read -p "Delete these merged local branches? (y/N): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "$merged_branches" | xargs -n 1 git branch -d + echo "✅ Merged branches deleted" + else + echo "â­ī¸ Skipped" + fi + fi + + echo "" + # Show orphaned branches + echo "đŸ—‘ī¸ Local branches tracking deleted remotes:" + local orphaned_branches=$(git branch -vv | grep ': gone]' | awk '{print $1}') + if [[ -z "$orphaned_branches" ]]; then + echo " (none)" + else + echo "$orphaned_branches" + echo "" + read -p "Delete these orphaned local branches? (y/N): " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "$orphaned_branches" | xargs -n 1 git branch -D + echo "✅ Orphaned branches deleted" + else + echo "â­ī¸ Skipped" + fi + fi + + echo "" + echo "📊 Remaining local branches:" + git branch -vv + + echo "" + echo "✨ Cleanup complete!" +} + +alias git-clean='git-prune-branches' + +touch ~/.aliases +source ~/.aliases + + +### 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' + diff --git a/.dotfiles_manifest b/.dotfiles_manifest index e69de29..6128631 100644 --- a/.dotfiles_manifest +++ b/.dotfiles_manifest @@ -0,0 +1,8 @@ +.bash_profile +.bashrc +.gitconfig +.ssh/config +.ssh/known_hosts +scripts/bootstrap.sh +scripts/dotfiles_manager.sh +scripts/setup_enterprise_ai_bash.sh diff --git a/.gitconfig b/.gitconfig new file mode 100644 index 0000000..71069bd --- /dev/null +++ b/.gitconfig @@ -0,0 +1,22 @@ +# This is Git's per-user configuration file. +[user] +# Please adapt and uncomment the following lines: +name = Kenji Morishige +email = kenjim@juniper.net +[push] + default = matching + autoSetupRemote = true + default = current + autoSetupRemote = true +[credential] + helper = osxkeychain +[alias] + st = status + stat = status + co = checkout + lg1 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all + lg2 = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all + lg = !"git lg1" + br = branch + ci = commit + unstage = reset HEAD -- diff --git a/.ssh/config b/.ssh/config new file mode 100644 index 0000000..9928551 --- /dev/null +++ b/.ssh/config @@ -0,0 +1,227 @@ +ForwardAgent yes +#ForwardX11 yes +UserKnownHostsFile /dev/null +StrictHostKeyChecking no +Protocol 2,1 +Compression yes +KeepAlive yes +#ServerAliveInterval 540 +#LocalForward 57711 127.0.0.1:57711 +AddKeysToAgent yes +IgnoreUnknown UseKeychain +UseKeychain yes + +Host k +HostName etqc-kenjim-11.juniper.net +#ProxyJump engdmz +DynamicForward 3139 +# For aws S3 command +LocalForward 8888 qnc-engdata5vs3.juniper.net:80 +# VMM Telemetry DB +#LocalForward 5445 etus-tel-db-k8.ttglb.juniper.net:5432 +# Notification DB +#LocalForward 5446 etus-not-db-k8.ttglb.juniper.net:5432 +# LRM Beta DB +LocalForward 5447 engtech-lrm-snap1-qnc.engtech-pgdb-qnc.qengk8.juniper.net:5432 +# TIM-API DB +#LocalForward 5448 engtech.timdb.qengk8.juniper.net:5432 +# Limited Use +RemoteForward 2222 localhost:22 +ServerAliveInterval 30 +ServerAliveCountMax 20 + +Host zet +HostName 172.27.0.35 +ServerAliveInterval 540 + +Host kenjim-mbm +HostName kenjim-mbm.home.arpa + +Host ktaas +HostName kenjim-taas.qengk8.juniper.net + +Host ttqc-shell +HostName ttqc-shell012.juniper.net + +Host ktq +HostName kenjim-temp.qengk8.juniper.net + +Host ktb +HostName kenjim-temp.bengk8.juniper.net + +Host kold +HostName etqc-kenjim-01.juniper.net + + +# Paul's machine in labdmz +Host p +HostName etqc-pms-02.juniper.net +DynamicForward 3140 + + +# Quincy K8 Dev Server +Host k8dev +HostName engtech-dev-01.juniper.net +#ProxyJump engdmz +DynamicForward 3132 +# For aws S3 command +LocalForward 8889 qnc-engdata5vs3.juniper.net:80 + +Host ttqc-shell005 +HostName ttqc-shell005 +ProxyJump engdmz + + +Host router +User root +HostName 172.27.0.254 + +Host bar +User root +HostName bar.kenjim.com +DynamicForward 3128 +Port 22 +Compression yes +ServerAliveInterval 540 + +Host akira +HostName lair.kenjim.com +Port 11722 +#RemoteForward 11522 localhost:22 +ForwardAgent yes +ForwardX11 yes +Compression yes +IdentityFile /export/kenjim/.ssh/juniper_kenjim_rsa_id +# for VNCing to home system +#LocalForward 55900 localhost:5900 +#LocalForward 53389 t41xp:3389 +ServerAliveInterval 540 + + +Host akira-mt +HostName 172.27.0.11 +#RemoteForward 11522 localhost:22 +#ForwardAgent yes +#ForwardX11 yes +#Compression yes +#IdentityFile /export/kenjim/.ssh/juniper_kenjim_rsa_id +# for VNCing to home system +#LocalForward 55900 localhost:5900 +#LocalForward 53389 t41xp:3389 +#ServerAliveInterval 540 + +# Port forwarded through home router +Host lair-t430-vm +HostName lair.kenjim.com +Port 11922 +#RemoteForward 11522 localhost:22 +ForwardAgent yes +#ForwardX11 yes +Compression yes +#IdentityFile /export/kenjim/.ssh/juniper_kenjim_rsa_id +# for VNCing to home system +#LocalForward 55900 localhost:5900 +#LocalForward 53389 t41xp:3389 +#ServerAliveInterval 540 + +# This is for connecting from kenjim-lnx +Host mbp +HostName 192.168.0.100 +User kenjim +ForwardAgent yes + +# This is for connecting to mint vm on macbook fusion +Host kenjim-vm +HostName 192.168.168.130 +User kenjim +ForwardAgent yes + +Host p-qnc-tt* +User kenjim +ServerAliveInterval 250 + +Host engdmz +User kenjim +HostName qceng-dmz-01 +Ciphers aes256-ctr,chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com +ServerAliveInterval 250 +DynamicForward 3130 +LocalForward 5433 10.47.245.41:5432 +# Engineering K8 Clusters +LocalForward 8443 qnengapp1-vip.juniper.net:443 +LocalForward 9443 bnengapp1-vip.juniper.net:443 +LocalForward 5430 10.47.245.65:5432 +# TIM Production +LocalForward 5450 10.47.245.53:5432 +#LocalForward 5452 etus-pgb-lrm.ttglb.juniper.net:5432 +#LocalForward 3306 engtech-metrics.juniper.net:3306 + +Host encdmz +User kenjim +HostName qcencl-dmz-01 +ServerAliveInterval 250 +DynamicForward 3135 + +Host labdmz +User kenjim +HostName ttqc-tim-sh01.juniper.net +Ciphers aes256-ctr,chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com +ServerAliveInterval 250 +DynamicForward 3129 +#LocalForward 22211 ttqc-tim-sh01:22 +#LocalForward 22221 ttqc-awx-01:22 +#LocalForward 22222 ttqc-awx-02:22 +#LocalForward 22231 ttqc-tim-app-01:22 +#LocalForward 22232 ttqc-tim-app-02:22 +#LocalForward 22233 ttqc-tim-app-03:22 +#LocalForward 22234 ttqc-tim-app-04:22 +#LocalForward 22235 ttqc-tim-app-05:22 +#LocalForward 22236 ttqc-tim-app-06:22 +#LocalForward 22241 ttqc-tim-utl-01:22 +#LocalForward 22242 ttqc-tim-utl-02:22 +#LocalForward 6548 etus-pgb-lrm-snap1.ttglb.juniper.net:6548 +LocalForward 6548 etus-pgb-lrm.ttglb.juniper.net:6548 +#LocalForward 5433 ttqc-testdb-01:5432 +#LocalForward 7432 p-qnc-ttdb03.juniper.net:5432 + +#Host etqc-awx-ez-* +#Compression yes +#ServerAliveInterval 60 +#ProxyJump engdmz + +Host evncdmz +User kenjim +HostName qceng-evnc-02.juniper.net + +# Bangalore K8 Dev Server +Host k8devb +HostName engtech-bdev-01 +ProxyJump engdmz + +Host etqc-pgtools-01 +HostName etqc-pgtools-01 +ProxyJump engdmz + +Host etbg-vmpgdb-02 +HostName etbg-vmpgdb-02 +ProxyJump engdmz +DynamicForward 3133 + +Host etqc-tim-agt-* +ProxyJump engdmz + +Host pfsense +HostName 172.27.0.1 +User root + +Host ttqc-testdb-01 +#LocalForward 5433 localhost:5432 +ProxyJump labdmz + +Host etqc-dlm-db-01 +HostName etqc-dlm-db-01 +User kenjim +LocalForward 5433 localhost:5431 + +Host awx-lz-01 +HostName etqc-awx-lz-01 diff --git a/.ssh/keys/id_rsa.gpg b/.ssh/keys/id_rsa.gpg new file mode 100644 index 0000000..3ccf0ce Binary files /dev/null and b/.ssh/keys/id_rsa.gpg differ diff --git a/.ssh/keys/id_rsa.jnpr.gpg b/.ssh/keys/id_rsa.jnpr.gpg new file mode 100644 index 0000000..f5cb81d Binary files /dev/null and b/.ssh/keys/id_rsa.jnpr.gpg differ diff --git a/.ssh/keys/id_rsa.kenjim-protected.gpg b/.ssh/keys/id_rsa.kenjim-protected.gpg new file mode 100644 index 0000000..3f3c63f Binary files /dev/null and b/.ssh/keys/id_rsa.kenjim-protected.gpg differ diff --git a/.ssh/keys/id_rsa.pub b/.ssh/keys/id_rsa.pub new file mode 100644 index 0000000..a64d9cc --- /dev/null +++ b/.ssh/keys/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDJGNwM6dI8ZNwvPKWl6VYP83l5vHUbCfbhmKVYYABgja/A0iluI2iPTutXZDwpLM8f4nCeeAONeFpX12OsFhVMc8OnpR36i6mOa3GbZJDMgt22ciESQF/m8IbHw+zdQoWC7gd9+pV7yCxVTE3NKbdHP/WWyqUt2AV51jJ0E/ZRf1KXY+YSyZVNWoah8YVfOKIRFZzJhJZ43rkl/8ZJpeZNnM28YLZ0Pfnxuk0fVpjx334rCy9oqehoG/5x3ycUtEHGo1T1AjVUYpJiCYOkh0CrFeo+L/AiJ5jI7n3OZJP157EmpZgWq9i30qnuwLJOSNugLrICkvDIY+MbUzi5MOMJ kenjim@akira-ub diff --git a/.ssh/keys/kenjim-backup-id.pub b/.ssh/keys/kenjim-backup-id.pub new file mode 100644 index 0000000..f596ccc --- /dev/null +++ b/.ssh/keys/kenjim-backup-id.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDlTn6uLryI4sBX8AO46I8FSPmQs0tTO5yVb9ZMn3VGK4B6CstcGU1CSykubnV+EzT/FNfwsYaER+u/uIIUHUSQNkQzwDgU4m6zSuCqYGMORq85e2JtvcEn3nH944hEBtOyza2cmxi241npsfskb3c7yDZ3VTNwOjDy7fSZ+QpbWnjhn/POh1MgkzuOdOWCeTXfcphLnu442OS5x9g6/44+tWynIotjpbt822H1pgnh7QmmygfHG214N/0TzpjQsY0t0aV9POSNO09dZvoVOw+DxPDq4HW0sBV0Pfu6YhCuTuYSskZVzyxaef/HrnZ1nMmMBswYImELJab0sDCMNQm9ZJUtXUT0pRrnH4beE4owvIh7g5NKcFQR0kfd6cWz1WJGSKW3iOoXx8zZxWzuKaixQEl9F+tz01N8N4i3Td37sic1lVa2zc+h4yjKMuv3lXNOisvWhWbW7yUujT69WrI89/96z8CQ+96APCoppI6xuez6IFyxFkSYz9fx5k0tQ+E= kenjim@kenjim-mbp diff --git a/.ssh/keys/kenjim-testtool-futsuu.pub b/.ssh/keys/kenjim-testtool-futsuu.pub new file mode 100644 index 0000000..afb712e --- /dev/null +++ b/.ssh/keys/kenjim-testtool-futsuu.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCxodjkxzlFCxYRzDkoxN1NHnQTc9WhiovufEuciR/I85n5JWJ/EnN6V5UdXSNnnvkaHdn9F7wZS2+wdg6qyuMffJwVHsx7yhqRs4s37d7RNu3VHpKpCr6cWEyLUTpC3vOoIY4vGC7ruX3Y96hGRNR0C6h6jUS/LabBmZq98bXumgjRvomw6Vaa7teLI5t06yA9AvrzzSB9g6IeyzciBcG+i8LHmwb3UbVfht9l88TMo2joNViqapDOm/Xq8EEONeNY52c80SmEvF4NaN0oT3bqtXLKavD95rTXmDC1bJedRBatYR1fRxvqCJ3QO4RBDYhmJsKTosX2GPTfoB/21bekzTvRi+WwBVWodAF6TD9atY4AUofxH+XL9j58d070fBQTOcYFUaRuMVn+477OVJlHcrXjKusd8/Y0DK7GlrcEp8cU480OkGzKpKG6XYzs3Cl9+3Gg0tpw5IEDI4Xt2oIlRhNDVIOeem48wSc32nbHgc/kxiJx1opHkONmQtzSg+c= kenjim@kenjim-m16 diff --git a/.ssh/keys/testtool-awx-ssh-key.pub b/.ssh/keys/testtool-awx-ssh-key.pub new file mode 100644 index 0000000..6568bf3 --- /dev/null +++ b/.ssh/keys/testtool-awx-ssh-key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDtasZMNQR0QoGvLTgfoj/SDfQ2Ob4GTQNbtv5iSCX6mN9/hRH8jc0j8chS50RDQdB6IQfT67C+7Tr4WR2idRW9N9M4o8h6/vmyMaYk7rwjEL+tTnS26ArgTy0mAKm+MpBz2q5J96wRRBCusrH9pONbPrEOYa9iOE6MosQlkYM9gHDqRQcuL4nIOp1Oiw5RjKm0ZQ77lrPPpDXN++i0RBJb7GikHdDa5GjMol2BBoswgAcKFzXYZljIe+RoMhZRWGbzSaO0QwfhWHwmSFr1kmLu2NfSInsQ6IeTNY7DtfOUZushhssEljvXIe3ZRKcImHOgNwm0pZsT/ZRWNBa/k29F testtool-awx-key diff --git a/.ssh/known_hosts b/.ssh/known_hosts new file mode 100644 index 0000000..d0a6cfb --- /dev/null +++ b/.ssh/known_hosts @@ -0,0 +1,16 @@ +kenjim-lnx ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC20Jm+3OIYp7bnQnydjnDPPzQBiC20M41AIwy60No5FVCviAfzvoYiaMd7J2DG7Yr3OEO+T6UQI8JVkcYdgHxKBD5OywUKY00V+XnKbPMwIFjOxCeFLzL3HKYtsgy4SE6e0BB13GZ0Mw0L/YNX2D3zm12vRpBQjg/KM9W7v0nDJvwJCF/qKIqhb/R+2652TAQcvEObUXKwvxK7HnTX1oFxyQ7eA0nAfU1r4Sk2603r6StptFy2LjM8+JUgCKQU1Q/vwC7IXEcl1JiwxCyIYzWKlkQpOzdzArwreyMj1bxJZU8djwqrsIMVce1TBnuttXxHbBsGtVXvxHpiCX7BIept +172.21.16.87 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC20Jm+3OIYp7bnQnydjnDPPzQBiC20M41AIwy60No5FVCviAfzvoYiaMd7J2DG7Yr3OEO+T6UQI8JVkcYdgHxKBD5OywUKY00V+XnKbPMwIFjOxCeFLzL3HKYtsgy4SE6e0BB13GZ0Mw0L/YNX2D3zm12vRpBQjg/KM9W7v0nDJvwJCF/qKIqhb/R+2652TAQcvEObUXKwvxK7HnTX1oFxyQ7eA0nAfU1r4Sk2603r6StptFy2LjM8+JUgCKQU1Q/vwC7IXEcl1JiwxCyIYzWKlkQpOzdzArwreyMj1bxJZU8djwqrsIMVce1TBnuttXxHbBsGtVXvxHpiCX7BIept +ace.kenjim.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoxcHNz6bIus6udT265ukz/DvyVcxGdjmMUDvR/KzVOF5r0y49PTsFAn3JM5UsxNQuE8mOMa4RI+0dK65s8VcEjI6KWLSUzGuunPbyxu7n28h3QwiON2kRXTwBrC43GXoo8DA7O8/iPu0vgSWxa54h9vU92q3jL/vRZ0JZj6OhuGWlVVFi6n5mc1pebRQu+7fivMt2O7DibxKIgfQ4BFWtEkWZGidtmWXxshVTKdiM6hQyODgJLuxyx9unCO8W1wns1JH+WFVuR+essrszG2ESyq/D7UkqckW0RiR/yrbrqgnv/MbREMa6hHU14QGwrspyhYu3c1d30UQ3h8bBQhdD +216.241.98.198 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCoxcHNz6bIus6udT265ukz/DvyVcxGdjmMUDvR/KzVOF5r0y49PTsFAn3JM5UsxNQuE8mOMa4RI+0dK65s8VcEjI6KWLSUzGuunPbyxu7n28h3QwiON2kRXTwBrC43GXoo8DA7O8/iPu0vgSWxa54h9vU92q3jL/vRZ0JZj6OhuGWlVVFi6n5mc1pebRQu+7fivMt2O7DibxKIgfQ4BFWtEkWZGidtmWXxshVTKdiM6hQyODgJLuxyx9unCO8W1wns1JH+WFVuR+essrszG2ESyq/D7UkqckW0RiR/yrbrqgnv/MbREMa6hHU14QGwrspyhYu3c1d30UQ3h8bBQhdD +kenjim.englab.juniper.net ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2gTGBv0gmUO0zfFo5VRFsjQ4mTvADMMuchP22C0RyrOrwvTzO4fuJUnU3B3526IGjyszoHPyxTmHGRIlV1DrkO8CjeGCAPKotNMC9c7A08oxgc99+zqJuLAJnlEyoXsWXTZr6PO0oNTmS5wOb7FHRLaQKjkYY6ytjrdzIketvt40KEXw05oE4I+aeOCAH/Zl4xpqe8iUfT4TDKjdB9g5bmxjA8gr9xAZOSOUdzguHSwqxm/6iN2KX4/VmI0HsVQ6jJ10JdbDvuWG82qgtatjYcxfsfolKSEdXAkl6yGPbcLAvi8UpiB5iXPFdOe45QAHGOPt0FcAVJhEyx7OdQ+DTQ== +192.168.186.120 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2gTGBv0gmUO0zfFo5VRFsjQ4mTvADMMuchP22C0RyrOrwvTzO4fuJUnU3B3526IGjyszoHPyxTmHGRIlV1DrkO8CjeGCAPKotNMC9c7A08oxgc99+zqJuLAJnlEyoXsWXTZr6PO0oNTmS5wOb7FHRLaQKjkYY6ytjrdzIketvt40KEXw05oE4I+aeOCAH/Zl4xpqe8iUfT4TDKjdB9g5bmxjA8gr9xAZOSOUdzguHSwqxm/6iN2KX4/VmI0HsVQ6jJ10JdbDvuWG82qgtatjYcxfsfolKSEdXAkl6yGPbcLAvi8UpiB5iXPFdOe45QAHGOPt0FcAVJhEyx7OdQ+DTQ== +ttsv-db-01 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAtzrCQYyzWDmWV83/wKvsjdX5YlpWybUuuPeGihaoBW0vmo+qd9osNXOrE6h+rTCZcdFdDD7SdXYE8LiDEAqXAa8yIzsD6KtiVrYdsELYnXOP5jYPmi57TyRRhEsfIUNkji1yPPzixQoNQx63ehyv/i7EpV6YbtjXjJMihnrq7ipWQvHvubgwjLiHCnpm1Ie7TpQZZruhQdXOHnyhxisZG6Yc4MKXwd0cuIPgVDq0hOsC3++ouEU15hoepxWPFYKL76JIb0pnALStl8y3IhBDQbFiFXBWPiIw8fYHzDtFeX2Q6WMncoq7+aOeTvEIgEdI1j7XLjtbh+dfhRE5l7muYQ== +172.17.28.151 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAtzrCQYyzWDmWV83/wKvsjdX5YlpWybUuuPeGihaoBW0vmo+qd9osNXOrE6h+rTCZcdFdDD7SdXYE8LiDEAqXAa8yIzsD6KtiVrYdsELYnXOP5jYPmi57TyRRhEsfIUNkji1yPPzixQoNQx63ehyv/i7EpV6YbtjXjJMihnrq7ipWQvHvubgwjLiHCnpm1Ie7TpQZZruhQdXOHnyhxisZG6Yc4MKXwd0cuIPgVDq0hOsC3++ouEU15hoepxWPFYKL76JIb0pnALStl8y3IhBDQbFiFXBWPiIw8fYHzDtFeX2Q6WMncoq7+aOeTvEIgEdI1j7XLjtbh+dfhRE5l7muYQ== +bng-tt-db01 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA636YWeKfI0e9lkzUiwYagKJtA6WAYB6Yu1prTJ70uStY5/Jh0jx/eTKz9I3UkzucHc4HpHo/rib6BMO+qAHtEcn2ZJTvTO+QvWPO8Es6bU3tIxuIhL1sjFZfZL8HFS25udv8++7FZ/mALnoCDdyMGN16tRMrmPdW3WB9j3xPfbD231/jEj7ArQSc61YTF5TuTSh0e+bd4T4NY8QZ4sKHv86GbryT5XIbx4sm37dzI+F66MXCa90xPfujqRhgZ9Lmj32I6plKjbwVaGc8/12lzcZcnhMO9TiuLts81wSYBGg3+W96keyZNV+SaVaRqQYdTq97e2KGiAHYidlNIBS9pQ== +10.212.163.154 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA636YWeKfI0e9lkzUiwYagKJtA6WAYB6Yu1prTJ70uStY5/Jh0jx/eTKz9I3UkzucHc4HpHo/rib6BMO+qAHtEcn2ZJTvTO+QvWPO8Es6bU3tIxuIhL1sjFZfZL8HFS25udv8++7FZ/mALnoCDdyMGN16tRMrmPdW3WB9j3xPfbD231/jEj7ArQSc61YTF5TuTSh0e+bd4T4NY8QZ4sKHv86GbryT5XIbx4sm37dzI+F66MXCa90xPfujqRhgZ9Lmj32I6plKjbwVaGc8/12lzcZcnhMO9TiuLts81wSYBGg3+W96keyZNV+SaVaRqQYdTq97e2KGiAHYidlNIBS9pQ== +adp-www-01.englab.juniper.net ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2gTGBv0gmUO0zfFo5VRFsjQ4mTvADMMuchP22C0RyrOrwvTzO4fuJUnU3B3526IGjyszoHPyxTmHGRIlV1DrkO8CjeGCAPKotNMC9c7A08oxgc99+zqJuLAJnlEyoXsWXTZr6PO0oNTmS5wOb7FHRLaQKjkYY6ytjrdzIketvt40KEXw05oE4I+aeOCAH/Zl4xpqe8iUfT4TDKjdB9g5bmxjA8gr9xAZOSOUdzguHSwqxm/6iN2KX4/VmI0HsVQ6jJ10JdbDvuWG82qgtatjYcxfsfolKSEdXAkl6yGPbcLAvi8UpiB5iXPFdOe45QAHGOPt0FcAVJhEyx7OdQ+DTQ== +192.168.186.238 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2gTGBv0gmUO0zfFo5VRFsjQ4mTvADMMuchP22C0RyrOrwvTzO4fuJUnU3B3526IGjyszoHPyxTmHGRIlV1DrkO8CjeGCAPKotNMC9c7A08oxgc99+zqJuLAJnlEyoXsWXTZr6PO0oNTmS5wOb7FHRLaQKjkYY6ytjrdzIketvt40KEXw05oE4I+aeOCAH/Zl4xpqe8iUfT4TDKjdB9g5bmxjA8gr9xAZOSOUdzguHSwqxm/6iN2KX4/VmI0HsVQ6jJ10JdbDvuWG82qgtatjYcxfsfolKSEdXAkl6yGPbcLAvi8UpiB5iXPFdOe45QAHGOPt0FcAVJhEyx7OdQ+DTQ== +ttsv-shell11 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2gTGBv0gmUO0zfFo5VRFsjQ4mTvADMMuchP22C0RyrOrwvTzO4fuJUnU3B3526IGjyszoHPyxTmHGRIlV1DrkO8CjeGCAPKotNMC9c7A08oxgc99+zqJuLAJnlEyoXsWXTZr6PO0oNTmS5wOb7FHRLaQKjkYY6ytjrdzIketvt40KEXw05oE4I+aeOCAH/Zl4xpqe8iUfT4TDKjdB9g5bmxjA8gr9xAZOSOUdzguHSwqxm/6iN2KX4/VmI0HsVQ6jJ10JdbDvuWG82qgtatjYcxfsfolKSEdXAkl6yGPbcLAvi8UpiB5iXPFdOe45QAHGOPt0FcAVJhEyx7OdQ+DTQ== +172.17.38.151 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA2gTGBv0gmUO0zfFo5VRFsjQ4mTvADMMuchP22C0RyrOrwvTzO4fuJUnU3B3526IGjyszoHPyxTmHGRIlV1DrkO8CjeGCAPKotNMC9c7A08oxgc99+zqJuLAJnlEyoXsWXTZr6PO0oNTmS5wOb7FHRLaQKjkYY6ytjrdzIketvt40KEXw05oE4I+aeOCAH/Zl4xpqe8iUfT4TDKjdB9g5bmxjA8gr9xAZOSOUdzguHSwqxm/6iN2KX4/VmI0HsVQ6jJ10JdbDvuWG82qgtatjYcxfsfolKSEdXAkl6yGPbcLAvi8UpiB5iXPFdOe45QAHGOPt0FcAVJhEyx7OdQ+DTQ== +127.0.0.1 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDqMmo6L9Q5prZ28h8QzyNwmWTu1Xx2lEwVfbpyY0YwzMU6MB8bb8e/zYMfmSTmh4bIxjJIiGtyJHLPuJnXEG08nl/4fJmiAPxUOJzDDp4f5xojrnDc2+4q8vHxQ8EWSuesJf7wUR93H8tm7z6GwADQCk40MGb2BHI0XpBxoZHMPZnyaPLFBn3n76O2B9a5manhJH8rHJt5XaASm47eH9YK4CV+HcSBP13lLFVZg2S6Qcb57sq3ctzRkk4Pp9/LgFcgMwAhPTaNTdoESUozIOfoy/kdPjCYm2QE2F2u//xrseyaKqK/ZZxE8Ho/CCsov/3tQI/UPVXSNe0xXljejxid +ttsv-lrm-apps-01 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAlhhVZyBk526hHZ8F1rHK7IhxevyXRP8Bwui3PvDC32LVXA34L/Adcg5XLLK2tItbMTiU9pvuLYunoqo2wVABPuDEXNzJJO3LF2lE8HJHC4989Ot519DJw8HGxtDVvhTHOhQSBXx1uaRgqsv5ipMB6oHZzVw8AhzbfFayWVN0EXd4EEYUIPWjt8KglTpboInGWZ0P2B67WXffVwzQIrJ0jkimn+Soxq8Ah4f6V33fNGx0wXZ5jMtmsIg+SzgdPtNlmcnZDqdXBVij/oUy+4r7Z07uD/Gs8EV6QoGTElcr2RJ66VzV6rlr5Hre4YboNAE3ChF9OmH7MZKW5PjXvvZ3cQ== diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..08c3ee3 --- /dev/null +++ b/install.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# Portable dotfiles restore script — generated by dotfiles_manager.sh +# Run on a new machine after: git clone http://172.27.0.35:3000/kenjim/dotfiles ~/dotfiles +set -euo pipefail + +DOTFILES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MANIFEST="$DOTFILES_DIR/.dotfiles_manifest" +BACKUP_DIR="$HOME/.dotfiles_backup/$(date +%Y%m%d_%H%M%S)" + +echo "=== Restoring dotfiles from $DOTFILES_DIR ===" + +[ -f "$MANIFEST" ] || { echo "No manifest found."; exit 1; } + +while IFS= read -r rel || [ -n "$rel" ]; do + [ -z "$rel" ] && continue + src="$DOTFILES_DIR/$rel" + dest="$HOME/$rel" + [ -e "$src" ] || { echo " MISSING: $src"; continue; } + mkdir -p "$(dirname "$dest")" + if [ -e "$dest" ] && [ ! -L "$dest" ]; then + mkdir -p "$BACKUP_DIR/$(dirname "$rel")" + cp -a "$dest" "$BACKUP_DIR/$rel" + echo " Backed up: $dest" + rm -f "$dest" + elif [ -L "$dest" ]; then + rm "$dest" + fi + [[ "$rel" == .ssh/* ]] && chmod 600 "$src" 2>/dev/null || true + ln -sf "$src" "$dest" + echo " Linked: ~/$rel" +done < "$MANIFEST" + +echo +echo "✓ Dotfiles symlinks applied." + +# ---- Machine-local config ---- +# Write a minimal .bashrc.local if one does not exist (user edits profile) +if [ ! -f "$HOME/.bashrc.local" ]; then + echo + echo "Select profile for this machine:" + echo " [1] work — OneDrive" + echo " [2] personal — ProtonDrive + GoogleDrive" + read -r -p "Profile (1/2) [2]: " _choice + _profile="personal" + [[ "${_choice:-2}" == "1" ]] && _profile="work" + # setup script lives inside dotfiles now: dotfiles/scripts/setup_enterprise_ai_bash.sh + if [ -f "$DOTFILES_DIR/scripts/setup_enterprise_ai_bash.sh" ]; then + MACHINE_PROFILE="$_profile" bash "$DOTFILES_DIR/scripts/setup_enterprise_ai_bash.sh" + else + echo " ⚠ Could not find setup_enterprise_ai_bash.sh — create ~/.bashrc.local manually." + fi +fi + +# ---- SSH key decrypt ---- +gpg_count=$(find "$DOTFILES_DIR/.ssh/keys" -maxdepth 1 -name '*.gpg' 2>/dev/null | wc -l | tr -d ' ') +if [[ "$gpg_count" -gt 0 ]]; then + echo + echo "Found $gpg_count GPG-encrypted SSH key(s) in dotfiles." + read -r -p "Decrypt SSH private keys now? (y/n): " _dec + if [[ "$_dec" == [yY] ]]; then + # dotfiles_manager.sh lives inside dotfiles now: dotfiles/scripts/dotfiles_manager.sh + bash "$DOTFILES_DIR/scripts/dotfiles_manager.sh" ssh-import + fi +fi + +echo +echo "✓ Restore complete. Run: source ~/.bash_profile" diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh new file mode 100755 index 0000000..51fa23f --- /dev/null +++ b/scripts/bootstrap.sh @@ -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." diff --git a/scripts/dotfiles_manager.sh b/scripts/dotfiles_manager.sh new file mode 100755 index 0000000..da2343f --- /dev/null +++ b/scripts/dotfiles_manager.sh @@ -0,0 +1,958 @@ +#!/usr/bin/env bash +# ============================================================================= +# dotfiles_manager.sh — Centralized Dotfiles Management +# +# Manages shell configs, SSH config, and editor settings via a git repo +# hosted on your local Gitea server. +# +# Remote: http://172.27.0.35:3000/kenjim/dotfiles +# Strategy: Files live in ~/dotfiles/, HOME locations are symlinked to them. +# +# Usage: +# ./dotfiles_manager.sh [args] +# +# Commands: +# init Clone remote repo (or init + set remote if new machine) +# add Track a file: move it into ~/dotfiles, create symlink back +# remove Untrack: restore file to HOME, remove symlink record +# install Reapply all symlinks (use on a new machine after cloning) +# sync Pull latest from remote, reapply any new symlinks +# push [message] Stage all changes, commit, and push to remote +# status Show tracked files and their symlink health +# list Alias for status +# ssh-setup Interactively add SSH config and optionally keys +# ssh-export GPG-encrypt private keys → dotfiles/.ssh/keys/*.gpg +# ssh-import Decrypt GPG-encrypted keys from dotfiles to ~/.ssh/ +# remote-bootstrap SSH into another machine and run full setup +# help Show this message +# +# File layout inside ~/dotfiles/: +# .bashrc → symlinked from ~/.bashrc +# .bash_profile → symlinked from ~/.bash_profile +# .bash_aliases → symlinked from ~/.bash_aliases +# .gitconfig → symlinked from ~/.gitconfig +# .ssh/config → symlinked from ~/.ssh/config +# .ssh/keys/ → encrypted or public-only keys (see ssh-setup) +# .vimrc → symlinked from ~/.vimrc +# .tmux.conf → symlinked from ~/.tmux.conf +# .inputrc → symlinked from ~/.inputrc +# .dotfiles_manifest internal list of tracked HOME paths +# install.sh portable restore script (run on new machines) +# README.md documentation +# ============================================================================= + +set -euo pipefail + +# ----------------------------------------------------------------------- +# CONFIG — override with env vars if needed +# ----------------------------------------------------------------------- +DOTFILES_DIR="${DOTFILES_DIR:-$HOME/dotfiles}" +DOTFILES_REMOTE="${DOTFILES_REMOTE:-http://172.27.0.35:3000/kenjim/dotfiles}" +MANIFEST="$DOTFILES_DIR/.dotfiles_manifest" +BACKUP_DIR="$HOME/.dotfiles_backup/$(date +%Y%m%d_%H%M%S)" + +# ----------------------------------------------------------------------- +# COLORS +# ----------------------------------------------------------------------- +RED='\033[0;31m'; YELLOW='\033[1;33m'; GREEN='\033[0;32m' +CYAN='\033[0;36m'; BOLD='\033[1m'; RESET='\033[0m' + +info() { echo -e "${CYAN}[dotfiles]${RESET} $*"; } +success() { echo -e "${GREEN}[dotfiles]${RESET} ✓ $*"; } +warn() { echo -e "${YELLOW}[dotfiles]${RESET} ⚠ $*"; } +error() { echo -e "${RED}[dotfiles]${RESET} ✗ $*" >&2; } +bold() { echo -e "${BOLD}$*${RESET}"; } +die() { error "$*"; exit 1; } + +# ----------------------------------------------------------------------- +# HELPERS +# ----------------------------------------------------------------------- + +# Resolve the relative path stored in manifest → absolute HOME path +manifest_to_home() { + # entries are stored relative to HOME, e.g. ".bashrc" or ".ssh/config" + echo "$HOME/$1" +} + +# Compute the path inside dotfiles/ for a given HOME path +home_to_dotfiles() { + local home_path="$1" + local rel="${home_path#"$HOME/"}" + echo "$DOTFILES_DIR/$rel" +} + +# Add entry to manifest (deduplicates) +manifest_add() { + local rel="${1#"$HOME/"}" + mkdir -p "$(dirname "$MANIFEST")" + touch "$MANIFEST" + grep -qxF "$rel" "$MANIFEST" || echo "$rel" >> "$MANIFEST" + # Keep manifest sorted for clean diffs + sort -o "$MANIFEST" "$MANIFEST" +} + +# Remove entry from manifest +manifest_remove() { + local rel="${1#"$HOME/"}" + [ -f "$MANIFEST" ] && sed -i.bak "/^${rel//\//\\/}$/d" "$MANIFEST" && rm -f "${MANIFEST}.bak" +} + +# Check if dotfiles dir is a git repo +is_git_repo() { + git -C "$DOTFILES_DIR" rev-parse --git-dir &>/dev/null +} + +# Git command scoped to dotfiles dir +dgit() { + git -C "$DOTFILES_DIR" "$@" +} + +# ----------------------------------------------------------------------- +# COMMAND: init +# ----------------------------------------------------------------------- +cmd_init() { + bold "=== Initializing dotfiles repo ===" + info "Local path : $DOTFILES_DIR" + info "Remote : $DOTFILES_REMOTE" + echo + + # Check if remote is reachable + if curl --silent --max-time 5 --output /dev/null "$DOTFILES_REMOTE" 2>/dev/null; then + info "Remote server is reachable." + else + warn "Remote server not reachable right now. Will init locally and set remote for later." + fi + + if [ -d "$DOTFILES_DIR/.git" ]; then + warn "Dotfiles repo already exists at $DOTFILES_DIR" + # Ensure remote is set correctly + if ! dgit remote get-url origin &>/dev/null; then + dgit remote add origin "$DOTFILES_REMOTE" + success "Remote 'origin' added: $DOTFILES_REMOTE" + else + local current_remote + current_remote=$(dgit remote get-url origin) + if [ "$current_remote" != "$DOTFILES_REMOTE" ]; then + dgit remote set-url origin "$DOTFILES_REMOTE" + success "Remote updated to: $DOTFILES_REMOTE" + else + info "Remote already set correctly." + fi + fi + return + fi + + # Try to clone first + if curl --silent --max-time 5 --output /dev/null "$DOTFILES_REMOTE" 2>/dev/null; then + info "Attempting to clone from remote..." + if git clone "$DOTFILES_REMOTE" "$DOTFILES_DIR" 2>/dev/null; then + success "Cloned from $DOTFILES_REMOTE" + info "Run './dotfiles_manager.sh install' to apply symlinks." + return + else + warn "Clone failed (repo may be empty). Initializing locally instead." + fi + fi + + # Init fresh repo + mkdir -p "$DOTFILES_DIR" + cd "$DOTFILES_DIR" + git init + dgit remote add origin "$DOTFILES_REMOTE" + + # Create .gitignore — protect private SSH keys by default + cat > "$DOTFILES_DIR/.gitignore" <<'GITIGNORE' +# macOS +.DS_Store +.DS_Store? +._* + +# Backup artifacts +*.bak +*.orig + +# SSH private keys — never commit unencrypted private keys +# Remove a line below only if you store GPG-encrypted versions +.ssh/id_rsa +.ssh/id_ed25519 +.ssh/id_ecdsa +.ssh/id_dsa +.ssh/keys/*_rsa +.ssh/keys/*_ed25519 +.ssh/keys/*_ecdsa +.ssh/keys/*.pem +# Public keys and config are fine +!.ssh/*.pub +!.ssh/keys/*.pub +!.ssh/config +!.ssh/known_hosts + +# GPG-encrypted private key backups are safe to commit +!.ssh/keys/*.gpg + +# Secrets / tokens — never commit +.env +.env.* +*.token +*.secrets +vault/ + +# Machine-local overrides — never commit (written by setup_enterprise_ai_bash.sh) +.bashrc.local +.bash_profile.local +GITIGNORE + + # Seed README + cat > "$DOTFILES_DIR/README.md" < +MARKDOWN + + touch "$MANIFEST" + + dgit add . + dgit commit -m "chore: initial dotfiles skeleton" + + success "Dotfiles repo initialized at $DOTFILES_DIR" + info "Push when ready: ./dotfiles_manager.sh push 'initial commit'" +} + +# ----------------------------------------------------------------------- +# COMMAND: add +# ----------------------------------------------------------------------- +cmd_add() { + [ $# -ge 1 ] || die "Usage: add [ ...]" + + for target in "$@"; do + # Expand ~ + target="${target/#\~/$HOME}" + target="$(realpath -m "$target" 2>/dev/null || echo "$target")" + + if [ ! -e "$target" ] && [ ! -L "$target" ]; then + warn "Skipping '$target': does not exist." + continue + fi + + local rel="${target#"$HOME/"}" + local dest="$DOTFILES_DIR/$rel" + + if [ -L "$target" ] && [ "$(readlink "$target")" = "$dest" ]; then + info "$rel is already tracked and symlinked." + continue + fi + + # Create destination parent directories + mkdir -p "$(dirname "$dest")" + + # Back up existing dotfiles destination if present and not a symlink + if [ -e "$dest" ] && [ ! -L "$dest" ]; then + mkdir -p "$BACKUP_DIR/$(dirname "$rel")" + cp -a "$dest" "$BACKUP_DIR/$rel" + warn "Backed up existing $dest → $BACKUP_DIR/$rel" + fi + + # Move file + if [ -L "$target" ]; then + # If it's already a symlink to somewhere else, copy the contents + cp -a "$(readlink "$target")" "$dest" + rm "$target" + else + mv "$target" "$dest" + fi + + # Create symlink + ln -sf "$dest" "$target" + manifest_add "$target" + + success "Tracked: $rel\n $target → $dest" + done +} + +# ----------------------------------------------------------------------- +# COMMAND: remove +# ----------------------------------------------------------------------- +cmd_remove() { + [ $# -ge 1 ] || die "Usage: remove " + local target="$1" + target="${target/#\~/$HOME}" + local rel="${target#"$HOME/"}" + local src="$DOTFILES_DIR/$rel" + + if [ ! -e "$src" ]; then + die "Not tracked: $rel" + fi + + if [ -L "$target" ]; then + rm "$target" + fi + + cp -a "$src" "$target" + manifest_remove "$target" + success "Untracked $rel — file restored to $target" + info "The copy in $src still exists. Remove it manually if desired." +} + +# ----------------------------------------------------------------------- +# COMMAND: install (idempotent — safe to re-run) +# ----------------------------------------------------------------------- +cmd_install() { + bold "=== Applying dotfiles symlinks ===" + [ -f "$MANIFEST" ] || { warn "Manifest empty — nothing to install."; return; } + + local count=0 skipped=0 errors=0 + while IFS= read -r rel || [ -n "$rel" ]; do + [ -z "$rel" ] && continue + local src="$DOTFILES_DIR/$rel" + local dest="$HOME/$rel" + + if [ ! -e "$src" ]; then + warn "Missing in dotfiles: $src — skipping." + (( errors++ )) || true + continue + fi + + mkdir -p "$(dirname "$dest")" + + if [ -L "$dest" ] && [ "$(readlink "$dest")" = "$src" ]; then + (( skipped++ )) || true + continue + fi + + # Back up conflicting file + if [ -e "$dest" ] && [ ! -L "$dest" ]; then + mkdir -p "$BACKUP_DIR/$(dirname "$rel")" + cp -a "$dest" "$BACKUP_DIR/$rel" + warn "Backed up existing $dest" + rm -f "$dest" + elif [ -L "$dest" ]; then + rm "$dest" + fi + + # Set safe permissions for SSH files + if [[ "$rel" == .ssh/* ]]; then + if [ -d "$src" ]; then + chmod 700 "$src" + else + chmod 600 "$src" + fi + fi + + ln -sf "$src" "$dest" + success "Linked: ~/$rel → $src" + (( count++ )) || true + done < "$MANIFEST" + + echo + bold "Install complete: $count linked, $skipped already up-to-date, $errors errors." + + # Offer ssh-import if GPG-encrypted keys are present but private key is missing + local gpg_count=0 + gpg_count=$(find "$DOTFILES_DIR/.ssh/keys" -maxdepth 1 -name '*.gpg' 2>/dev/null | wc -l | tr -d ' ') + if [[ "$gpg_count" -gt 0 ]]; then + local missing_keys=false + for gpg_f in "$DOTFILES_DIR/.ssh/keys/"*.gpg; do + local base_name; base_name="$(basename "${gpg_f%.gpg}")" + [ ! -f "$HOME/.ssh/$base_name" ] && missing_keys=true && break + done + if $missing_keys; then + echo + warn "$gpg_count GPG-encrypted SSH key(s) found in dotfiles but not yet decrypted." + read -r -p "Decrypt SSH keys now? (y/n): " _imp + [[ "$_imp" == [yY] ]] && cmd_ssh_import + fi + fi +} + +# ----------------------------------------------------------------------- +# COMMAND: sync +# ----------------------------------------------------------------------- +cmd_sync() { + bold "=== Syncing from remote ===" + is_git_repo || die "Not a git repo: $DOTFILES_DIR. Run 'init' first." + + # Stash any local changes so pull is clean + local stashed=false + if ! dgit diff --quiet || ! dgit diff --cached --quiet; then + warn "Local changes detected — stashing before pull." + dgit stash push -m "dotfiles_manager auto-stash before sync" + stashed=true + fi + + dgit pull --rebase origin main 2>/dev/null || dgit pull --rebase origin master 2>/dev/null || { + warn "Could not pull (remote unreachable or branch mismatch). Working offline." + } + + if $stashed; then + dgit stash pop || warn "Stash pop had conflicts. Resolve in $DOTFILES_DIR" + fi + + cmd_install + success "Sync complete." +} + +# ----------------------------------------------------------------------- +# COMMAND: push +# ----------------------------------------------------------------------- +cmd_push() { + local msg="${1:-"chore: update dotfiles $(date '+%Y-%m-%d %H:%M')"}" + bold "=== Pushing to remote ===" + is_git_repo || die "Not a git repo. Run 'init' first." + + dgit add --all + + if dgit diff --cached --quiet; then + info "Nothing to commit." + else + dgit commit -m "$msg" + success "Committed: $msg" + fi + + # Determine default branch + local branch + branch=$(dgit rev-parse --abbrev-ref HEAD) + + # Set upstream on first push if needed + if ! dgit config "branch.$branch.remote" &>/dev/null; then + dgit push --set-upstream origin "$branch" + else + dgit push origin "$branch" + fi + + success "Pushed to $DOTFILES_REMOTE ($branch)." +} + +# ----------------------------------------------------------------------- +# COMMAND: status +# ----------------------------------------------------------------------- +cmd_status() { + bold "=== Dotfiles Status ===" + info "Repo : $DOTFILES_DIR" + info "Remote : $DOTFILES_REMOTE" + echo + + if [ ! -f "$MANIFEST" ] || [ ! -s "$MANIFEST" ]; then + echo " (no files tracked yet — use 'add' to start)" + return + fi + + printf " %-42s %s\n" "HOME PATH" "STATUS" + printf " %-42s %s\n" "-----------------------------------------" "-------" + + while IFS= read -r rel || [ -n "$rel" ]; do + [ -z "$rel" ] && continue + local home_path="$HOME/$rel" + local src="$DOTFILES_DIR/$rel" + local status + + if [ ! -e "$src" ]; then + status="${RED}MISSING in dotfiles${RESET}" + elif [ -L "$home_path" ] && [ "$(readlink "$home_path")" = "$src" ]; then + status="${GREEN}OK (symlinked)${RESET}" + elif [ -e "$home_path" ] && [ ! -L "$home_path" ]; then + status="${YELLOW}CONFLICT (real file exists at HOME)${RESET}" + elif [ ! -e "$home_path" ]; then + status="${YELLOW}NOT LINKED (run install)${RESET}" + else + status="${YELLOW}STALE SYMLINK${RESET}" + fi + + printf " %-42s " "~/$rel" + echo -e "$status" + done < "$MANIFEST" + + echo + if is_git_repo; then + bold "Git status:" + dgit status --short + echo + local last_commit + last_commit=$(dgit log -1 --format="%h %s (%ar)" 2>/dev/null || echo "no commits yet") + info "Last commit: $last_commit" + fi +} + +# ----------------------------------------------------------------------- +# COMMAND: ssh-setup +# ----------------------------------------------------------------------- +cmd_ssh_setup() { + bold "=== SSH Config & Key Management ===" + echo + warn "SECURITY REMINDER: Private SSH key files are in .gitignore by default." + warn "Only ~/.ssh/config, known_hosts, and .pub files will be committed." + warn "To store encrypted private keys, use GPG encryption first." + echo + + local ssh_config="$HOME/.ssh/config" + local ssh_known="$HOME/.ssh/known_hosts" + local dotfiles_ssh="$DOTFILES_DIR/.ssh" + mkdir -p "$dotfiles_ssh/keys" + chmod 700 "$dotfiles_ssh" + + # Track ssh/config + if [ -f "$ssh_config" ]; then + read -r -p "Track ~/.ssh/config in dotfiles? (y/n): " yn + if [[ "$yn" == [yY] ]]; then + cmd_add "$ssh_config" + fi + else + info "No ~/.ssh/config found. Creating a template..." + mkdir -p "$HOME/.ssh" + cat > "$dotfiles_ssh/config" <<'SSH_CONFIG' +# SSH Client Configuration +# Managed by dotfiles_manager.sh +# See: man ssh_config + +Host * + ServerAliveInterval 60 + ServerAliveCountMax 3 + AddKeysToAgent yes + IdentitiesOnly yes + +# Example host alias: +# Host myserver +# HostName 192.168.1.100 +# User kenji +# IdentityFile ~/.ssh/id_ed25519 +# Port 22 + +# Local Gitea server +Host gitea-local + HostName 172.27.0.35 + User git + Port 22 + IdentitiesOnly yes + # IdentityFile ~/.ssh/id_ed25519_gitea + +SSH_CONFIG + chmod 600 "$dotfiles_ssh/config" + ln -sf "$dotfiles_ssh/config" "$HOME/.ssh/config" + manifest_add "$HOME/.ssh/config" + success "Created and tracked ~/.ssh/config" + fi + + # Track known_hosts (useful for pre-seeding new machines) + if [ -f "$ssh_known" ]; then + read -r -p "Track ~/.ssh/known_hosts in dotfiles? (y/n): " yn + [[ "$yn" == [yY] ]] && cmd_add "$ssh_known" + fi + + # Track public keys + local pub_keys=() + while IFS= read -r -d '' f; do + pub_keys+=("$f") + done < <(find "$HOME/.ssh" -maxdepth 1 -name "*.pub" -print0 2>/dev/null) + + if [ ${#pub_keys[@]} -gt 0 ]; then + echo + info "Found public keys:" + for k in "${pub_keys[@]}"; do echo " $k"; done + read -r -p "Copy public keys to dotfiles/.ssh/keys/? (y/n): " yn + if [[ "$yn" == [yY] ]]; then + for pubkey in "${pub_keys[@]}"; do + cp "$pubkey" "$dotfiles_ssh/keys/" + success "Copied: $(basename "$pubkey") → dotfiles/.ssh/keys/" + done + fi + fi + + # Offer to store encrypted private keys + echo + warn "Private key storage (advanced — optional):" + info "If you want to store encrypted private key backups, use:" + echo " gpg --symmetric --cipher-algo AES256 ~/.ssh/id_ed25519" + echo " mv ~/.ssh/id_ed25519.gpg ~/dotfiles/.ssh/keys/" + echo " Then amend .gitignore to allow the .gpg file." + echo + success "SSH setup complete." + echo + info "Next step: GPG-encrypt your private keys for cross-machine sharing:" + info " dotfiles ssh-export → encrypt keys into dotfiles/.ssh/keys/*.gpg" + info " dotfiles push → commit and push to $DOTFILES_REMOTE" + info " dotfiles ssh-import → decrypt on any other machine after sync" +} + +# ----------------------------------------------------------------------- +# COMMAND: ssh-export (GPG-encrypt private keys → dotfiles/.ssh/keys/) +# ----------------------------------------------------------------------- +cmd_ssh_export() { + bold "=== Export SSH Private Keys (GPG-encrypted) ===" + command -v gpg &>/dev/null || die "gpg not installed. Install with: brew install gnupg" + + local dotfiles_ssh="$DOTFILES_DIR/.ssh/keys" + mkdir -p "$dotfiles_ssh" + + # Collect private keys (id_* files, no .pub extension, no .gpg) + local private_keys=() + while IFS= read -r -d '' f; do + private_keys+=("$f") + done < <(find "$HOME/.ssh" -maxdepth 1 -type f \ + \( -name "id_*" ! -name "*.pub" ! -name "*.gpg" \) -print0 2>/dev/null) + + if [ ${#private_keys[@]} -eq 0 ]; then + info "No private keys found in ~/.ssh/" + return + fi + + info "Found private keys:" + for k in "${private_keys[@]}"; do echo " $k"; done + echo + warn "Each key will be GPG-encrypted with a symmetric passphrase you choose." + warn "Store this passphrase in a password manager — you need it to restore keys." + echo + read -r -p "Proceed? (y/n): " yn + [[ "$yn" == [yY] ]] || return + + for key in "${private_keys[@]}"; do + local key_name; key_name="$(basename "$key")" + local encrypted="$dotfiles_ssh/${key_name}.gpg" + + if [ -f "$encrypted" ]; then + read -r -p " $key_name.gpg exists. Re-encrypt? (y/n): " re_enc + [[ "$re_enc" == [yY] ]] || continue + rm "$encrypted" + fi + + if gpg --symmetric --cipher-algo AES256 --output "$encrypted" "$key"; then + success "Encrypted: $key → $encrypted" + else + warn "Encryption failed for $key_name — skipping." + rm -f "$encrypted" + fi + done + + # Ensure .gitignore allows .gpg files (idempotent) + local gitignore="$DOTFILES_DIR/.gitignore" + if ! grep -q "^!.ssh/keys/\*.gpg" "$gitignore" 2>/dev/null; then + printf '\n# GPG-encrypted private key backups are safe to commit\n!.ssh/keys/*.gpg\n' >> "$gitignore" + success "Updated .gitignore to allow .gpg files" + fi + + echo + success "SSH export complete. Run 'push' to save to $DOTFILES_REMOTE" +} + +# ----------------------------------------------------------------------- +# COMMAND: ssh-import (decrypt GPG keys from dotfiles/.ssh/keys/ → ~/.ssh/) +# ----------------------------------------------------------------------- +cmd_ssh_import() { + bold "=== Import SSH Private Keys (GPG decrypt) ===" + command -v gpg &>/dev/null || die "gpg not installed. Install with: brew install gnupg" + + local dotfiles_ssh="$DOTFILES_DIR/.ssh/keys" + local gpg_keys=() + while IFS= read -r -d '' f; do + gpg_keys+=("$f") + done < <(find "$dotfiles_ssh" -maxdepth 1 -name "*.gpg" -print0 2>/dev/null) + + if [ ${#gpg_keys[@]} -eq 0 ]; then + info "No GPG-encrypted keys found in dotfiles/.ssh/keys/" + info "Run 'ssh-export' first to encrypt your private keys." + return + fi + + info "Found encrypted keys:" + for k in "${gpg_keys[@]}"; do echo " $(basename "$k")"; done + echo + read -r -p "Decrypt and install to ~/.ssh/? (y/n): " yn + [[ "$yn" == [yY] ]] || return + + mkdir -p "$HOME/.ssh" + chmod 700 "$HOME/.ssh" + + local imported=0 + for gpg_key in "${gpg_keys[@]}"; do + local key_name; key_name="$(basename "${gpg_key%.gpg}")" + local dest="$HOME/.ssh/$key_name" + + if [ -f "$dest" ]; then + warn " $dest already exists — skipping. Remove it first to re-import." + continue + fi + + if gpg --decrypt --output "$dest" "$gpg_key"; then + chmod 600 "$dest" + success "Decrypted: $(basename "$gpg_key") → $dest" + (( imported++ )) || true + else + warn " Failed to decrypt $(basename "$gpg_key")" + rm -f "$dest" + fi + done + + # Add freshly imported keys to the running ssh-agent + if [[ "$imported" -gt 0 ]] && ssh-add -l &>/dev/null 2>&1; then + for gpg_key in "${gpg_keys[@]}"; do + local key_name; key_name="$(basename "${gpg_key%.gpg}")" + local dest="$HOME/.ssh/$key_name" + [ -f "$dest" ] && ssh-add "$dest" 2>/dev/null && info " Added to agent: $key_name" + done + fi + + echo + success "SSH import complete ($imported key(s) decrypted)." +} + +# ----------------------------------------------------------------------- +# COMMAND: remote-bootstrap (SSH into another machine and run full setup) +# ----------------------------------------------------------------------- +cmd_remote_bootstrap() { + [ $# -ge 1 ] || die "Usage: remote-bootstrap [--profile work|personal]" + + local target="$1"; shift + local profile="personal" + + # Parse optional --profile flag + while [[ $# -gt 0 ]]; do + case "$1" in + --profile) profile="${2:-personal}"; shift 2 ;; + *) die "Unknown option: $1" ;; + esac + done + + bold "=== Remote Bootstrap: $target (profile: $profile) ===" + echo + + # ---- 1. Verify SSH connectivity (prefer key-based auth) ---- + info "Testing SSH connectivity..." + if ! ssh -o ConnectTimeout=10 -o BatchMode=yes -o StrictHostKeyChecking=accept-new \ + "$target" "exit 0" 2>/dev/null; then + warn "Key-based SSH auth failed for $target." + read -r -p "Copy your SSH public key to $target now? (y/n): " yn + if [[ "$yn" == [yY] ]]; then + ssh-copy-id "$target" || die "ssh-copy-id failed. Fix SSH access to $target first." + else + die "SSH key access required. Run:\n ssh-copy-id $target" + fi + fi + success "SSH connection OK." + echo + + # ---- 2. Upload scripts ---- + local self_dir; self_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + info "Uploading scripts to $target:~/scripts/ ..." + ssh "$target" "mkdir -p ~/scripts" + scp -q \ + "$self_dir/dotfiles_manager.sh" \ + "$self_dir/setup_enterprise_ai_bash.sh" \ + "$target:~/scripts/" + ssh "$target" "chmod +x ~/scripts/dotfiles_manager.sh ~/scripts/setup_enterprise_ai_bash.sh" + success "Scripts uploaded." + echo + + # ---- 3. Run setup interactively over SSH (-t allocates a tty) ---- + info "Launching setup on $target (profile=$profile)..." + info "You will be prompted interactively on the remote machine." + echo + ssh -t "$target" \ + "MACHINE_PROFILE=${profile} DOTFILES_REMOTE=${DOTFILES_REMOTE} bash ~/scripts/setup_enterprise_ai_bash.sh" + + echo + success "Remote bootstrap of $target complete." + info "Log in and verify: ssh $target 'dotfiles status'" +} + +# ----------------------------------------------------------------------- +# COMMAND: help +# ----------------------------------------------------------------------- +cmd_help() { + cat < [args] + +${BOLD}COMMANDS — Core${RESET} + init Clone from remote or init locally with remote set + add [...] Track file(s): move to ~/dotfiles, symlink back to HOME + remove Untrack: restore file to HOME + install Reapply all symlinks (use on a new machine after cloning) + sync Pull latest from remote, reapply symlinks + push [message] Commit all changes and push to $DOTFILES_REMOTE + status / list Show tracked files and symlink health + +${BOLD}COMMANDS — SSH & Keys${RESET} + ssh-setup Guided SSH config + key migration + ssh-export GPG-encrypt private keys → dotfiles/.ssh/keys/*.gpg + ssh-import Decrypt GPG-encrypted keys from dotfiles to ~/.ssh/ + +${BOLD}COMMANDS — Multi-machine${RESET} + remote-bootstrap [--profile work|personal] + Upload scripts and run full setup on a remote machine + +${BOLD}QUICK START — this machine (work)${RESET} + ./dotfiles_manager.sh init + ./dotfiles_manager.sh add ~/.bashrc ~/.bash_profile ~/.gitconfig + ./dotfiles_manager.sh ssh-setup + ./dotfiles_manager.sh ssh-export # GPG-encrypt private keys + ./dotfiles_manager.sh push "initial migration" + +${BOLD}QUICK START — personal machine restore${RESET} + # Option A: remote bootstrap from work machine + ./dotfiles_manager.sh remote-bootstrap user@personal-mac --profile personal + + # Option B: manual steps on the personal machine + git clone $DOTFILES_REMOTE ~/dotfiles + ~/dotfiles/install.sh # applies symlinks, prompts for key decrypt + +${BOLD}.bashrc STRATEGY${RESET} + ~/.bashrc → tracked in dotfiles, shared across all machines + ~/.bashrc.local → NOT tracked, written by setup_enterprise_ai_bash.sh + contains profile-specific vars (cloud paths, MACHINE_PROFILE) + Work: OneDrive paths, CLOUD_ROOT=~/OneDrive + Personal: ProtonDrive + Google Drive, CLOUD_ROOT=~/Cloud + +${BOLD}SSH KEY SHARING STRATEGY${RESET} + ~/.ssh/config → tracked in dotfiles (shared) + ~/.ssh/*.pub → copied to dotfiles/.ssh/keys/ (shared) + ~/.ssh/id_* → NOT committed plain-text + ~/.ssh/id_*.gpg → GPG-encrypted backups committed to dotfiles, decrypted + on new machines with 'ssh-import' + +${BOLD}ENVIRONMENT OVERRIDES${RESET} + DOTFILES_DIR=$DOTFILES_DIR + DOTFILES_REMOTE=$DOTFILES_REMOTE +HELP +} + +# ----------------------------------------------------------------------- +# GENERATE PORTABLE install.sh (regenerated on each push) +# ----------------------------------------------------------------------- +generate_install_sh() { + cat > "$DOTFILES_DIR/install.sh" </dev/null || true + ln -sf "\$src" "\$dest" + echo " Linked: ~/\$rel" +done < "\$MANIFEST" + +echo +echo "✓ Dotfiles symlinks applied." + +# ---- Machine-local config ---- +# Write a minimal .bashrc.local if one does not exist (user edits profile) +if [ ! -f "\$HOME/.bashrc.local" ]; then + echo + echo "Select profile for this machine:" + echo " [1] work — OneDrive" + echo " [2] personal — ProtonDrive + GoogleDrive" + read -r -p "Profile (1/2) [2]: " _choice + _profile="personal" + [[ "\${_choice:-2}" == "1" ]] && _profile="work" + # setup script lives inside dotfiles now: dotfiles/scripts/setup_enterprise_ai_bash.sh + if [ -f "\$DOTFILES_DIR/scripts/setup_enterprise_ai_bash.sh" ]; then + MACHINE_PROFILE="\$_profile" bash "\$DOTFILES_DIR/scripts/setup_enterprise_ai_bash.sh" + else + echo " ⚠ Could not find setup_enterprise_ai_bash.sh — create ~/.bashrc.local manually." + fi +fi + +# ---- SSH key decrypt ---- +gpg_count=\$(find "\$DOTFILES_DIR/.ssh/keys" -maxdepth 1 -name '*.gpg' 2>/dev/null | wc -l | tr -d ' ') +if [[ "\$gpg_count" -gt 0 ]]; then + echo + echo "Found \$gpg_count GPG-encrypted SSH key(s) in dotfiles." + read -r -p "Decrypt SSH private keys now? (y/n): " _dec + if [[ "\$_dec" == [yY] ]]; then + # dotfiles_manager.sh lives inside dotfiles now: dotfiles/scripts/dotfiles_manager.sh + bash "\$DOTFILES_DIR/scripts/dotfiles_manager.sh" ssh-import + fi +fi + +echo +echo "✓ Restore complete. Run: source ~/.bash_profile" +INSTALL + chmod +x "$DOTFILES_DIR/install.sh" +} + +# ----------------------------------------------------------------------- +# ENTRYPOINT +# ----------------------------------------------------------------------- +main() { + local cmd="${1:-help}" + shift || true + + # Auto-generate install.sh when pushing + case "$cmd" in + init) cmd_init "$@" ;; + add) cmd_add "$@" ;; + remove) cmd_remove "$@" ;; + install) cmd_install "$@" ;; + sync) cmd_sync "$@" ;; + push) + generate_install_sh + cmd_push "$@" + ;; + status|list) cmd_status "$@" ;; + ssh-setup) cmd_ssh_setup "$@" ;; + ssh-export) cmd_ssh_export "$@" ;; + ssh-import) cmd_ssh_import "$@" ;; + remote-bootstrap) cmd_remote_bootstrap "$@" ;; + help|--help|-h) cmd_help ;; + *) + error "Unknown command: $cmd" + cmd_help + exit 1 + ;; + esac +} + +main "$@" diff --git a/scripts/setup_enterprise_ai_bash.sh b/scripts/setup_enterprise_ai_bash.sh new file mode 100755 index 0000000..3d669cb --- /dev/null +++ b/scripts/setup_enterprise_ai_bash.sh @@ -0,0 +1,410 @@ +#!/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" + +# ------------------------------------------------------ +# 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 +