Compare commits

..

10 Commits

Author SHA1 Message Date
Kenji Morishige
28485a9be6 add ~/.local/bin to PATH and git tree alias
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 08:18:57 -05:00
Kenji Morishige
72ffe3c30d initial dotfiles migration from kenjim-mbm 2026-03-30 08:41:51 -05:00
Kenji Morishige
b23f5d7e5f latest to support taas shells 2026-02-25 15:20:07 -06:00
Kenji Morishige
d25dac1897 added taas machines as work type machines 2026-02-24 10:00:10 -06:00
Kenji Morishige
67926c37bd NFS-aware .bashrc.local and VS Code dirs for work servers
- Remove zet* from _is_work_host (local git server, not a work host)
- setup_enterprise_ai_bash.sh: detect NFS home on work servers, relocate
  .bashrc.local to /opt/kenjim/ with symlink, handle re-runs safely
- setup_enterprise_ai_bash.sh: symlink ~/.vscode-server and
  ~/.vscode-remote-containers to /opt/kenjim/ on NFS hosts
- dotfiles_manager.sh: deploy-to detects NFS on remote, deploys
  .bashrc.local to /opt/kenjim/ with symlink, backs up symlink targets
- hosts/etqc-kenjim-11.bashrc.local: add NFS note to header
2026-02-24 09:57:29 -06:00
Kenji Morishige
074ab05908 adding ollama installer 2026-02-23 19:53:49 -06:00
Kenji Morishige
0fc1b3cba0 docs: document --run-setup and --profile flags in deploy-to section 2026-02-23 17:27:21 -06:00
Kenji Morishige
3860c8a33d feat: deploy-to --run-setup flag to bootstrap directory structure on remote 2026-02-23 17:27:00 -06:00
Kenji Morishige
16cbadd016 fix: deploy-to uses scp -r for directories (fixes .bashrc.d upload) 2026-02-23 17:17:15 -06:00
Kenji Morishige
a6296da5df feat: add hosts/ convention for centrally managed server .bashrc.local
- hosts/etqc-kenjim-11.bashrc.local: per-host local config for work server,
  managed from kenjim-mbp and deployed via 'dotfiles deploy-to'.
  Credentials replaced with CHANGEME placeholders — set real values on
  server after first deploy, never commit actual secrets.
- dotfiles_manager.sh: deploy-to step 5 auto-detects hosts/<hostname>.bashrc.local
  and SCPs it to ~/.bashrc.local on the remote (with backup of existing file)
- .gitignore: clarify that hosts/*.bashrc.local is intentionally tracked
  (existing .bashrc.local rule only matches the exact filename)
- README.md: document hosts/ layout, workflow, and credential placeholder strategy
2026-02-23 17:12:29 -06:00
13 changed files with 640 additions and 20 deletions

View File

@@ -32,3 +32,8 @@ fi
# Legacy aliases file (kept for compatibility) # Legacy aliases file (kept for compatibility)
[ -f ~/.aliases ] && source ~/.aliases [ -f ~/.aliases ] && source ~/.aliases
# OpenClaw Completion
#source "/Users/kenjim/.openclaw/completions/openclaw.bash"
#export OPENCLAW_MODEL=kimi-k2.5:cloud
export PATH="$HOME/.local/bin:$PATH"

View File

@@ -14,7 +14,7 @@ _is_work_host() {
[[ "${MACHINE_PROFILE:-}" == "work" ]] && return 0 [[ "${MACHINE_PROFILE:-}" == "work" ]] && return 0
# Fallback hostname pattern for work servers where .bashrc.local may not exist # Fallback hostname pattern for work servers where .bashrc.local may not exist
case "$(hostname -s)" in case "$(hostname -s)" in
kenjim-mbp*|etqc-*|etbg-*|engtech-dev-*|zet*) return 0 ;; kenjim-mbp*|etqc-*|etbg-*|engtech-dev-*|qnc-kenjim-toby-shell*|qtaas*|bng-kenjim-toby-shell*|btaas*|kenjim-taas*) return 0 ;;
*) return 1 ;; *) return 1 ;;
esac esac
} }

View File

@@ -20,3 +20,4 @@ email = kenjim@juniper.net
br = branch br = branch
ci = commit ci = commit
unstage = reset HEAD -- unstage = reset HEAD --
tree = log --graph --oneline --all --decorate

6
.gitignore vendored
View File

@@ -33,6 +33,10 @@
*.secrets *.secrets
vault/ vault/
# Machine-local overrides — never commit (written by setup_enterprise_ai_bash.sh) # Machine-local overrides at HOME level — never commit (written by setup_enterprise_ai_bash.sh)
.bashrc.local .bashrc.local
.bash_profile.local .bash_profile.local
# Per-host .bashrc.local files ARE committed — managed centrally in dotfiles/hosts/
# Files are named <hostname>.bashrc.local and deployed via: dotfiles deploy-to user@host
# hosts/*.bashrc.local is intentionally tracked (gitignore rules above only match exact name)

View File

@@ -84,9 +84,12 @@ Host kold
HostName etqc-kenjim-01.juniper.net HostName etqc-kenjim-01.juniper.net
# TaaS dev machine # TaaS dev machine
Host ktaas Host qtaas
HostName kenjim-taas.qengk8.juniper.net HostName kenjim-taas.qengk8.juniper.net
Host btaas
HostName kenjim-taas.bengk8.juniper.net
# Temp machine — Bangalore K8 # Temp machine — Bangalore K8
Host ktb Host ktb
HostName kenjim-temp.bengk8.juniper.net HostName kenjim-temp.bengk8.juniper.net

121
README.md
View File

@@ -75,6 +75,8 @@ Three scripts drive the system:
│ │ ├── setup_enterprise_ai_bash.sh → symlinked from ~/scripts/setup_enterprise_ai_bash.sh │ │ ├── setup_enterprise_ai_bash.sh → symlinked from ~/scripts/setup_enterprise_ai_bash.sh
│ │ └── bootstrap.sh → symlinked from ~/scripts/bootstrap.sh │ │ └── bootstrap.sh → symlinked from ~/scripts/bootstrap.sh
│ ├── .dotfiles_manifest # internal list of tracked HOME-relative paths │ ├── .dotfiles_manifest # internal list of tracked HOME-relative paths
│ ├── hosts/
│ │ └── <hostname>.bashrc.local # per-server local configs (deployed via deploy-to)
│ ├── install.sh # portable restore script (auto-generated) │ ├── install.sh # portable restore script (auto-generated)
│ └── README.md # this file │ └── README.md # this file
@@ -230,6 +232,13 @@ on the remote.
# Push everything (dotfiles + scripts): # Push everything (dotfiles + scripts):
dotfiles deploy-to user@server dotfiles deploy-to user@server
# After file transfer, also run setup_enterprise_ai_bash.sh on the remote
# to create ~/workspace, ~/data, ~/models and write shell config:
dotfiles deploy-to user@server --run-setup
# Specify a machine profile for the setup script (default: work):
dotfiles deploy-to user@server --run-setup --profile work
# Scripts only (dotfiles_manager.sh, bootstrap.sh, setup script): # Scripts only (dotfiles_manager.sh, bootstrap.sh, setup script):
dotfiles deploy-to user@server --scripts-only dotfiles deploy-to user@server --scripts-only
@@ -249,6 +258,38 @@ Files are copied directly (not symlinked). Re-run `deploy-to` any time you
want to push updates. `~/.ssh/` is skipped by default to avoid accidentally want to push updates. `~/.ssh/` is skipped by default to avoid accidentally
pushing private keys or your personal known_hosts to a shared server. pushing private keys or your personal known_hosts to a shared server.
> **First-time setup on a server?** Use `--run-setup` to create the full
> directory structure after file transfer, or answer `y` when prompted
> interactively at the end of a normal `deploy-to` run.
### Centrally managing `~/.bashrc.local` for servers
Work servers can't reach the Gitea repo, so their `~/.bashrc.local` is managed
centrally from `kenjim-mbp` using per-host files in `dotfiles/hosts/`:
```
dotfiles/hosts/
└── <hostname>.bashrc.local # deployed as ~/.bashrc.local on that server
```
`deploy-to` automatically detects and deploys the matching file:
```bash
# Edit the server's local config on kenjim-mbp:
$EDITOR ~/dotfiles/hosts/etqc-kenjim-11.bashrc.local
# Commit and push from kenjim-mbp:
dotfiles push "fix: update etqc-kenjim-11 local config"
# Deploy to the server (no git access needed on the server):
dotfiles deploy-to kenjim@etqc-kenjim-11
```
The `hosts/` files are committed to git. They may contain non-secret
machine-specific variables (`MACHINE_PROFILE`, `MACHINE_HOST`, `AWS_PROFILE`,
etc.). **Do not commit real passwords or tokens** — use `CHANGEME` placeholders
and set real values manually on the server after first deploy.
--- ---
## Dotfiles Management — How Symlinks Work ## Dotfiles Management — How Symlinks Work
@@ -315,3 +356,83 @@ To add manually:
sudo tmutil addexclusion ~/data/raw sudo tmutil addexclusion ~/data/raw
sudo tmutil addexclusion ~/models sudo tmutil addexclusion ~/models
``` ```
**Ollama Bootstrap & Usage**
- **Bootstrap (macOS)**: Run the macOS installer/check + create model folders:
```bash
bash scripts/bootstrap_ollama_mac.sh
```
- **Bootstrap (Linux)**: Run the Linux helper (attempts to detect package manager and prepares model folders):
```bash
bash scripts/bootstrap_ollama_linux.sh
```
- The scripts create the model directory referenced by the setup script as `OLLAMA_MODELS` (by default `$HOME/models/ollama`).
- Start the Ollama server inside a tmux session (helper functions):
```bash
# Load helper functions (one-liner to source from dotfiles workspace)
source scripts/ollama_tmux.sh
# Start the server in a detached tmux session named 'ollama' (default command):
start_ollama_tmux
# To attach to the session:
tmux attach -t ollama
# To stop the server session:
stop_ollama_tmux
```
- Model management (two simple options):
- Pull a remote model (via the `ollama` CLI):
```bash
# example: pull a model by reference
pull_ollama_model <model-ref>
```
- Import or stage a local model into the $OLLAMA_MODELS path (creates a symlink):
```bash
import_local_model /path/to/local/model-dir-or-file
# The helper will link it into the directory created by setup_enterprise_ai_bash.sh
```
- Scripts referenced:
- [scripts/bootstrap_ollama_mac.sh](scripts/bootstrap_ollama_mac.sh)
- [scripts/bootstrap_ollama_linux.sh](scripts/bootstrap_ollama_linux.sh)
- [scripts/ollama_tmux.sh](scripts/ollama_tmux.sh)
- How to use Ollama from other AI applications:
- Run the server with `start_ollama_tmux` (above). By default the helper uses `ollama serve --host 127.0.0.1 --port 11434`.
- Many client apps can be pointed at the local Ollama HTTP API (default: `http://127.0.0.1:11434`). Check the Ollama docs for exact endpoints and payloads.
- Example using the `ollama` CLI to run a model directly from scripts or other programs:
```bash
# Run a model locally (interactive / CLI). Replace <model> with the model name.
ollama run <model> --prompt "Write a short haiku about trees"
```
- Example (generic) using HTTP from another application (replace endpoint/payload per Ollama docs):
```bash
curl -X POST "http://127.0.0.1:11434/api/generate" \
-H "Content-Type: application/json" \
-d '{"model":"<model>","prompt":"Hello from my app"}'
```
- When integrating from other AI tooling, supply the Ollama host/port (e.g. `OLLAMA_URL=http://127.0.0.1:11434`) or call the `ollama` CLI directly from the application process.
If you want, I can also:
- Make the new scripts executable (`chmod +x`) automatically in the repo
- Attempt to detect the Ollama default model storage path and auto-configure a symlink

View File

@@ -0,0 +1,41 @@
### MACHINE_LOCAL — managed centrally in dotfiles/hosts/ on kenjim-mbp
### Deployed via: dotfiles deploy-to kenjim@etqc-kenjim-11
### DO NOT edit directly on the server — edit in ~/dotfiles/hosts/ and re-deploy
### NFS NOTE: On NFS-home servers, this file lives at /opt/kenjim/.bashrc.local
### with a symlink from ~/.bashrc.local → /opt/kenjim/.bashrc.local
# Host : etqc-kenjim-11
# Profile : work (server)
# =============================================================================
export MACHINE_PROFILE="work"
export MACHINE_HOST="etqc-kenjim-11"
# Prevent shell auto-logout on idle
unset TMOUT
# =============================================================================
# ServiceNow API credentials
# =============================================================================
export SN_USERNAME='_integ-soap-read'
export SN_PASSWORD='CHANGEME' # set real value — never commit
# =============================================================================
# LDAP bind credentials
# Referenced by ldaps() and ldaps2() in ~/.bashrc.d/30_work.sh
# =============================================================================
export JNPR_LDAP_BIND_PW='CHANGEME' # was: xqYzhL%lLe!FIr!67LJX%7a^PWOWY0
export JNPR_LDAP_BIND_PW2='CHANGEME' # was: tF#w3St@nGqq36XZDym#857U)v4xKw
# =============================================================================
# Unified Hub (Artifactory) credentials
# Referenced by unified-hub-login() and unified-hub-engtech-bin-upload()
# in ~/.bashrc.d/30_work.sh
# =============================================================================
export UNIFIED_HUB_USERNAME='kenjim@juniper.net'
export UNIFIED_HUB_TOKEN='CHANGEME' # base64 API token from Artifactory
# =============================================================================
# AWS — server uses named profiles from ~/.aws/config loaded by k8configs env
# =============================================================================
export AWS_PROFILE=pgdb-qnc
export AWS_SDK_LOAD_CONFIG=1

View File

@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
echo "🔧 Ollama Linux bootstrap — starting"
# Load environment (MODEL_ROOT, OLLAMA_MODELS)
# Temporarily disable "nounset" when sourcing user shell files to avoid
# failures from system /etc/bashrc referencing undefined vars like PS1.
if [ -f "$HOME/.bashrc" ]; then
set +u
# shellcheck disable=SC1091
source "$HOME/.bashrc" || true
set -u
fi
: "${OLLAMA_MODELS:=$HOME/models/ollama}"
mkdir -p "$OLLAMA_MODELS"
if command -v ollama >/dev/null 2>&1; then
echo "✅ ollama already installed: $(ollama version 2>/dev/null || echo 'unknown')"
else
echo " Attempting to install ollama on Linux"
if command -v apt-get >/dev/null 2>&1; then
echo "Using apt to install prerequisites..."
sudo apt-get update -y || true
sudo apt-get install -y ca-certificates curl gnupg lsb-release || true
echo "⚠️ Please follow the official ollama Linux install instructions if a package is not available: https://ollama.com/docs/install"
echo "If you have a .deb from Ollama, install it with: sudo dpkg -i <file.deb>"
elif command -v dnf >/dev/null 2>&1; then
echo "Using dnf; please refer to Ollama docs for distro-specific instructions: https://ollama.com/docs/install"
else
echo "⚠️ Could not detect package manager. Please install ollama manually. See: https://ollama.com/docs/install"
fi
fi
echo "📁 Ensuring model directory exists: $OLLAMA_MODELS"
mkdir -p "$OLLAMA_MODELS"
echo "✅ Linux Ollama bootstrap complete."
echo "Next steps: run scripts/ollama_tmux.sh to start the server in tmux, and place models under $OLLAMA_MODELS"

41
scripts/bootstrap_ollama_mac.sh Executable file
View File

@@ -0,0 +1,41 @@
#!/usr/bin/env bash
set -euo pipefail
echo "🔧 Ollama macOS bootstrap — starting"
# Load environment (MODEL_ROOT, OLLAMA_MODELS)
# Temporarily disable "nounset" when sourcing user shell files to avoid
# failures from system /etc/bashrc referencing undefined vars like PS1.
if [ -f "$HOME/.bashrc" ]; then
set +u
# shellcheck disable=SC1091
source "$HOME/.bashrc" || true
set -u
fi
: "${OLLAMA_MODELS:=$HOME/models/ollama}"
mkdir -p "$OLLAMA_MODELS"
if command -v ollama >/dev/null 2>&1; then
echo "✅ ollama already installed: $(ollama version 2>/dev/null || echo 'unknown')"
else
if command -v brew >/dev/null 2>&1; then
echo "🍺 Installing ollama via Homebrew..."
brew update || true
brew install ollama || {
echo "⚠️ Homebrew install failed. See https://ollama.com/docs/install"
exit 1
}
echo "✅ ollama installed"
else
echo "⚠️ Homebrew not found. Install Homebrew first: https://brew.sh"
echo "Then re-run this script: $0"
exit 2
fi
fi
echo "📁 Ensuring model directory exists: $OLLAMA_MODELS"
mkdir -p "$OLLAMA_MODELS"
echo "✅ macOS Ollama bootstrap complete."
echo "Next steps: run scripts/ollama_tmux.sh to start the server in tmux, and place models under $OLLAMA_MODELS"

View File

@@ -883,12 +883,14 @@ cmd_remote_bootstrap() {
# COMMAND: deploy-to (push dotfiles to a server that can't reach Gitea) # COMMAND: deploy-to (push dotfiles to a server that can't reach Gitea)
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
cmd_deploy_to() { cmd_deploy_to() {
[ $# -ge 1 ] || die "Usage: deploy-to <user@host> [--scripts-only] [--include-ssh] [--no-backup] [--dry-run]" [ $# -ge 1 ] || die "Usage: deploy-to <user@host> [--scripts-only] [--include-ssh] [--no-backup] [--run-setup] [--profile work|personal] [--dry-run]"
local target="$1"; shift local target="$1"; shift
local scripts_only=false local scripts_only=false
local skip_ssh=true # default: skip .ssh/ — avoid pushing keys/config to servers local skip_ssh=true # default: skip .ssh/ — avoid pushing keys/config to servers
local no_backup=false local no_backup=false
local run_setup=false
local setup_profile="work" # default profile for servers
local dry_run=false local dry_run=false
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
@@ -896,6 +898,8 @@ cmd_deploy_to() {
--scripts-only) scripts_only=true; shift ;; --scripts-only) scripts_only=true; shift ;;
--include-ssh) skip_ssh=false; shift ;; --include-ssh) skip_ssh=false; shift ;;
--no-backup) no_backup=true; shift ;; --no-backup) no_backup=true; shift ;;
--run-setup) run_setup=true; shift ;;
--profile) setup_profile="${2:-work}"; shift 2 ;;
--dry-run) dry_run=true; shift ;; --dry-run) dry_run=true; shift ;;
*) die "Unknown option: $1" ;; *) die "Unknown option: $1" ;;
esac esac
@@ -944,11 +948,17 @@ cmd_deploy_to() {
local backed_up=0 local backed_up=0
for rel in "${remote_paths[@]}"; do for rel in "${remote_paths[@]}"; do
# Check if the file actually exists on the remote before fetching # Check if the file/dir actually exists on the remote before fetching
if ssh "$target" "[ -f ~/$rel ]" 2>/dev/null; then if ssh "$target" "[ -e ~/$rel ]" 2>/dev/null; then
local local_dest="$backup_base/$rel" local local_dest="$backup_base/$rel"
mkdir -p "$(dirname "$local_dest")" mkdir -p "$(dirname "$local_dest")"
if scp -q "$target:~/$rel" "$local_dest" 2>/dev/null; then # Use -r for directories, plain scp for files
if ssh "$target" "[ -d ~/$rel ]" 2>/dev/null; then
mkdir -p "$local_dest"
if scp -rq "$target:~/$rel/." "$local_dest/" 2>/dev/null; then
(( backed_up++ )) || true
fi
elif scp -q "$target:~/$rel" "$local_dest" 2>/dev/null; then
(( backed_up++ )) || true (( backed_up++ )) || true
fi fi
fi fi
@@ -1018,17 +1028,107 @@ cmd_deploy_to() {
fi fi
if $dry_run; then if $dry_run; then
echo " [dry-run] ~/$rel" echo " [dry-run] ~/$rel$([ -d "$src" ] && echo '/')"
else else
# Ensure parent directory exists on remote
local parent; parent="$(dirname "$rel")" local parent; parent="$(dirname "$rel")"
if [ -d "$src" ]; then
# For directories: remove stale dest first (avoids double-nesting on re-run)
# then scp -r into the parent so the dir name is preserved correctly.
ssh "$target" "rm -rf ~/$rel"
if [[ "$parent" == "." ]]; then
scp -rq "$src" "$target:~/"
else
ssh "$target" "mkdir -p ~/$parent"
scp -rq "$src" "$target:~/$parent/"
fi
else
[[ "$parent" != "." ]] && ssh "$target" "mkdir -p ~/$parent" [[ "$parent" != "." ]] && ssh "$target" "mkdir -p ~/$parent"
scp -q "$src" "$target:~/$rel" scp -q "$src" "$target:~/$rel"
fi
success "Deployed: ~/$rel" success "Deployed: ~/$rel"
(( deployed++ )) || true (( deployed++ )) || true
fi fi
done < "$MANIFEST" done < "$MANIFEST"
# ---- 5. Deploy hosts/<hostname>.bashrc.local → ~/.bashrc.local ----
# Strip user@ prefix, then strip domain suffix to get short hostname
local remote_short; remote_short="${target##*@}"
remote_short="${remote_short%%.*}"
local host_local="$DOTFILES_DIR/hosts/${remote_short}.bashrc.local"
if [ -f "$host_local" ]; then
echo
info "Found host-specific config: hosts/${remote_short}.bashrc.local"
if $dry_run; then
echo " [dry-run] hosts/${remote_short}.bashrc.local → ~/.bashrc.local"
else
# Back up existing remote .bashrc.local if not already captured above
if $no_backup; then
: # skip
elif ssh "$target" '[ -L ~/.bashrc.local ]' 2>/dev/null; then
# It's a symlink (NFS setup) — back up the target file
local real_path
real_path=$(ssh "$target" 'readlink -f ~/.bashrc.local' 2>/dev/null || true)
if [[ -n "$real_path" ]]; then
local bl_backup="$HOME/.dotfiles_backup/remote-${remote_short}-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$bl_backup"
scp -q "$target:$real_path" "$bl_backup/.bashrc.local" 2>/dev/null || true
info "Backed up remote $real_path$bl_backup/.bashrc.local"
fi
elif ssh "$target" '[ -f ~/.bashrc.local ]' 2>/dev/null; then
local bl_backup="$HOME/.dotfiles_backup/remote-${remote_short}-$(date +%Y%m%d_%H%M%S)"
mkdir -p "$bl_backup"
scp -q "$target:~/.bashrc.local" "$bl_backup/.bashrc.local" 2>/dev/null || true
info "Backed up remote ~/.bashrc.local → $bl_backup/.bashrc.local"
fi
# Detect NFS home on the remote and deploy to /opt/kenjim/ if needed
local _remote_nfs=false
if ssh "$target" 'df -P "$HOME" 2>/dev/null | tail -1 | grep -qE ":|nfs"' 2>/dev/null; then
_remote_nfs=true
fi
if $_remote_nfs; then
info "NFS home detected on $target — deploying to /opt/kenjim/.bashrc.local"
# Ensure /opt/kenjim exists — skip sudo if it already does
if ! ssh "$target" '[ -d /opt/kenjim ]' 2>/dev/null; then
info "Creating /opt/kenjim on $target (requires sudo)..."
ssh -t "$target" 'sudo mkdir -p /opt/kenjim && sudo chown $(id -u):$(id -g) /opt/kenjim && chmod 700 /opt/kenjim'
fi
scp -q "$host_local" "$target:/opt/kenjim/.bashrc.local"
ssh "$target" 'chmod 600 /opt/kenjim/.bashrc.local && ln -sfn /opt/kenjim/.bashrc.local ~/.bashrc.local'
success "Deployed: hosts/${remote_short}.bashrc.local → /opt/kenjim/.bashrc.local (symlinked from ~)"
else
scp -q "$host_local" "$target:~/.bashrc.local"
success "Deployed: hosts/${remote_short}.bashrc.local → ~/.bashrc.local"
fi
(( deployed++ )) || true
fi
else
info "No host-specific config found at hosts/${remote_short}.bashrc.local — skipping."
info "Create one to manage ~/.bashrc.local centrally: dotfiles/hosts/${remote_short}.bashrc.local"
fi
# ---- 6. Optionally run setup on the remote ----
if ! $dry_run; then
local do_setup=$run_setup
if ! $run_setup; then
echo
read -r -p "Run setup_enterprise_ai_bash.sh on $target now? Creates dirs, writes shell config. (y/n): " _ans
[[ "$_ans" == [yY] ]] && do_setup=true
fi
if $do_setup; then
echo
info "Running setup on $target (profile=$setup_profile)..."
info "This will create the directory structure and write shell config."
echo
ssh -t "$target" \
"MACHINE_PROFILE=${setup_profile} DOTFILES_REMOTE=${DOTFILES_REMOTE} bash ~/scripts/setup_enterprise_ai_bash.sh"
success "Setup complete on $target."
fi
fi
echo echo
if $dry_run; then if $dry_run; then
info "Dry run complete. Re-run without --dry-run to transfer files." info "Dry run complete. Re-run without --dry-run to transfer files."
@@ -1036,6 +1136,7 @@ cmd_deploy_to() {
bold "Deploy complete: $deployed file(s) deployed, $skipped skipped." bold "Deploy complete: $deployed file(s) deployed, $skipped skipped."
info "Files were copied directly (no symlinks). Re-run deploy-to to push updates." info "Files were copied directly (no symlinks). Re-run deploy-to to push updates."
$skip_ssh && info "~/.ssh/ was skipped. Use --include-ssh to also deploy ~/.ssh/config." $skip_ssh && info "~/.ssh/ was skipped. Use --include-ssh to also deploy ~/.ssh/config."
! $run_setup && info "Tip: add --run-setup to also create directories and shell config on the remote."
fi fi
} }
@@ -1067,13 +1168,16 @@ ${BOLD}COMMANDS — SSH & Keys${RESET}
${BOLD}COMMANDS — Multi-machine${RESET} ${BOLD}COMMANDS — Multi-machine${RESET}
remote-bootstrap <user@host> [--profile work|personal] remote-bootstrap <user@host> [--profile work|personal]
Upload scripts and run full setup on a remote machine Upload scripts and run full setup on a remote machine
deploy-to <user@host> [--scripts-only] [--include-ssh] [--no-backup] [--dry-run] deploy-to <user@host> [--scripts-only] [--include-ssh] [--no-backup] [--run-setup] [--profile work|personal] [--dry-run]
SCP tracked dotfiles + scripts directly to a server. SCP tracked dotfiles + scripts directly to a server.
Use when the server can't reach the Gitea repo. Use when the server can't reach the Gitea repo.
Backs up existing remote files locally before overwriting. Backs up existing remote files locally before overwriting.
--scripts-only Only push ~/scripts/, skip dotfiles --scripts-only Only push ~/scripts/, skip dotfiles
--include-ssh Also deploy ~/.ssh/config (skipped by default) --include-ssh Also deploy ~/.ssh/config (skipped by default)
--no-backup Skip the pre-deploy remote backup --no-backup Skip the pre-deploy remote backup
--run-setup Run setup_enterprise_ai_bash.sh on the remote
after deploy (creates dirs, shell config)
--profile work|personal Profile to pass to setup (default: work)
--dry-run Preview what would be transferred --dry-run Preview what would be transferred
${BOLD}QUICK START — this machine (work)${RESET} ${BOLD}QUICK START — this machine (work)${RESET}

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env bash
set -euo pipefail
echo "🔍 Detecting Ollama model directories and linking into OLLAMA_MODELS"
# Load env if available
if [ -f "$HOME/.bashrc" ]; then
set +u
# shellcheck disable=SC1091
source "$HOME/.bashrc" || true
set -u
fi
OLLAMA_MODELS="${OLLAMA_MODELS:-$HOME/models/ollama}"
mkdir -p "$OLLAMA_MODELS"
candidates=(
"$HOME/.ollama/models"
"$HOME/.local/share/ollama/models"
"/var/lib/ollama/models"
"/usr/local/var/ollama/models"
"$HOME/.ollama"
)
linked=0
for cand in "${candidates[@]}"; do
if [ -d "$cand" ]; then
# skip empty directories
if [ -z "$(ls -A "$cand" 2>/dev/null)" ]; then
echo " Found $cand but it's empty — skipping"
continue
fi
name=$(basename "$cand")
dest="$OLLAMA_MODELS/$name"
if [ -e "$dest" ]; then
echo " Destination exists: $dest — skipping"
continue
fi
ln -sfn "$cand" "$dest"
echo "✅ Linked $cand$dest"
linked=$((linked+1))
fi
done
if [ $linked -eq 0 ]; then
echo "⚠️ No existing Ollama model directories detected in common locations."
echo "You can place models in: $OLLAMA_MODELS or run: pull_ollama_model <model-ref> (see scripts/ollama_tmux.sh)"
else
echo "🎉 Completed linking ($linked). Verify with: ls -la $OLLAMA_MODELS"
fi
echo "Done."

144
scripts/ollama_tmux.sh Executable file
View File

@@ -0,0 +1,144 @@
#!/usr/bin/env bash
set -euo pipefail
# Ollama tmux helper and model utilities
# Source this file or call functions directly: source scripts/ollama_tmux.sh
# Ensure environment variables exist
if [ -f "$HOME/.bashrc" ]; then
set +u
# shellcheck disable=SC1091
source "$HOME/.bashrc" || true
set -u
fi
: "${OLLAMA_MODELS:=$HOME/models/ollama}"
start_ollama_tmux() {
session="ollama"
# Use provided command or sensible default. Ollama uses env vars (OLLAMA_HOST)
# Example: OLLAMA_HOST=0.0.0.0:11434 ollama serve
default_cmd="${OLLAMA_CMD:-ollama serve}"
cmd="${1:-$default_cmd}"
echo "Using command: $cmd"
if [ -n "${OLLAMA_HOST:-}" ]; then
echo "OLLAMA_HOST is set to: $OLLAMA_HOST"
fi
if ! command -v tmux >/dev/null 2>&1; then
echo "⚠️ tmux not installed. Install tmux and re-run."
return 2
fi
# Create models dir if needed
mkdir -p "$OLLAMA_MODELS"
if tmux has-session -t "$session" 2>/dev/null; then
echo " tmux session '$session' already running. Attach with: tmux attach -t $session"
return 0
fi
echo "🔁 Starting ollama in tmux session '$session' with command: $cmd"
tmux new-session -d -s "$session" "$cmd"
sleep 0.5
if tmux has-session -t "$session" 2>/dev/null; then
echo "✅ Started. Attach: tmux attach -t $session"
else
echo "❌ Failed to start tmux session. Check logs or run the command directly: $cmd"
return 1
fi
}
pull_ollama_model() {
if [ "$#" -lt 1 ]; then
echo "Usage: pull_ollama_model <model-ref>"
return 2
fi
if ! command -v ollama >/dev/null 2>&1; then
echo "⚠️ ollama CLI not found. Install first."
return 3
fi
model="$1"
echo "⬇️ Pulling model: $model"
ollama pull "$model"
}
import_local_model() {
if [ "$#" -lt 1 ]; then
echo "Usage: import_local_model <path-to-model-dir-or-file>"
return 2
fi
src="$1"
if [ ! -e "$src" ]; then
echo "⚠️ Source not found: $src"
return 3
fi
mkdir -p "$OLLAMA_MODELS"
dest="$OLLAMA_MODELS/$(basename "$src")"
ln -sfn "$src" "$dest"
echo "🔗 Linked $src$dest"
echo "Note: depending on your ollama installation you may need to reindex or use the ollama CLI to register the model."
}
stop_ollama_tmux() {
session="ollama"
if tmux has-session -t "$session" 2>/dev/null; then
tmux kill-session -t "$session"
echo "🛑 Stopped tmux session '$session'"
else
echo " No tmux session named '$session' running"
fi
}
print_usage() {
cat <<EOF
Usage: $(basename "$0") <command> [args]
Commands:
start [<cmd>] Start ollama in tmux (optional command overrides default)
stop Stop the tmux session named 'ollama'
pull <model-ref> Pull a model using the ollama CLI
import <path> Import/link a local model directory/file into OLLAMA_MODELS
help Show this help
If you want to use these as shell functions, source this file instead:
source scripts/ollama_tmux.sh
EOF
}
if [ "${BASH_SOURCE[0]}" == "${0}" ]; then
# Script executed directly; provide a simple CLI wrapper
cmd="${1:-}"
case "$cmd" in
start)
# shift and pass remaining args as the command
shift || true
start_ollama_tmux "$@"
exit $?
;;
stop)
stop_ollama_tmux
exit $?
;;
pull)
shift || true
pull_ollama_model "$@"
exit $?
;;
import)
shift || true
import_local_model "$@"
exit $?
;;
help|--help|-h|"")
print_usage
exit 0
;;
*)
echo "Unknown command: $cmd"
print_usage
exit 2
;;
esac
else
echo "Loaded ollama tmux helper. Functions: start_ollama_tmux, stop_ollama_tmux, pull_ollama_model, import_local_model"
fi

View File

@@ -186,6 +186,69 @@ fi
echo "✅ .bashrc.local written for profile: $MACHINE_PROFILE" 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) # 4⃣ INITIALIZE DOTFILES REPO (with Gitea remote)
# ------------------------------------------------------ # ------------------------------------------------------