Skip to content

Instantly share code, notes, and snippets.

@mikestankavich
Last active July 18, 2025 14:44
Show Gist options
  • Select an option

  • Save mikestankavich/137742bceb0215265103d947d25f9fa9 to your computer and use it in GitHub Desktop.

Select an option

Save mikestankavich/137742bceb0215265103d947d25f9fa9 to your computer and use it in GitHub Desktop.
Convenience script to bootstrap an ubuntu claude code sandbox
#!/usr/bin/env bash
set -euo pipefail
# --- Configurable defaults ---
USER="${1:-ubuntu}"
HOME_DIR=$(getent passwd "$USER" | cut -d: -f6 || true)
GO_VER="go1.24.5.linux-amd64"
NVM_VER="v0.40.3"
SAMBA_CONF="/etc/samba/smb.conf"
if ! id "$USER" >/dev/null 2>&1; then
echo "User $USER does not exist; creating..." >&2
useradd -m -s /bin/bash -G sudo "$USER"
HOME_DIR=$(getent passwd "$USER" | cut -d: -f6)
# Optionally set a locked password: passwd -l "$USER"
echo "$USER ALL=(ALL) NOPASSWD:ALL" >/etc/sudoers.d/"$USER"
chmod 440 /etc/sudoers.d/"$USER"
fi
if [[ -z "$HOME_DIR" ]] || [[ ! -d "$HOME_DIR" ]]; then
echo "Could not determine home directory for $USER" >&2
exit 1
fi
# --- Ensure packages are installed ---
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get upgrade -y
apt-get install -y \
avahi-daemon openssh-server curl tree wget samba git build-essential \
software-properties-common ca-certificates gnupg lsb-release nano direnv \
python3 python3-pip python3-venv
# --- Configure Samba home share only if needed ---
if ! grep -q "\[${USER}-home\]" "$SAMBA_CONF" 2>/dev/null; then
tee -a "$SAMBA_CONF" > /dev/null <<EOF
[${USER}-home]
path = $HOME_DIR
browseable = yes
writable = yes
valid users = $USER
create mask = 0755
directory mask = 0755
EOF
systemctl restart smbd || true
fi
# --- .bashrc patch: only if not already present ---
BASHRC="$HOME_DIR/.bashrc"
if ! grep -q "# direnv hook to auto set env vars" "$BASHRC" 2>/dev/null; then
sudo -u "$USER" tee -a "$BASHRC" > /dev/null <<'EOF'
# direnv hook to auto set env vars on change directory when the destination directory has a .envrc file
eval "$(direnv hook bash)"
# Add user-global NPM path
export PATH=$PATH:~/.npm-global/bin
# Add golang bin path
export PATH=$PATH:/usr/local/go/bin
# Add uv path
export PATH=$PATH:~/.cargo/bin
# First-login Samba password setup
if [ -x "$HOME/.first-login-smbpasswd.sh" ]; then
"$HOME/.first-login-smbpasswd.sh"
fi
EOF
chown "$USER":"$USER" "$BASHRC"
fi
# create samba user
RANDO_PWD=$(openssl rand -base64 20)
(echo "$RANDO_PWD"; echo "$RANDO_PWD") | smbpasswd -a -s $USER
# --- .first-login-smbpasswd.sh creation ---
FIRST_LOGIN_SH="$HOME_DIR/.first-login-smbpasswd.sh"
if [ ! -f "$FIRST_LOGIN_SH" ]; then
sudo -u "$USER" tee "$FIRST_LOGIN_SH" > /dev/null <<'EOS'
#!/usr/bin/env bash
FLAG="$HOME/.smbpasswd_set"
MAX_RETRIES=3
attempt=1
if [ ! -f "$FLAG" ]; then
echo
echo "Please choose a Samba password for your account (used for file sharing):"
while [ $attempt -le $MAX_RETRIES ]; do
echo
echo "------------------------------------"
echo "Attempt $attempt of $MAX_RETRIES"
if sudo smbpasswd "$USER"; then
touch "$FLAG"
echo "Samba password set. You won't be prompted again."
exit 0
else
echo
echo "There was a problem setting your Samba password."
if [ $attempt -lt $MAX_RETRIES ]; then
echo "Would you like to try again? [y/N]"
read -r answer
case "$answer" in
[Yy]* ) : ;;
* ) echo "Skipping Samba password setup. You will be prompted again next time."; exit 1;;
esac
fi
fi
attempt=$((attempt+1))
done
echo
echo "Maximum attempts reached. Samba password was not set."
echo "You will be prompted to set the password again next time."
fi
EOS
chmod 755 "$FIRST_LOGIN_SH"
chown "$USER":"$USER" "$FIRST_LOGIN_SH"
fi
# --- NPM global directory setup ---
sudo -u "$USER" mkdir -p "$HOME_DIR/.npm-global"
# --- GitHub CLI install (runs as root, standard script installs system-wide) ---
if ! command -v gh >/dev/null 2>&1; then
curl -fsSL https://gist.github.com/mikestankavich/4728909ba36b5142bd59d722210c6f43/raw/install-gh-cli.sh | bash
fi
# --- Node.js (NodeSource) ---
if ! command -v node >/dev/null 2>&1; then
curl -fsSL https://deb.nodesource.com/setup_lts.x | bash -
apt-get install -y nodejs
fi
# --- NVM (Node Version Manager), as user ---
if ! sudo -u "$USER" test -d "$HOME_DIR/.nvm"; then
sudo -u "$USER" bash -c "curl -o- 'https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_VER}/install.sh' | bash"
fi
# --- uv (Python), as user to get ~/.cargo/bin available ---
if ! sudo -u "$USER" command -v uv >/dev/null 2>&1; then
sudo -u "$USER" bash -c 'curl -LsSf https://astral.sh/uv/install.sh | sh'
fi
# --- Go install ---
if ! [ -x /usr/local/go/bin/go ] || [[ "$(/usr/local/go/bin/go version 2>/dev/null)" != *"${GO_VER%%.*}"* ]]; then
curl -fsSL "https://go.dev/dl/${GO_VER}.tar.gz" | tar -C /usr/local -xz
fi
# --- Set NPM user-global prefix and install pnpm/claude-code as user ---
sudo -u "$USER" bash -c "
npm config set prefix '$HOME_DIR/.npm-global'
if ! command -v pnpm >/dev/null 2>&1 || ! npm list -g | grep -q '@anthropic-ai/claude-code'; then
npm install -g pnpm @anthropic-ai/claude-code
fi
"
echo ""
echo "Bootstrap complete for user $USER."
echo "You will be prompted for a Samba password on first login."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment