|
# bootstrap_devbox.sh (Ubuntu 24.04 LTS) |
|
# Run once as root: sudo bash /tmp/bootstrap_devbox.sh |
|
set -euo pipefail |
|
|
|
# ===== Config (edit as needed) ===== |
|
TZ="Europe/Berlin" |
|
# Auto-detect first non-root login; override by exporting TARGET_USER before running |
|
TARGET_USER="${TARGET_USER:-$(logname 2>/dev/null || getent passwd 1000 | cut -d: -f1)}" |
|
TARGET_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)" |
|
DOCKER_REMOTE_HOST="${DOCKER_REMOTE_HOST:-docker-host.local}" # e.g., docker-host.lan |
|
|
|
echo ">> Using TARGET_USER=$TARGET_USER (home=$TARGET_HOME)" |
|
|
|
# ===== Timezone & base packages ===== |
|
timedatectl set-timezone "$TZ" || true |
|
export DEBIAN_FRONTEND=noninteractive |
|
apt-get update |
|
apt-get install -y \ |
|
ca-certificates gnupg \ |
|
curl wget unzip rsync file procps \ |
|
zsh git build-essential pkg-config \ |
|
openssh-client openssh-server \ |
|
sshfs qemu-guest-agent software-properties-common |
|
|
|
systemctl enable --now qemu-guest-agent || true |
|
|
|
# ===== SSH agent-forwarding (server-side) ===== |
|
if grep -qE '^\s*AllowAgentForwarding' /etc/ssh/sshd_config; then |
|
sed -i 's|^\s*AllowAgentForwarding.*|AllowAgentForwarding yes|' /etc/ssh/sshd_config |
|
else |
|
echo 'AllowAgentForwarding yes' >> /etc/ssh/sshd_config |
|
fi |
|
systemctl reload ssh || true |
|
|
|
# ===== Docker CE (official repo) ===== |
|
install -m 0755 -d /etc/apt/keyrings |
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg |
|
chmod a+r /etc/apt/keyrings/docker.gpg |
|
. /etc/os-release |
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu ${VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list |
|
apt-get update |
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin |
|
usermod -aG docker "$TARGET_USER" || true |
|
|
|
# ===== Node.js 22 (NodeSource) ===== |
|
curl -fsSL https://deb.nodesource.com/setup_22.x | bash - |
|
apt-get install -y nodejs |
|
|
|
# ===== Homebrew (Linuxbrew) under non-root user ===== |
|
# Pre-create prefix and permissions |
|
mkdir -p /home/linuxbrew/.linuxbrew |
|
chown -R "$TARGET_USER":"$TARGET_USER" /home/linuxbrew |
|
chmod 0755 /home/linuxbrew |
|
|
|
# Install Brew as the normal user (no TTY needed) |
|
runuser -l "$TARGET_USER" -c 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"' || true |
|
|
|
# Make Brew available system-wide and for non-login shells |
|
if [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then |
|
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" |
|
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' > /etc/profile.d/brew.sh |
|
# Nice dev tools + Antigen (installed as user so paths resolve in zshrc) |
|
runuser -l "$TARGET_USER" -c 'bash -lc "/home/linuxbrew/.linuxbrew/bin/brew update || true; /home/linuxbrew/.linuxbrew/bin/brew install antigen fzf ripgrep bat fd gh mise eza || true"' |
|
fi |
|
|
|
# ===== oh-my-zsh + Antigen + Powerlevel10k theme ===== |
|
runuser -l "$TARGET_USER" -c 'export RUNZSH=no; [ -d ~/.oh-my-zsh ] || sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"' |
|
cat > "$TARGET_HOME/.zshrc" <<'ZRC' |
|
zmodload zsh/zprof |
|
export ZSH_DISABLE_COMPFIX=true |
|
|
|
# Homebrew env |
|
if [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then |
|
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" |
|
fi |
|
|
|
# Antigen + OMZ + git plugins + Powerlevel10k |
|
if command -v brew >/dev/null 2>&1 && [ -f "$(brew --prefix)/share/antigen/antigen.zsh" ]; then |
|
source "$(brew --prefix)/share/antigen/antigen.zsh" |
|
antigen use oh-my-zsh |
|
antigen bundle git |
|
antigen bundle command-not-found |
|
antigen bundle zsh-users/zsh-autosuggestions |
|
antigen bundle zsh-users/zsh-syntax-highlighting |
|
antigen bundle lukechilds/zsh-nvm |
|
antigen theme romkatv/powerlevel10k |
|
antigen apply |
|
fi |
|
|
|
export ZSH="$HOME/.oh-my-zsh" |
|
ZSH_THEME="powerlevel10k/powerlevel10k" # fallback if theme already present |
|
ENABLE_CORRECTION="true" |
|
COMPLETION_WAITING_DOTS="true" |
|
plugins=(git git-prompt) |
|
|
|
export LANG=en_US.UTF-8 |
|
path+=($HOME/.local/bin /usr/local/bin) |
|
|
|
# Optional pyenv path if you add it later |
|
[ -d "$HOME/.pyenv" ] && export PATH="$HOME/.pyenv/shims:$PATH" && eval "$(pyenv init -)" || true |
|
|
|
autoload -Uz compinit |
|
zstyle ':completion:*' menu select |
|
fpath+=($HOME/.zfunc) |
|
ZRC |
|
chown "$TARGET_USER":"$TARGET_USER" "$TARGET_HOME/.zshrc" |
|
chsh -s /bin/zsh "$TARGET_USER" || true |
|
|
|
# ===== Agentic CLIs (global) ===== |
|
npm i -g @anthropic-ai/claude-code @musistudio/claude-code-router @google/gemini-cli @qwen-code/qwen-code@latest || true |
|
|
|
# ===== Auto-update script (runs npm as root, brew as user) ===== |
|
cat >/usr/local/bin/update-agentic-tools.sh <<'UPD' |
|
#!/usr/bin/env bash |
|
set -euo pipefail |
|
TARGET_USER="${TARGET_USER:-devuser}" # override via env in unit if desired |
|
BREW_BIN="/home/linuxbrew/.linuxbrew/bin/brew" |
|
as_user() { sudo -u "$TARGET_USER" -H bash -lc "$*"; } |
|
|
|
update_npm_root() { |
|
if command -v npm >/dev/null 2>&1; then |
|
npm i -g @anthropic-ai/claude-code @musistudio/claude-code-router @google/gemini-cli @qwen-code/qwen-code@latest || true |
|
fi |
|
} |
|
update_brew_as_user() { |
|
if [ -x "$BREW_BIN" ]; then |
|
as_user 'eval "$('"$BREW_BIN"' shellenv)"; brew update || true; brew upgrade --formula || true' |
|
fi |
|
} |
|
|
|
if [ "$(id -u)" -eq 0 ]; then |
|
update_npm_root |
|
update_brew_as_user |
|
else |
|
# If run manually as user |
|
if command -v npm >/dev/null 2>&1; then |
|
sudo npm i -g @anthropic-ai/claude-code @musistudio/claude-code-router @google/gemini-cli @qwen-code/qwen-code@latest || true |
|
fi |
|
if [ -x "$BREW_BIN" ]; then |
|
eval "$($BREW_BIN shellenv)" |
|
brew update || true |
|
brew upgrade --formula || true |
|
fi |
|
fi |
|
UPD |
|
chmod 0755 /usr/local/bin/update-agentic-tools.sh |
|
|
|
cat >/etc/systemd/system/update-agentic-tools.service <<SVC |
|
[Unit] |
|
Description=Update agentic CLI tools at boot |
|
After=network-online.target |
|
Wants=network-online.target |
|
|
|
[Service] |
|
Type=oneshot |
|
User=$TARGET_USER |
|
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/linuxbrew/.linuxbrew/bin |
|
Environment=TARGET_USER=$TARGET_USER |
|
ExecStart=/usr/local/bin/update-agentic-tools.sh |
|
|
|
[Install] |
|
WantedBy=multi-user.target |
|
SVC |
|
systemctl daemon-reload |
|
systemctl enable update-agentic-tools.service |
|
|
|
# ===== Docker remote context over SSH (uses your laptop's agent) ===== |
|
runuser -l "$TARGET_USER" -c "docker context create myhost --docker 'host=ssh://$TARGET_USER@$DOCKER_REMOTE_HOST' || true" |
|
|
|
echo "✅ Bootstrap complete. Reboot recommended so '$TARGET_USER' gains docker group permissions." |