Files
dotfiles/scripts/02-dev-tools-setup.sh
Blake Ridgway 0b1e7b7f51 feat: add NixOS support, Go, and Ruby on Rails; remove Rust
- Add nixos/ directory with declarative flake.nix and home.nix for NixOS
- Make all setup scripts NixOS-aware with proper detection and early exit
- Update bashrc, zshrc, and aliases with Go, Ruby on Rails, and Nix profile paths
- Replace Rust/Cargo references in shell configs with rbenv/Ruby on Rails
- Update README with NixOS quick start and full documentation
2026-07-30 20:13:13 -05:00

357 lines
13 KiB
Bash
Executable File

#!/bin/bash
# 02-dev-tools-setup.sh
# Installs various development tools and utilities.
# Supports NixOS (declarative via flake), Fedora, and Debian/Ubuntu.
# Relies on DISTRO and PACKAGE_MANAGER being set by the caller.
echo "--- Starting Development Tools Setup ---"
if [ -z "$DISTRO" ] || [ -z "$PACKAGE_MANAGER" ]; then
echo "ERROR: DISTRO and PACKAGE_MANAGER must be set in the environment."
exit 1
fi
# ── NixOS path ─────────────────────────────────────────────
if [ "$DISTRO" == "nixos" ]; then
echo "NixOS detected: development tools are managed declaratively"
echo "via nixos/flake.nix and nixos/home.nix."
echo ""
echo "The following packages are already configured in the flake:"
echo " - Go (go, gopls, delve)"
echo " - Ruby on Rails (ruby, bundler, rails, rbenv, nodejs)"
echo " - Python (python3, pip, virtualenv)"
echo " - Neovim (latest)"
echo " - Cloud/DevOps (kubectl, k9s, terraform, helm, awscli2)"
echo " - Other tools (oh-my-posh, fzf, bat, eza, ripgrep, fd)"
echo ""
echo "Run the main-setup.sh NixOS workflow or apply manually:"
echo ""
echo " sudo nixos-rebuild switch --flake ~/dotfiles/nixos#nixos"
echo " home-manager switch --flake ~/dotfiles/nixos#blake"
echo ""
echo "Skipping imperative dev tools setup."
echo "--- Development Tools Setup Finished ---"
exit 0
fi
# ═══════════════════════════════════════════════════════════
# TRADITIONAL DISTRO PATH (Fedora / Ubuntu / Debian / Pop)
# ═══════════════════════════════════════════════════════════
# Setup NVIM
echo "Setting up Neovim..."
_nvim_asset_name() {
case "$(uname -m)" in
x86_64) echo "nvim-linux-x86_64" ;;
aarch64) echo "nvim-linux-arm64" ;;
*) echo "Unsupported arch for Neovim: $(uname -m)" >&2; exit 1 ;;
esac
}
NVIM_LATEST="$(curl -fsSL https://api.github.com/repos/neovim/neovim/releases/latest | grep '"tag_name"' | cut -d'"' -f4)"
NVIM_INSTALLED="$(nvim --version 2>/dev/null | head -n1 | awk '{print $2}')"
if [ "$NVIM_INSTALLED" = "$NVIM_LATEST" ]; then
echo "Neovim ${NVIM_LATEST} already up-to-date."
else
NVIM_ASSET="$(_nvim_asset_name)"
echo "Installing Neovim ${NVIM_LATEST} (${NVIM_ASSET})..."
TEMP_NVIM_DIR=$(mktemp -d)
curl -fsSLo "${TEMP_NVIM_DIR}/${NVIM_ASSET}.tar.gz" \
"https://github.com/neovim/neovim/releases/latest/download/${NVIM_ASSET}.tar.gz"
sudo rm -rf /opt/nvim
sudo tar -C /opt -xzf "${TEMP_NVIM_DIR}/${NVIM_ASSET}.tar.gz"
sudo mv "/opt/${NVIM_ASSET}" /opt/nvim 2>/dev/null || true
rm -rf "${TEMP_NVIM_DIR}"
sudo ln -sf /opt/nvim/bin/nvim /usr/local/bin/nvim
echo "Neovim ${NVIM_LATEST} installed to /opt/nvim, symlinked to /usr/local/bin/nvim"
fi
# pynvim
echo "Installing pynvim for Neovim Python support..."
if /usr/bin/python3 -m pip show pynvim &>/dev/null; then
echo "pynvim already installed."
else
/usr/bin/python3 -m pip install --user pynvim
echo "pynvim installed for the current user."
fi
# Install Nerd Font (Hack)
echo "Installing Hack Nerd Font..."
NERDFONT_VERSION="v3.2.1" # Or use "latest" if API allows, else check manually
NERDFONT_NAME="Hack"
FONT_DIR="$HOME/.local/share/fonts"
mkdir -p "$FONT_DIR"
# Check if a Hack Nerd Font is already installed to avoid re-downloading
if fc-list | grep -qi "Hack Nerd Font"; then
echo "Hack Nerd Font already installed."
else
echo "Downloading and installing Hack Nerd Font..."
TEMP_FONT_DIR=$(mktemp -d)
wget -qO "${TEMP_FONT_DIR}/${NERDFONT_NAME}.zip" "https://github.com/ryanoasis/nerd-fonts/releases/download/${NERDFONT_VERSION}/${NERDFONT_NAME}.zip"
if [ $? -eq 0 ]; then
unzip -q "${TEMP_FONT_DIR}/${NERDFONT_NAME}.zip" -d "${TEMP_FONT_DIR}/${NERDFONT_NAME}NerdFont"
# Copy only .ttf or .otf files
find "${TEMP_FONT_DIR}/${NERDFONT_NAME}NerdFont" \( -name "*.ttf" -o -name "*.otf" \) -exec cp {} "$FONT_DIR/" \;
echo "Updating font cache..."
fc-cache -f -v
echo "Hack Nerd Font installed."
else
echo "ERROR: Failed to download Hack Nerd Font."
fi
rm -rf "${TEMP_FONT_DIR}" # Clean up
fi
OS="$(uname | tr '[:upper:]' '[:lower:]')"
ARCH_RAW="$(uname -m)"
case "${ARCH_RAW}" in
x86_64) ARCH=amd64 ;;
aarch64|arm64) ARCH=arm64 ;;
*) echo "Unsupported arch: ${ARCH_RAW}" >&2; exit 1 ;;
esac
_add_to_rc() {
local rcfile="$1" snippet="$2"
grep -qxF "${snippet}" "${rcfile}" 2>/dev/null || {
echo "# added by 02-dev-tools-setup.sh" >> "${rcfile}"
echo "${snippet}" >> "${rcfile}"
echo " → updated ${rcfile}"
}
}
if ! command -v go &>/dev/null; then
echo "➤ Fetching latest Go version..."
LATEST_GO_FULL="$(curl -fsSL https://go.dev/VERSION?m=text)"
# e.g. "go1.21.4"
LATEST_GO="${LATEST_GO_FULL#go}"
TAR="go${LATEST_GO}.${OS}-${ARCH}.tar.gz"
URL="https://go.dev/dl/${TAR}"
echo " latest is ${LATEST_GO_FULL}, downloading ${TAR}..."
TMPDIR="$(mktemp -d)"
curl -fsSL "${URL}" -o "${TMPDIR}/${TAR}"
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf "${TMPDIR}/${TAR}"
rm -rf "${TMPDIR}"
echo "Installed Go ${LATEST_GO_FULL} to /usr/local/go"
# ensure PATH in your RCs
SNIPPET='export PATH=$PATH:/usr/local/go/bin'
for rc in "${HOME}/.bashrc" "${HOME}/.zshrc"; do
[ -f "${rc}" ] && _add_to_rc "${rc}" "${SNIPPET}"
done
else
INSTALLED_FULL="$(go version | awk '{print $3}')" # e.g. "go1.21.4"
LATEST_GO_FULL="$(curl -fsSL https://go.dev/VERSION?m=text)"
if [ "${INSTALLED_FULL}" = "${LATEST_GO_FULL}" ]; then
echo "Go ${INSTALLED_FULL} already up-to-date"
else
echo "Go ${INSTALLED_FULL} installed but ${LATEST_GO_FULL} is available."
echo " rerun this script to upgrade or uninstall manually."
fi
fi
echo
if ! command -v oh-my-posh &>/dev/null; then
echo "➤ Installing Oh-My-Posh latest release..."
# GitHub /releases/latest/download will 302 redirect to actual asset
BIN_URL="https://github.com/JanDeDobbeleer/oh-my-posh/releases/latest/download/posh-${OS}-${ARCH}"
mkdir -p "${HOME}/.local/bin"
TMPBIN="$(mktemp)"
curl -fsSL -L "${BIN_URL}" -o "${TMPBIN}"
install -m755 "${TMPBIN}" "${HOME}/.local/bin/oh-my-posh"
rm -f "${TMPBIN}"
echo "Installed oh-my-posh to ~/.local/bin/oh-my-posh"
echo "Theme is managed via dotfiles repo at terminal/posh-theme.omp.json"
echo "Run 04-config-symlinks.sh to link it to ~/.config/oh-my-posh/theme.omp.json"
else
echo "✔ Oh-My-Posh already installed: $(oh-my-posh --version | head -n1)"
fi
# ── Ruby on Rails ──────────────────────────────────────────
echo ""
echo "--- Setting up Ruby on Rails ---"
if ! command -v ruby &>/dev/null; then
echo "➤ Installing Ruby via rbenv..."
# Install rbenv
if [ ! -d "$HOME/.rbenv" ]; then
git clone https://github.com/rbenv/rbenv.git "$HOME/.rbenv"
git clone https://github.com/rbenv/ruby-build.git "$HOME/.rbenv/plugins/ruby-build"
echo 'export PATH="$PATH:$HOME/.rbenv/bin"' >> "$HOME/.bashrc"
echo 'export PATH="$PATH:$HOME/.rbenv/bin"' >> "$HOME/.zshrc"
echo 'eval "$(rbenv init -)"' >> "$HOME/.bashrc"
echo 'eval "$(rbenv init -)"' >> "$HOME/.zshrc"
echo "rbenv installed. A new shell is needed or run:"
echo " export PATH=\"\$PATH:\$HOME/.rbenv/bin\" && eval \"\$(rbenv init -)\""
else
echo "rbenv already installed."
fi
# Install Ruby latest stable via rbenv
export PATH="$PATH:$HOME/.rbenv/bin"
eval "$(rbenv init - bash 2>/dev/null || true)"
if command -v rbenv &>/dev/null; then
RUBY_LATEST=$(rbenv install -l 2>/dev/null | grep -v - | tail -1)
if [ -n "$RUBY_LATEST" ]; then
echo "➤ Installing Ruby ${RUBY_LATEST} (this may take a while)..."
rbenv install "$RUBY_LATEST"
rbenv global "$RUBY_LATEST"
echo "Ruby ${RUBY_LATEST} installed and set as global version."
fi
fi
else
echo "✔ Ruby already installed: $(ruby --version 2>/dev/null | head -n1)"
fi
# Install Bundler
if command -v gem &>/dev/null; then
if ! command -v bundler &>/dev/null; then
echo "➤ Installing Bundler..."
gem install bundler
echo "Bundler installed."
else
echo "✔ Bundler already installed."
fi
# Install Rails
if ! command -v rails &>/dev/null; then
echo "➤ Installing Rails..."
gem install rails
rbenv rehash 2>/dev/null || true
echo "Rails installed."
else
echo "✔ Rails already installed: $(rails --version 2>/dev/null | head -n1)"
fi
fi
echo "--- Development Tools Setup Finished ---"
# ── Helm (Kubernetes package manager) ──────────────────────
echo ""
echo "--- Installing Helm ---"
if command -v helm &>/dev/null; then
echo "✔ Helm already installed: $(helm version --short 2>/dev/null | head -n1)"
else
echo "➤ Fetching latest Helm version..."
HELM_LATEST="$(curl -fsSL https://api.github.com/repos/helm/helm/releases/latest | grep '"tag_name"' | cut -d'"' -f4)"
# e.g. "v3.16.0"
echo " latest is ${HELM_LATEST}, downloading..."
TMPDIR="$(mktemp -d)"
curl -fsSL "https://get.helm.sh/helm-${HELM_LATEST}-linux-${ARCH}.tar.gz" -o "${TMPDIR}/helm.tar.gz"
tar -C "${TMPDIR}" -xzf "${TMPDIR}/helm.tar.gz"
sudo install -m755 "${TMPDIR}/linux-${ARCH}/helm" /usr/local/bin/helm
rm -rf "${TMPDIR}"
echo "Installed Helm ${HELM_LATEST} to /usr/local/bin/helm"
fi
# --- kubectx + kubens (fast k8s context/namespace switching) ---
echo ""
echo "--- Installing kubectx & kubens ---"
_install_kubectx_kubens() {
KUBECTX_VERSION="v0.9.5"
echo " version: ${KUBECTX_VERSION}"
TMPDIR="$(mktemp -d)"
curl -fsSL "https://raw.githubusercontent.com/ahmetb/kubectx/${KUBECTX_VERSION}/kubectx" \
-o "${TMPDIR}/kubectx"
curl -fsSL "https://raw.githubusercontent.com/ahmetb/kubectx/${KUBECTX_VERSION}/kubens" \
-o "${TMPDIR}/kubens"
sudo install -m755 "${TMPDIR}/kubectx" /usr/local/bin/kubectx
sudo install -m755 "${TMPDIR}/kubens" /usr/local/bin/kubens
rm -rf "${TMPDIR}"
echo "✔ kubectx & kubens installed to /usr/local/bin/"
}
if command -v kubectx &>/dev/null && command -v kubens &>/dev/null; then
echo "✔ kubectx & kubens already installed"
else
_install_kubectx_kubens
fi
# --- stern (multi-pod/container log tailing for k8s) ---
echo ""
echo "--- Installing stern ---"
_stern_version() {
curl -fsSL "https://api.github.com/repos/stern/stern/releases/latest" \
| grep '"tag_name"' | cut -d'"' -f4
}
if command -v stern &>/dev/null; then
echo "✔ stern already installed"
else
STERN_VER="$(_stern_version)"
echo " latest is ${STERN_VER}, downloading..."
TMPDIR="$(mktemp -d)"
STERN_URL="https://github.com/stern/stern/releases/download/${STERN_VER}/stern_${STERN_VER#v}_linux_${ARCH}.tar.gz"
curl -fsSL "${STERN_URL}" -o "${TMPDIR}/stern.tar.gz"
tar -C "${TMPDIR}" -xzf "${TMPDIR}/stern.tar.gz" stern
sudo install -m755 "${TMPDIR}/stern" /usr/local/bin/stern
rm -rf "${TMPDIR}"
echo "✔ stern ${STERN_VER} installed to /usr/local/bin/stern"
fi
# --- dive (Docker image layer explorer) ---
echo ""
echo "--- Installing dive ---"
_dive_version() {
curl -fsSL "https://api.github.com/repos/wagoodman/dive/releases/latest" \
| grep '"tag_name"' | cut -d'"' -f4
}
if command -v dive &>/dev/null; then
echo "✔ dive already installed"
else
DIVE_VER="$(_dive_version)"
echo " latest is ${DIVE_VER}, downloading..."
DIVE_URL="https://github.com/wagoodman/dive/releases/download/${DIVE_VER}/dive_${DIVE_VER#v}_linux_${ARCH}.deb"
TMPDIR="$(mktemp -d)"
curl -fsSL "${DIVE_URL}" -o "${TMPDIR}/dive.deb"
if [ "${PACKAGE_MANAGER}" = "apt" ]; then
sudo dpkg -i "${TMPDIR}/dive.deb" 2>/dev/null || sudo apt-get install -f -y
else
# For rpm-based distros, extract binary from deb
ar x "${TMPDIR}/dive.deb" data.tar.gz 2>/dev/null \
&& tar -C "${TMPDIR}" -xzf data.tar.gz ./usr/local/bin/dive \
&& sudo install -m755 "${TMPDIR}/usr/local/bin/dive" /usr/local/bin/dive \
|| echo "⚠ Could not install dive from .deb on non-apt system. Try manual install."
fi
rm -rf "${TMPDIR}"
echo "✔ dive ${DIVE_VER} installed"
fi
# --- trivy (container/filesystem vulnerability scanner) ---
echo ""
echo "--- Installing trivy ---"
if command -v trivy &>/dev/null; then
echo "✔ trivy already installed: $(trivy --version 2>/dev/null | head -n1)"
else
echo "➤ Downloading trivy via official install script..."
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh \
| sudo sh -s -- -b /usr/local/bin
echo "✔ trivy installed to /usr/local/bin/trivy"
fi
# --- AWS CLI v2 ---
echo ""
echo "--- Installing AWS CLI v2 ---"
_awscli_install() {
TMPDIR="$(mktemp -d)"
curl -fsSL "https://awscli.amazonaws.com/awscli-exe-linux-${ARCH_RAW}.zip" \
-o "${TMPDIR}/awscliv2.zip"
unzip -q "${TMPDIR}/awscliv2.zip" -d "${TMPDIR}"
sudo "${TMPDIR}/aws/install" --update
rm -rf "${TMPDIR}"
echo "✔ AWS CLI v2 installed"
}
if command -v aws &>/dev/null; then
echo "✔ AWS CLI already installed: $(aws --version 2>&1)"
else
_awscli_install
fi