Skip to content

Instantly share code, notes, and snippets.

@ursisterbtw
Last active June 7, 2025 10:34
Show Gist options
  • Select an option

  • Save ursisterbtw/2d2a8e390b1d96170eb3d0526492c7f5 to your computer and use it in GitHub Desktop.

Select an option

Save ursisterbtw/2d2a8e390b1d96170eb3d0526492c7f5 to your computer and use it in GitHub Desktop.
ubuntu-vm-setup.sh
#!/bin/bash
#==============================================================================
# Ubuntu Development Environment Setup Script
# Description: Comprehensive installation script for development tools,
# applications, and utilities on fresh Ubuntu VMs
# Author: Generated for dotfiles repository
# Usage: chmod +x ubuntu-dev-setup.sh && ./ubuntu-dev-setup.sh [--auto-approve]
#==============================================================================
# Set up logging
LOG_FILE="${HOME}/.ubuntu-dev-setup-$(date +%Y%m%d-%H%M%S).log"
exec > >(tee -a "${LOG_FILE}") 2>&1
# Log script start
echo "=== Ubuntu Development Environment Setup Log - $(date) ==="
echo "Log file: ${LOG_FILE}"
echo "Command line: $0 $*"
echo "User: $(whoami)@$(hostname)"
echo "System: $(uname -a)"
echo "========================================================"
echo
# Function to log to both console and log file
log() {
local level="$1"
local message="$2"
local timestamp
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[${timestamp}] [${level}] ${message}"
}
set -euo pipefail
# Prevent interactive prompts during package installation
export DEBIAN_FRONTEND=noninteractive
export DEBCONF_NONINTERACTIVE_SEEN=true
# Color codes for output formatting
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly BLUE='\033[0;34m'
readonly PURPLE='\033[0;35m'
readonly CYAN='\033[0;36m'
readonly NC='\033[0m' # No Color
# Logging functions
log_info() {
log "INFO" "$1"
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
log "SUCCESS" "$1"
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
log "WARNING" "$1"
echo -e "${YELLOW}[WARNING]${NC} $1" >&2
}
log_error() {
log "ERROR" "$1"
echo -e "${RED}[ERROR]${NC} $1" >&2
}
# Function to run commands with error handling
run_command() {
log_info "Running: $*"
if "$@"; then
log_info "Command succeeded: $*"
return 0
else
local status=$?
log_error "Command failed with status ${status}: $*"
return $status
fi
}
log_section() {
echo -e "\n${PURPLE}=================================${NC}"
echo -e "${PURPLE}$1${NC}"
echo -e "${PURPLE}=================================${NC}\n"
}
# Error handling
error_exit() {
log_error "$1"
log_error "Installation failed. Check the log file for details: ${LOG_FILE}"
exit 1
}
# Helper functions
# Check if a command exists
command_exists() {
command -v "$1" >/dev/null 2>&1
}
# Install cargo package if not already installed
cargo_install() {
for pkg in "$@"; do
if ! command_exists "$pkg"; then
log_info "Installing $pkg via cargo..."
cargo install "$pkg" || log_warning "Failed to install $pkg"
else
log_info "$pkg is already installed"
fi
done
}
codename_fallback() {
local c
c=$(lsb_release -cs)
[[ $c == questing ]] && c=noble
echo "$c"
}
safe_apt_install() {
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "$@" \
|| error_exit "apt failed on: $*"
}
add_external_repo() {
local repo_name=$1
local repo_url=$2
local key_url=$3
local keyring_path="/etc/apt/keyrings/${repo_name}.gpg"
local list_path="/etc/apt/sources.list.d/${repo_name}.list"
local codename
codename=$(codename_fallback)
log_info "Adding $repo_name repository..."
# Download and add repository key
sudo mkdir -p /etc/apt/keyrings
curl -fsSL "$key_url" | sudo gpg --dearmor -o "$keyring_path"
# Special case for Helm repository which doesn't have a noble release yet
if [[ "$repo_url" == *"baltocdn.com/helm"* && "$codename" == "noble" ]]; then
log_info "Using 'jammy' codename for Helm repository (noble not available yet)"
codename="jammy"
fi
# Add repository
echo "deb [arch=$(dpkg --print-architecture) signed-by=${keyring_path}] ${repo_url} ${codename} stable" | \
sudo tee "$list_path" > /dev/null
sudo apt update
}
# Check if running as root (we don't want that)
if [[ $EUID -eq 0 ]]; then
error_exit "This script should not be run as root. Please run as a regular user."
fi
# Check Ubuntu version and system information
check_ubuntu_version() {
log_section "SYSTEM INFORMATION"
if ! command -v lsb_release &> /dev/null; then
log_warning "lsb_release not found. Installing..."
sudo apt update && sudo apt install -y lsb-release || {
log_error "Failed to install lsb-release"
return 1
}
fi
local version codename
version=$(lsb_release -rs)
codename=$(lsb_release -cs)
# Apply codename fallback if needed
if [[ $codename == "questing" ]]; then
log_warning "Detected 'questing' codename, falling back to 'noble'"
codename="noble"
fi
log_info "Ubuntu Version: $version ($(lsb_release -cs) -> $codename)"
log_info "Architecture: $(uname -m)"
log_info "Kernel: $(uname -r)"
# Ensure we're on a supported version (18.04+)
if (( $(echo "$version >= 18.04" | bc -l 2>/dev/null || echo 0) )); then
log_success "Ubuntu version is supported"
else
log_warning "Ubuntu version may not be fully supported. Proceeding anyway..."
fi
# Export codename for use in other functions
export UBUNTU_CODENAME=$codename
}
# Update system packages
update_system() {
log_section "SYSTEM UPDATE"
log_info "Updating package lists..."
sudo apt update
log_info "Upgrading existing packages..."
sudo apt upgrade -y
log_info "Installing essential build tools and dependencies..."
# Install packages in groups to avoid argument conflicts
safe_apt_install \
curl wget git vim nano htop btop tree unzip zip tar gzip \
build-essential software-properties-common apt-transport-https \
ca-certificates gnupg lsb-release bc jq file xclip xsel
# Install ripgrep and fd-find separately as they might have different sources
safe_apt_install ripgrep fd-find bat
# Install zoxide and fzf separately to prevent argument conflicts
safe_apt_install zoxide
safe_apt_install fzf
# Install remaining packages
sudo apt install -y \
tmux screen neofetch net-tools dnsutils traceroute \
nmap tcpdump openssh-server \
openssh-client rsync rclone ffmpeg imagemagick graphicsmagick \
pandoc texlive-latex-base texlive-latex-recommended sqlite3 \
postgresql-client mysql-client redis-tools
# Install packages that need special handling or have interactive prompts
safe_apt_install iperf3
# Pre-seed wireshark-common to avoid interactive prompt
echo "wireshark-common wireshark-common/install-setuid boolean false" | sudo debconf-set-selections
safe_apt_install wireshark-common
log_success "System packages updated and essential tools installed"
}
# Install development languages and runtimes
install_programming_languages() {
log_section "PROGRAMMING LANGUAGES & RUNTIMES"
# Homebrew (Linux package manager)
log_info "Installing Homebrew..."
export NONINTERACTIVE=1
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || {
log_warning "Homebrew installation had issues but continuing..."
}
# Add Homebrew to PATH (appending instead of prepending for safety)
if [ -f "/home/linuxbrew/.linuxbrew/bin/brew" ]; then
# Only add to PATH if not already present
if ! echo "$PATH" | grep -q "/home/linuxbrew/.linuxbrew/bin"; then
echo 'export PATH="$PATH:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin"' >> ~/.bashrc
export PATH="$PATH:/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin"
fi
# Initialize Homebrew without modifying PATH again
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv 2>/dev/null || true)"
fi
# Python development
log_info "Installing Python development environment..."
sudo apt install -y \
python3 \
python3-pip \
python3-dev \
python3-venv \
python3-wheel \
python3-setuptools \
pipx \
black \
flake8 \
mypy \
pylint \
python3-pytest
# Install uv (modern Python package manager)
log_info "Installing uv (Python package manager)..."
curl -LsSf https://astral.sh/uv/install.sh | sh
# Ollama (AI language model runtime)
log_info "Installing Ollama..."
if ! command -v ollama &>/dev/null; then
curl -fsSL https://ollama.com/install.sh | sh || {
log_warning "Ollama installation had issues but continuing..."
}
else
log_info "Ollama is already installed"
fi
# Function to pull Ollama models if Ollama is installed
pull_ollama_models() {
if command -v ollama &>/dev/null; then
log_info "Pulling Ollama models..."
local models=(
"qwen3:latest"
"gemma3:latest"
"nomic-embed-text:latest"
)
for model in "${models[@]}"; do
log_info "Pulling model: $model"
if ! ollama pull "$model"; then
log_warning "Failed to pull Ollama model: $model"
fi
done
else
log_warning "Ollama not found, skipping model downloads"
fi
}
# Pull models if Ollama installation was successful
if command -v ollama &>/dev/null; then
if auto_approve "Download Ollama models (qwen3, gemma3, nomic-embed-text)?"; then
pull_ollama_models
fi
fi
# NVM (Node Version Manager)
log_info "Setting up NVM (Node Version Manager)..."
# Function to safely source NVM
source_nvm() {
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
}
# Install NVM if not present
if [ ! -d "$HOME/.nvm" ]; then
log_info "Installing NVM..."
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source_nvm
else
log_info "NVM is already installed"
source_nvm
# Only attempt update if git is available
if command -v git &> /dev/null; then
log_info "Updating NVM..."
(
cd "$NVM_DIR"
git fetch --quiet --tags origin
CURRENT_TAG=$(git describe --abbrev=0 --tags)
LATEST_TAG=$(git describe --abbrev=0 --tags $(git rev-list --tags --max-count=1))
if [ "$CURRENT_TAG" != "$LATEST_TAG" ]; then
log_info "Updating NVM from $CURRENT_TAG to $LATEST_TAG"
git checkout --quiet "$LATEST_TAG"
source_nvm
else
log_info "NVM is already at the latest version ($CURRENT_TAG)"
fi
)
fi
fi
# Verify NVM is available
if ! command -v nvm &> /dev/null; then
log_error "Failed to initialize NVM. Please install it manually."
return 1
fi
# Node.js via NVM
log_info "Setting up Node.js via NVM..."
# Install LTS version if not already installed
if ! nvm ls lts/* &>/dev/null; then
log_info "Installing latest Node.js LTS..."
nvm install --lts --latest-npm --no-progress
else
log_info "Node.js LTS is already installed"
fi
# Ensure we're using the LTS version
nvm use --lts --delete-prefix
# Set default to LTS
nvm alias default lts/*
# Enable corepack for package managers
if command -v corepack &> /dev/null; then
log_info "Enabling corepack..."
corepack enable
fi
# Global Node.js tools
log_info "Installing global Node.js tools..."
npm install -g \
yarn \
pnpm \
typescript \
ts-node \
eslint \
prettier \
nodemon \
pm2 \
http-server \
live-server \
create-react-app \
vue-cli \
@angular/cli \
next \
nuxt \
gatsby-cli \
vercel \
netlify-cli
# Rust
log_info "Installing Rust toolchain..."
if ! command -v rustc &>/dev/null; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y || {
log_warning "Rust installation had issues but continuing..."
}
# Source cargo env if it exists
[ -f "$HOME/.cargo/env" ] && source "$HOME/.cargo/env"
else
log_info "Rust is already installed"
fi
# Rust tools - only install what's not available via apt
log_info "Installing Rust development tools..."
cargo_install \
cargo-edit \
cargo-watch \
cargo-expand \
cargo-audit \
cargo-outdated \
cargo-tree \
serde_json \
tokio-console \
diesel_cli \
sqlx-cli \
trunk \
wasm-pack \
bottom \
zoxide \
starship
# Install tools that might be available via apt, but only if not present
command_exists bat || cargo_install bat
command_exists eza || cargo_install eza
command_exists fd || cargo_install fd-find
command_exists rg || cargo_install ripgrep
command_exists tokei || cargo_install tokei
command_exists dust || cargo_install du-dust
command_exists delta || cargo_install git-delta
command_exists procs || safe_apt_install procs # Prefer apt version as it's pre-built
# Go
log_info "Installing Go..."
if ! command -v go &>/dev/null; then
local go_version
go_version=$(curl -s https://go.dev/VERSION?m=text | head -n1)
wget "https://golang.org/dl/${go_version}.linux-amd64.tar.gz"
sudo tar -C /usr/local -xzf "${go_version}.linux-amd64.tar.gz"
rm "${go_version}.linux-amd64.tar.gz"
else
log_info "Go is already installed"
fi
# Java
log_info "Installing Java (OpenJDK)..."
sudo apt install -y \
openjdk-17-jdk \
openjdk-11-jdk \
openjdk-8-jdk \
maven \
gradle
# Ruby
log_info "Installing Ruby..."
sudo apt install -y \
ruby \
ruby-dev \
rubygems \
bundler
# PHP
log_info "Installing PHP..."
sudo apt install -y \
php \
php-cli \
php-common \
php-curl \
php-gd \
php-json \
php-mbstring \
php-mysql \
php-xml \
php-zip \
composer
# C/C++
log_info "Installing C/C++ development tools..."
sudo apt install -y \
gcc \
g++ \
clang \
clang-format \
clang-tidy \
gdb \
valgrind \
cmake \
make \
ninja-build \
ccache \
pkg-config
log_success "Programming languages and runtimes installed"
}
# Install containerization tools
install_containers() {
log_section "CONTAINERIZATION & ORCHESTRATION"
# Docker
log_info "Installing Docker..."
add_external_repo \
docker \
"https://download.docker.com/linux/ubuntu" \
"https://download.docker.com/linux/ubuntu/gpg"
safe_apt_install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Add user to docker group
sudo usermod -aG docker "$USER"
# Docker Compose (standalone)
log_info "Installing Docker Compose standalone..."
local compose_version
compose_version=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | jq -r .tag_name)
sudo curl -L "https://github.com/docker/compose/releases/download/${compose_version}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# Kubernetes tools
log_info "Installing Kubernetes tools..."
# kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl
# Helm
log_info "Installing Helm..."
# Add Helm repository using the new URL format
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt update
safe_apt_install helm
# Minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube
rm minikube-linux-amd64
# k9s (Kubernetes CLI)
local k9s_version
k9s_version=$(curl -s https://api.github.com/repos/derailed/k9s/releases/latest | jq -r .tag_name)
wget "https://github.com/derailed/k9s/releases/download/${k9s_version}/k9s_Linux_amd64.tar.gz"
tar -xzf k9s_Linux_amd64.tar.gz
sudo mv k9s /usr/local/bin/
rm k9s_Linux_amd64.tar.gz LICENSE README.md
log_success "Containerization tools installed"
}
# Install cloud CLI tools
install_cloud_tools() {
log_section "CLOUD PLATFORM TOOLS"
# AWS CLI
log_info "Installing AWS CLI v2..."
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
rm -rf aws/ awscliv2.zip
# Google Cloud SDK
log_info "Installing Google Cloud SDK..."
add_external_repo \
google-cloud-sdk \
"https://packages.cloud.google.com/apt" \
"https://packages.cloud.google.com/apt/doc/apt-key.gpg"
safe_apt_install google-cloud-cli google-cloud-cli-gke-gcloud-auth-plugin
# Azure CLI
log_info "Installing Azure CLI..."
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Terraform
log_info "Installing Terraform..."
add_external_repo \
hashicorp \
"https://apt.releases.hashicorp.com" \
"https://apt.releases.hashicorp.com/gpg"
safe_apt_install terraform
# Ansible
log_info "Installing Ansible..."
sudo apt install -y ansible ansible-lint
# Pulumi
log_info "Installing Pulumi..."
curl -fsSL https://get.pulumi.com | sh
log_success "Cloud platform tools installed"
}
# Install databases
install_databases() {
log_section "DATABASE SYSTEMS"
# PostgreSQL
log_info "Installing PostgreSQL..."
sudo apt install -y postgresql postgresql-contrib postgresql-client-common
# MySQL
log_info "Installing MySQL..."
sudo apt install -y mysql-server mysql-client
# MongoDB
log_info "Installing MongoDB..."
curl -fsSL https://pgp.mongodb.com/server-7.0.asc | sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor
echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/ubuntu $(lsb_release -cs)/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt update
sudo apt install -y mongodb-org
# Redis
log_info "Installing Redis..."
sudo apt install -y redis-server redis-tools
# InfluxDB
log_info "Installing InfluxDB..."
curl -s https://repos.influxdata.com/influxdata-archive_compat.key | gpg --dearmor | sudo tee /usr/share/keyrings/influxdb.gpg > /dev/null
echo "deb [signed-by=/usr/share/keyrings/influxdb.gpg] https://repos.influxdata.com/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/influxdb.list
sudo apt update
sudo apt install -y influxdb2 influxdb2-cli
log_success "Database systems installed"
}
# Install text editors and IDEs
install_editors() {
log_section "TEXT EDITORS & IDEs"
# Neovim (latest stable)
log_info "Installing Neovim..."
sudo add-apt-repository ppa:neovim-ppa/stable -y
sudo apt update
sudo apt install -y neovim
# Install vim-plug for Neovim
sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'
# Visual Studio Code
log_info "Installing Visual Studio Code..."
wget -qO- https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
sudo install -o root -g root -m 644 packages.microsoft.gpg /etc/apt/trusted.gpg.d/
sudo sh -c 'echo "deb [arch=amd64,arm64,armhf signed-by=/etc/apt/trusted.gpg.d/packages.microsoft.gpg] https://packages.microsoft.com/repos/code stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt update
sudo apt install -y code
rm packages.microsoft.gpg
# Sublime Text
log_info "Installing Sublime Text..."
wget -qO - https://download.sublimetext.com/sublimehq-pub.gpg | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/sublimehq-archive.gpg > /dev/null
echo "deb https://download.sublimetext.com/ apt/stable/" | sudo tee /etc/apt/sources.list.d/sublime-text.list
sudo apt update
sudo apt install -y sublime-text
# JetBrains Toolbox (for IntelliJ, PyCharm, etc.)
log_info "Installing JetBrains Toolbox..."
local toolbox_version
toolbox_version=$(curl -s 'https://data.services.jetbrains.com/products/releases?code=TBA&latest=true&type=release' | jq -r '.TBA[0].downloads.linux.link')
wget "$toolbox_version" -O jetbrains-toolbox.tar.gz
tar -xzf jetbrains-toolbox.tar.gz
local toolbox_dir
toolbox_dir=$(find . -maxdepth 1 -type d -name "jetbrains-toolbox-*" | head -n 1)
sudo mv "$toolbox_dir/jetbrains-toolbox" /usr/local/bin/
rm -rf jetbrains-toolbox.tar.gz "$toolbox_dir"
log_success "Text editors and IDEs installed"
}
# Install development utilities
install_dev_utilities() {
log_section "DEVELOPMENT UTILITIES"
# GitHub CLI
log_info "Installing GitHub CLI..."
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install -y gh
# Git extras
log_info "Installing Git utilities..."
sudo apt install -y \
git-flow \
git-lfs \
tig \
gitk \
git-gui \
meld \
diffutils
# LazyGit
log_info "Installing LazyGit..."
local lazygit_version
lazygit_version=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | grep -Po '"tag_name": "v\K[^"]*')
curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_${lazygit_version}_Linux_x86_64.tar.gz"
tar xf lazygit.tar.gz lazygit
sudo install lazygit /usr/local/bin
rm lazygit.tar.gz lazygit
# HTTPie
log_info "Installing HTTPie..."
python3 -m pip install --user httpie
# Postman
log_info "Installing Postman..."
wget https://dl.pstmn.io/download/latest/linux64 -O postman-linux-x64.tar.gz
sudo tar -xzf postman-linux-x64.tar.gz -C /opt
sudo ln -sf /opt/Postman/Postman /usr/local/bin/postman
rm postman-linux-x64.tar.gz
# Insomnia
log_info "Installing Insomnia..."
echo "deb [trusted=yes arch=amd64] https://download.konghq.com/insomnia-ubuntu/ default all" | sudo tee -a /etc/apt/sources.list.d/insomnia.list
sudo apt update
sudo apt install -y insomnia
# Bruno (API client)
log_info "Installing Bruno..."
local bruno_version
bruno_version=$(curl -s https://api.github.com/repos/usebruno/bruno/releases/latest | jq -r .tag_name)
wget "https://github.com/usebruno/bruno/releases/download/${bruno_version}/bruno_${bruno_version:1}_amd64_linux.deb"
sudo dpkg -i "bruno_${bruno_version:1}_amd64_linux.deb" || sudo apt-get install -f -y
rm "bruno_${bruno_version:1}_amd64_linux.deb"
# Modern Unix tools
log_info "Installing modern Unix tools..."
# bat (better cat)
sudo apt install -y bat
# Create symlink for bat (Ubuntu packages it as batcat)
sudo ln -sf /usr/bin/batcat /usr/local/bin/bat
# exa/#eza (better ls)
cargo install eza
# delta (better git diff)
cargo install git-delta
# dust (better du)
cargo install du-dust
# procs (better ps)
cargo install procs
# bottom (better top)
cargo install bottom
# hyperfine (benchmarking)
cargo install hyperfine
# tokei (code statistics)
cargo install tokei
# sd (better sed)
cargo install sd
# choose (better cut/awk)
cargo install choose
log_success "Development utilities installed"
}
# Install system monitoring and performance tools
install_monitoring_tools() {
log_section "SYSTEM MONITORING & PERFORMANCE"
# System monitoring
sudo apt install -y \
htop \
btop \
iotop \
iftop \
nethogs \
nload \
bmon \
vnstat \
sysstat \
lm-sensors \
stress \
stress-ng \
sysbench \
hardinfo \
inxi \
hwinfo \
lshw \
dmidecode \
smartmontools \
hdparm \
nvme-cli
# Glances (comprehensive monitoring)
log_info "Installing Glances..."
python3 -m pip install --user glances[all]
# Netdata (real-time monitoring)
log_info "Installing Netdata..."
bash <(curl -Ss https://my-netdata.io/kickstart.sh) --non-interactive
log_success "Monitoring and performance tools installed"
}
# Install security tools
install_security_tools() {
log_section "SECURITY TOOLS"
sudo apt install -y \
ufw \
fail2ban \
clamav \
clamav-daemon \
rkhunter \
chkrootkit \
lynis \
aide \
apparmor \
apparmor-utils \
firejail \
openssl \
gnupg2 \
pass \
keepassxc \
wireguard \
openvpn \
network-manager-openvpn
# Enable UFW firewall
log_info "Configuring UFW firewall..."
sudo ufw --force enable
# Update ClamAV database
log_info "Updating ClamAV database..."
sudo freshclam || log_warning "ClamAV database update failed"
log_success "Security tools installed and configured"
}
# Install multimedia tools
install_multimedia() {
log_section "MULTIMEDIA TOOLS"
sudo apt install -y \
vlc \
mpv \
ffmpeg \
obs-studio \
audacity \
gimp \
inkscape \
blender \
krita \
darktable \
rawtherapee \
handbrake \
kdenlive \
openshot \
pitivi \
cheese \
guvcview \
pavucontrol \
pulseaudio \
alsa-utils \
jack-tools \
qjackctl
# Install multimedia codecs
log_info "Installing multimedia codecs..."
sudo apt install -y ubuntu-restricted-extras
log_success "Multimedia tools installed"
}
# Install communication tools
install_communication() {
log_section "COMMUNICATION TOOLS"
# Slack
log_info "Installing Slack..."
local slack_version
slack_version=$(curl -s https://slack.com/api/desktop.latestRelease | jq -r '.linux')
wget "https://downloads.slack-edge.com/releases/linux/${slack_version}/prod/x64/slack-desktop-${slack_version}-amd64.deb"
sudo dpkg -i "slack-desktop-${slack_version}-amd64.deb" || sudo apt-get install -f -y
rm "slack-desktop-${slack_version}-amd64.deb"
# Discord
log_info "Installing Discord..."
wget "https://discord.com/api/download?platform=linux&format=deb" -O discord.deb
sudo dpkg -i discord.deb || sudo apt-get install -f -y
rm discord.deb
# Teams (if available)
log_info "Installing Microsoft Teams..."
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/
sudo sh -c 'echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] https://packages.microsoft.com/repos/ms-teams stable main" > /etc/apt/sources.list.d/teams.list'
sudo apt update
sudo apt install -y teams-for-linux || log_warning "Teams installation failed"
rm microsoft.gpg
# Zoom
log_info "Installing Zoom..."
wget https://zoom.us/client/latest/zoom_amd64.deb
sudo dpkg -i zoom_amd64.deb || sudo apt-get install -f -y
rm zoom_amd64.deb
# Telegram
sudo apt install -y telegram-desktop
log_success "Communication tools installed"
}
# Install browsers
install_browsers() {
log_section "WEB BROWSERS"
# Google Chrome
log_info "Installing Google Chrome..."
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | sudo tee /etc/apt/sources.list.d/google-chrome.list
sudo apt update
sudo apt install -y google-chrome-stable
# Firefox (latest)
log_info "Installing Firefox..."
sudo apt install -y firefox
# Brave Browser
log_info "Installing Brave Browser..."
curl -fsSLo /usr/share/keyrings/brave-browser-archive-keyring.gpg https://brave-browser-apt-release.s3.brave.com/brave-browser-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/brave-browser-archive-keyring.gpg arch=amd64] https://brave-browser-apt-release.s3.brave.com/ stable main" | sudo tee /etc/apt/sources.list.d/brave-browser-release.list
sudo apt update
sudo apt install -y brave-browser
# Microsoft Edge
log_info "Installing Microsoft Edge..."
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/
sudo sh -c 'echo "deb [arch=amd64 signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] https://packages.microsoft.com/repos/edge stable main" > /etc/apt/sources.list.d/microsoft-edge-dev.list'
sudo apt update
sudo apt install -y microsoft-edge-stable
rm microsoft.gpg
log_success "Web browsers installed"
}
# Install shells and terminal tools
install_shells() {
log_section "SHELLS & TERMINAL TOOLS"
# Fish shell
log_info "Installing Fish shell..."
sudo apt-add-repository ppa:fish-shell/release-3 -y
sudo apt update
sudo apt install -y fish
# Add Fish to /etc/shells if not present
if ! grep -q "$(which fish)" /etc/shells; then
log_info "Adding Fish to /etc/shells..."
echo "$(which fish)" | sudo tee -a /etc/shells > /dev/null
fi
# Set Fish as the default shell
log_info "Setting Fish as the default shell..."
if [ "$SHELL" != "$(which fish)" ]; then
sudo chsh -s "$(which fish)" "$USER"
log_success "Fish has been set as the default shell. The change will take effect after you log out and back in."
else
log_info "Fish is already the default shell"
fi
# Zsh and Oh My Zsh
log_info "Installing Zsh and Oh My Zsh..."
sudo apt install -y zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
# Starship prompt
log_info "Installing Starship prompt..."
curl -sS https://starship.rs/install.sh | sh -s -- -y
# Terminal emulators
log_info "Installing terminal emulators..."
sudo apt install -y \
alacritty \
kitty \
terminator \
tilix \
gnome-terminal
# Terminal multiplexers
sudo apt install -y tmux screen
# Install TPM (Tmux Plugin Manager)
log_info "Installing Tmux Plugin Manager..."
git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm
log_success "Shells and terminal tools installed"
}
# Install file managers and utilities
install_file_tools() {
log_section "FILE MANAGEMENT TOOLS"
# GUI file managers
sudo apt install -y \
thunar \
nautilus \
dolphin \
pcmanfm \
ranger \
nnn \
mc \
broot
# Archive tools
sudo apt install -y \
p7zip-full \
p7zip-rar \
rar \
unrar \
zip \
unzip \
tar \
gzip \
bzip2 \
xz-utils
# Sync and backup
sudo apt install -y \
rsync \
rclone \
duplicity \
borgbackup \
restic
log_success "File management tools installed"
}
# Install fonts
install_fonts() {
log_section "FONTS"
# Create fonts directory
mkdir -p ~/.local/share/fonts
# Install Nerd Fonts
log_info "Installing Nerd Fonts..."
local fonts_dir="$HOME/.local/share/fonts"
# Download popular Nerd Fonts
local fonts=(
"FiraCode"
"JetBrainsMono"
"Hack"
"SourceCodePro"
"UbuntuMono"
"CascadiaCode"
"Meslo"
)
for font in "${fonts[@]}"; do
log_info "Installing ${font} Nerd Font..."
wget "https://github.com/ryanoasis/nerd-fonts/releases/latest/download/${font}.zip" -O "${font}.zip"
unzip -o "${font}.zip" -d "$fonts_dir"
rm "${font}.zip"
done
# Update font cache
fc-cache -fv
log_success "Fonts installed"
}
# Install additional productivity tools
install_productivity() {
log_section "PRODUCTIVITY TOOLS"
sudo apt install -y \
libreoffice \
thunderbird \
evolution \
calibre \
zotero \
anki \
focuswriter \
typora \
obsidian \
notion-app \
simplenote \
xournalpp \
flameshot \
shutter \
peek \
kazam \
vokoscreen-ng \
greenshot
# Notion (if available)
log_info "Installing Notion..."
wget "https://github.com/notion-enhancer/notion-repackaged/releases/latest/download/notion-app_amd64.deb" -O notion.deb || log_warning "Notion download failed"
sudo dpkg -i notion.deb || sudo apt-get install -f -y || log_warning "Notion installation failed"
rm -f notion.deb
log_success "Productivity tools installed"
}
# Configure development environment
configure_environment() {
log_section "ENVIRONMENT CONFIGURATION"
# Set up shell paths
log_info "Configuring shell environment..."
# Add common paths to .bashrc
{
echo ""
echo "# Development tools paths"
echo 'export PATH="$HOME/.local/bin:$PATH"'
echo 'export PATH="$HOME/.cargo/bin:$PATH"'
echo 'export PATH="/usr/local/go/bin:$PATH"'
echo 'export PATH="$HOME/go/bin:$PATH"'
echo 'export GOPATH="$HOME/go"'
echo ""
echo "# Homebrew"
echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"'
echo ""
echo "# NVM (Node Version Manager)"
echo 'export NVM_DIR="$HOME/.nvm"'
echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"'
echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"'
echo ""
echo "# Python"
echo 'export PATH="$HOME/.local/bin:$PATH"'
echo ""
echo "# Aliases"
echo "alias ll='ls -alF'"
echo "alias grep='grep --color=auto'"
echo "alias fgrep='fgrep --color=auto'"
echo "alias egrep='egrep --color=auto'"
echo "alias bat='batcat'"
echo "alias cat='batcat'"
echo "alias find='fd'"
echo "alias ps='procs'"
echo "alias top='btm'"
echo "alias du='dust'"
echo "alias ls='eza'"
} >> ~/.bashrc
# Create useful aliases
log_info "Setting up useful aliases..."
{
echo "# System monitoring"
echo "alias ps='procs'"
echo "alias top='btm'"
echo "alias du='dust'"
echo "alias ls='eza --group-directories-first --color=auto'"
echo "alias ll='ls -la'"
echo "alias la='ls -a'"
} >> ~/.bashrc
# Configure npm (will use NVM's global directory)
# npm config set prefix is not needed with NVM
# Configure Git (basic)
log_info "Setting up basic Git configuration..."
git config --global init.defaultBranch main
git config --global pull.rebase false
git config --global core.editor "nano"
# Set up SSH directory
mkdir -p ~/.ssh
chmod 700 ~/.ssh
log_success "Environment configured"
}
# Install core snaps
install_core_snaps() {
log_section "CORE SNAPS"
# Ensure snapd is installed
if ! command -v snap &> /dev/null; then
log_info "Installing snapd..."
sudo apt update
safe_apt_install snapd
fi
# Enable snapd socket
if ! systemctl is-active --quiet snapd.socket; then
log_info "Enabling snapd socket..."
sudo systemctl enable --now snapd.socket
sudo systemctl start snapd.socket
fi
# Install core snaps
local core_snaps=(
"core"
"core18"
"core20"
"core22"
"snapd"
"snapd-desktop-integration"
)
for snap in "${core_snaps[@]}"; do
if ! snap list "$snap" &> /dev/null; then
log_info "Installing $snap snap..."
sudo snap install "$snap" --classic || log_warning "Failed to install $snap snap"
else
log_info "$snap snap is already installed"
fi
done
# Refresh all snaps
log_info "Refreshing all snaps..."
sudo snap refresh
log_success "Core snaps installed and up to date"
}
# Install Flatpak applications
install_flatpak() {
log_section "FLATPAK APPLICATIONS"
# Install Flatpak
sudo apt install -y flatpak
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
# Install common Flatpak applications
log_info "Installing Flatpak applications..."
local flatpak_apps=(
"com.spotify.Client"
"org.signal.Signal"
"org.telegram.desktop"
"com.discordapp.Discord"
"us.zoom.Zoom"
"com.slack.Slack"
"org.videolan.VLC"
"org.audacityteam.Audacity"
"org.gimp.GIMP"
"org.inkscape.Inkscape"
"org.blender.Blender"
"com.obsproject.Studio"
"org.libreoffice.LibreOffice"
"org.mozilla.Thunderbird"
"org.gnome.Calendar"
"org.gnome.Calculator"
"com.github.jeromerobert.pdfarranger"
"org.gnome.TextEditor"
"org.kde.kdenlive"
"org.flameshot.Flameshot"
)
for app in "${flatpak_apps[@]}"; do
log_info "Installing $app..."
flatpak install -y flathub "$app" || log_warning "Failed to install $app"
done
log_success "Flatpak applications installed"
}
# Final system cleanup and optimization
final_cleanup() {
log_section "FINAL CLEANUP & OPTIMIZATION"
# Clean package cache
log_info "Cleaning package cache..."
sudo apt autoremove -y
sudo apt autoclean
sudo apt clean
# Update locate database
log_info "Updating locate database..."
sudo updatedb
# Update font cache
log_info "Updating font cache..."
fc-cache -fv
# Update snap packages if snap is installed
log_info "Updating snap packages..."
if command -v snap &>/dev/null; then
sudo snap refresh || log_warning "Failed to refresh snap packages"
else
log_info "snapd not found, skipping snap updates"
fi
# Enable and start services
log_info "Enabling and starting services..."
for svc in docker ssh ufw fail2ban; do
if command -v systemctl &>/dev/null; then
sudo systemctl enable --now "$svc" || log_warning "Could not enable $svc"
fi
done
log_success "System cleanup completed"
}
# Display installation summary
show_summary() {
log_section "INSTALLATION SUMMARY"
log_info "Ubuntu Development Environment Setup Complete!"
echo
log_info "Installed components:"
echo " ✓ System updates and essential tools"
echo " ✓ Package managers (Homebrew)"
echo " ✓ Programming languages (Python, Node.js, Rust, Go, Java, Ruby, PHP, C/C++)"
echo " ✓ AI tools (Ollama with qwen2, gemma, nomic-embed-text models)"
echo " ✓ Containerization tools (Docker, Kubernetes, Helm)"
echo " ✓ Cloud platform tools (AWS, GCP, Azure CLIs, Terraform)"
echo " ✓ Database systems (PostgreSQL, MySQL, MongoDB, Redis)"
echo " ✓ Text editors and IDEs (Neovim, VS Code, Sublime Text, JetBrains)"
echo " ✓ Development utilities (Git tools, HTTP clients, modern Unix tools)"
echo " ✓ System monitoring and performance tools"
echo " ✓ Security tools and firewall configuration"
echo " ✓ Multimedia tools and codecs"
echo " ✓ Communication tools (Slack, Discord, Teams, Zoom)"
echo " ✓ Web browsers (Chrome, Firefox, Brave, Edge)"
echo " ✓ Shells and terminal tools (Fish, Zsh, Starship, Tmux)"
echo " ✓ File management and backup tools"
echo " ✓ Fonts (Nerd Fonts collection)"
echo " ✓ Productivity applications"
echo " ✓ Flatpak applications"
echo
log_warning "IMPORTANT POST-INSTALLATION STEPS:"
echo " 1. Reboot your system to ensure all changes take effect"
echo " 2. Log out and log back in to refresh group memberships"
echo " 3. Configure your preferred shell (Fish/Zsh) as default"
echo " 4. Set up your Git credentials: git config --global user.name/user.email"
echo " 5. Configure cloud CLI tools (aws configure, gcloud init, az login)"
echo " 6. Install additional programming language versions as needed"
echo " 7. Configure your preferred terminal emulator and shell theme"
echo " 8. Set up SSH keys for Git and server access"
echo
log_info "For shell configuration, run:"
echo " • Fish: chsh -s /usr/bin/fish"
echo " • Zsh: chsh -s /usr/bin/zsh"
echo
log_info "Reboot recommended to complete installation."
}
# Auto-approval function
auto_approve() {
if [[ ${AUTO_APPROVE:-0} -eq 1 ]]; then
REPLY=y
else
read -r -p "$1 [y/N] " -n 1
echo
fi
[[ $REPLY =~ ^[Yy]$ ]]
}
# Main execution function
main() {
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-y|--auto-approve) AUTO_APPROVE=1 ; shift ;;
*) log_error "Unknown option: $1" ; exit 1 ;;
esac
done
log_section "Starting Ubuntu Development Environment Setup"
log_info "Auto-approval is ${AUTO_APPROVE:-disabled}"
if ! auto_approve "Do you want to proceed with the setup?"; then
log_info "Setup cancelled by user"
exit 0
fi
log_section "UBUNTU DEVELOPMENT ENVIRONMENT SETUP"
log_info "Starting comprehensive development environment installation..."
log_info "This will install ALL essential development tools and applications."
log_info "Estimated time: 30-60 minutes depending on internet speed."
echo
# Check system information first
check_ubuntu_version
# Update system packages
if auto_approve "Update system packages?"; then
update_system
fi
# Install programming languages and runtimes
if auto_approve "Install programming languages and runtimes?"; then
install_programming_languages
fi
# Install containerization tools
if auto_approve "Install containerization tools (Docker, Kubernetes, etc.)?"; then
install_containers
fi
# Install cloud tools
if auto_approve "Install cloud platform tools (AWS, GCP, Azure, Terraform)?"; then
install_cloud_tools
fi
# Install databases
if auto_approve "Install database systems?"; then
install_databases
fi
# Install text editors and IDEs
if auto_approve "Install text editors and IDEs?"; then
install_editors
fi
# Install development utilities
if auto_approve "Install development utilities?"; then
install_dev_utilities
fi
# Install monitoring tools
if auto_approve "Install monitoring tools?"; then
install_monitoring_tools
fi
# Install security tools
if auto_approve "Install security tools?"; then
install_security_tools
fi
# Install multimedia tools
if auto_approve "Install multimedia tools?"; then
install_multimedia
fi
# Install communication tools
if auto_approve "Install communication tools?"; then
install_communication
fi
# Install browsers
if auto_approve "Install web browsers?"; then
install_browsers
fi
# Install shells and terminal tools
if auto_approve "Install shells and terminal tools?"; then
install_shells
fi
# Install file managers and utilities
if auto_approve "Install file managers and utilities?"; then
install_file_tools
fi
# Install fonts
if auto_approve "Install additional fonts?"; then
install_fonts
fi
# Install productivity tools
if auto_approve "Install productivity tools?"; then
install_productivity
fi
# Install core snaps
if auto_approve "Install core snaps (required for some applications)?"; then
install_core_snaps
fi
# Install Flatpak applications
if auto_approve "Install Flatpak applications?"; then
install_flatpak
fi
# Configure development environment
if auto_approve "Configure development environment?"; then
configure_environment
fi
# Final cleanup
if auto_approve "Perform final cleanup?"; then
final_cleanup
fi
# Show installation summary
show_summary
log_success "Ubuntu Development Environment Setup completed successfully!"
log_info "Please reboot your system to complete the installation."
}
# Trap errors and cleanup
trap 'log_error "Script interrupted. Some installations may be incomplete."; exit 1' INT TERM
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment