Created
April 7, 2026 15:57
-
-
Save tosin2013/67025b9020f0a7dfcf13d471da9230da to your computer and use it in GitHub Desktop.
setup-dev-ubuntu.sh
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -Eeuo pipefail | |
| # Ubuntu developer workstation bootstrap | |
| # Installs: | |
| # - Node.js 20 | |
| # - Docker Engine + compose/buildx plugins | |
| # - Git | |
| # - Common developer tools | |
| log() { | |
| echo "" | |
| echo "==> $*" | |
| } | |
| fail() { | |
| echo "ERROR: $*" >&2 | |
| exit 1 | |
| } | |
| require_root() { | |
| if [[ "${EUID}" -ne 0 ]]; then | |
| fail "Run this script with sudo or as root." | |
| fi | |
| } | |
| detect_user() { | |
| if [[ -n "${SUDO_USER:-}" && "${SUDO_USER}" != "root" ]]; then | |
| TARGET_USER="${SUDO_USER}" | |
| else | |
| TARGET_USER="$(logname 2>/dev/null || true)" | |
| TARGET_USER="${TARGET_USER:-root}" | |
| fi | |
| } | |
| check_ubuntu() { | |
| if [[ ! -f /etc/os-release ]]; then | |
| fail "/etc/os-release not found. This does not appear to be Ubuntu." | |
| fi | |
| . /etc/os-release | |
| if [[ "${ID:-}" != "ubuntu" ]]; then | |
| fail "This script is intended for Ubuntu only. Detected ID=${ID:-unknown}" | |
| fi | |
| ARCH="$(dpkg --print-architecture)" | |
| UBUNTU_CODENAME="${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}" | |
| if [[ -z "${UBUNTU_CODENAME}" ]]; then | |
| fail "Could not determine Ubuntu codename." | |
| fi | |
| log "Detected Ubuntu ${VERSION_ID:-unknown} (${UBUNTU_CODENAME}) on ${ARCH}" | |
| } | |
| apt_update_upgrade() { | |
| log "Updating apt metadata" | |
| apt-get update -y | |
| log "Upgrading installed packages" | |
| DEBIAN_FRONTEND=noninteractive apt-get upgrade -y | |
| } | |
| install_base_packages() { | |
| log "Installing base packages" | |
| DEBIAN_FRONTEND=noninteractive apt-get install -y \ | |
| ca-certificates \ | |
| curl \ | |
| wget \ | |
| gnupg \ | |
| lsb-release \ | |
| apt-transport-https \ | |
| software-properties-common \ | |
| git \ | |
| unzip \ | |
| zip \ | |
| jq \ | |
| build-essential \ | |
| make \ | |
| gcc \ | |
| g++ \ | |
| pkg-config \ | |
| python3 \ | |
| python3-pip \ | |
| pipx \ | |
| vim \ | |
| nano \ | |
| tmux \ | |
| htop \ | |
| tree \ | |
| ripgrep \ | |
| fd-find \ | |
| xclip \ | |
| openssl \ | |
| net-tools \ | |
| dnsutils \ | |
| iputils-ping \ | |
| telnet \ | |
| nmap \ | |
| rsync \ | |
| shellcheck \ | |
| bash-completion | |
| } | |
| cleanup_old_docker_packages() { | |
| log "Removing conflicting Docker packages if present" | |
| apt-get remove -y docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc || true | |
| } | |
| install_docker_repo() { | |
| log "Configuring Docker official apt repository" | |
| install -m 0755 -d /etc/apt/keyrings | |
| curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc | |
| chmod a+r /etc/apt/keyrings/docker.asc | |
| cat >/etc/apt/sources.list.d/docker.sources <<EOF | |
| Types: deb | |
| URIs: https://download.docker.com/linux/ubuntu | |
| Suites: ${UBUNTU_CODENAME} | |
| Components: stable | |
| Architectures: ${ARCH} | |
| Signed-By: /etc/apt/keyrings/docker.asc | |
| EOF | |
| apt-get update -y | |
| } | |
| install_docker() { | |
| log "Installing Docker Engine and plugins" | |
| DEBIAN_FRONTEND=noninteractive apt-get install -y \ | |
| docker-ce \ | |
| docker-ce-cli \ | |
| containerd.io \ | |
| docker-buildx-plugin \ | |
| docker-compose-plugin | |
| systemctl enable docker | |
| systemctl start docker | |
| } | |
| configure_docker_group() { | |
| log "Configuring docker group for user: ${TARGET_USER}" | |
| groupadd docker 2>/dev/null || true | |
| usermod -aG docker "${TARGET_USER}" || true | |
| } | |
| cleanup_old_nodesource_entries() { | |
| log "Cleaning up old NodeSource entries if they exist" | |
| rm -f /etc/apt/sources.list.d/nodesource.list | |
| rm -f /etc/apt/sources.list.d/nodesource.sources | |
| rm -f /usr/share/keyrings/nodesource.gpg | |
| rm -f /etc/apt/keyrings/nodesource.gpg | |
| } | |
| install_nodejs_20() { | |
| log "Installing Node.js 20 from NodeSource" | |
| install -m 0755 -d /etc/apt/keyrings | |
| # Use NodeSource setup script for Node 20 repo configuration | |
| curl -fsSL https://deb.nodesource.com/setup_20.x | bash - | |
| DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs | |
| } | |
| install_global_npm_tools() { | |
| log "Installing useful global npm tools" | |
| npm install -g npm@latest | |
| npm install -g \ | |
| yarn \ | |
| pnpm \ | |
| typescript \ | |
| ts-node \ | |
| eslint \ | |
| prettier \ | |
| nodemon \ | |
| serve | |
| } | |
| install_optional_cli_tools() { | |
| log "Installing optional CLI tools from apt where available" | |
| DEBIAN_FRONTEND=noninteractive apt-get install -y \ | |
| gh || true | |
| # pipx is already installed above; ensure path is configured for the target user | |
| if [[ "${TARGET_USER}" != "root" ]]; then | |
| sudo -u "${TARGET_USER}" env PATH="/usr/bin:/usr/local/bin:${PATH}" pipx ensurepath || true | |
| fi | |
| } | |
| verify_install() { | |
| log "Verifying installations" | |
| echo "Git version: $(git --version || true)" | |
| echo "Node version: $(node --version || true)" | |
| echo "npm version: $(npm --version || true)" | |
| echo "Docker version: $(docker --version || true)" | |
| echo "Buildx version: $(docker buildx version || true)" | |
| echo "Compose version: $(docker compose version || true)" | |
| echo "Python version: $(python3 --version || true)" | |
| echo "pipx version: $(pipx --version || true)" | |
| } | |
| post_notes() { | |
| cat <<EOF | |
| Bootstrap complete. | |
| Important: | |
| - Log out and back in, or run: newgrp docker | |
| so your '${TARGET_USER}' account can use Docker without sudo. | |
| - Test Docker with: | |
| docker run hello-world | |
| Installed tool categories: | |
| - Core dev: git, curl, wget, jq, build-essential, vim, tmux | |
| - Node.js: node, npm, yarn, pnpm, typescript, eslint, prettier | |
| - Containers: docker-ce, docker compose plugin, buildx | |
| - Utilities: ripgrep, fd-find, tree, htop, xclip, rsync, shellcheck | |
| EOF | |
| } | |
| main() { | |
| require_root | |
| detect_user | |
| check_ubuntu | |
| apt_update_upgrade | |
| install_base_packages | |
| cleanup_old_docker_packages | |
| install_docker_repo | |
| install_docker | |
| configure_docker_group | |
| cleanup_old_nodesource_entries | |
| install_nodejs_20 | |
| install_global_npm_tools | |
| install_optional_cli_tools | |
| verify_install | |
| post_notes | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment