Skip to content

Instantly share code, notes, and snippets.

@bgrewell
Created November 25, 2025 22:51
Show Gist options
  • Select an option

  • Save bgrewell/23a7ba4adf742403c98d607c808f080f to your computer and use it in GitHub Desktop.

Select an option

Save bgrewell/23a7ba4adf742403c98d607c808f080f to your computer and use it in GitHub Desktop.
Bootstrap Desktop is my custom install script for setting up Linux environments
#!/usr/bin/env bash
set -euo pipefail
# ================================================================
# Config: edit this section to match your preferences
# ================================================================
# --- APT packages ------------------------------------------------
COMMON_APT_PACKAGES=(
git
curl
wget
htop
tmux
build-essential
python3
python3-pip
ca-certificates
)
UBUNTU_APT_PACKAGES=(
gnome-tweaks
fonts-firacode
)
KALI_APT_PACKAGES=(
kali-tools-top10
)
# --- Snap packages (optional) -----------------------------------
# Each entry is literally appended to `snap install`, so:
# "code --classic" => snap install code --classic
SNAP_PACKAGES=(
# "code --classic"
)
# --- Custom installers (git clone, curl | bash, etc.) ------------
custom_installers() {
# Example: install Starship prompt
# if ! command -v starship &>/dev/null; then
# curl -sS https://starship.rs/install.sh | sh -s -- -y
# fi
:
}
# --- PATH entries to ensure are present --------------------------
PATH_ENTRIES=(
"$HOME/bin"
"$HOME/.local/bin"
)
# --- GNOME config ------------------------------------------------
# Favorites in the dock (GNOME Shell)
GNOME_DOCK_FAVORITES=(
"org.gnome.Nautilus.desktop"
"org.gnome.Terminal.desktop"
"firefox.desktop"
# "code.desktop"
)
# Background image – must exist for this to work
GNOME_BACKGROUND_IMAGE="$HOME/Pictures/wallpapers/default-bg.jpg"
# Optional: dconf dump file for GNOME Terminal profiles
# You can create one with:
# dconf dump /org/gnome/terminal/legacy/profiles:/ > gnome-terminal.dconf
GNOME_TERMINAL_DCONF_FILE="$HOME/.config/gnome-terminal.dconf"
# ================================================================
# Helpers
# ================================================================
log() {
printf '[*] %s\n' "$*" >&2
}
warn() {
printf '[!] %s\n' "$*" >&2
}
die() {
printf '[X] %s\n' "$*" >&2
exit 1
}
# sudo wrapper – override with SUDO= if you want
SUDO="${SUDO:-sudo}"
detect_distro() {
if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
. /etc/os-release
echo "${ID:-unknown}"
else
echo "unknown"
fi
}
ensure_path_entry_in_shell_rc() {
local entry="$1"
local shell_rc="$2"
[[ -f "$shell_rc" ]] || return 0
local marker="# BEGIN bootstrap PATH $entry"
if grep -Fq "$marker" "$shell_rc"; then
return 0
fi
log "Adding PATH entry '$entry' to $shell_rc"
{
echo ""
echo "$marker"
echo "if [[ \":\$PATH:\" != *\":$entry:\"* ]]; then"
echo " export PATH=\"$entry:\$PATH\""
echo "fi"
echo "# END bootstrap PATH $entry"
} >>"$shell_rc"
}
# ================================================================
# Tasks
# ================================================================
install_packages() {
local distro pkgs=()
distro="$(detect_distro)"
if ! command -v apt-get &>/dev/null; then
die "apt-get not found; this script currently expects an APT-based distro."
fi
pkgs+=("${COMMON_APT_PACKAGES[@]}")
case "$distro" in
ubuntu)
pkgs+=("${UBUNTU_APT_PACKAGES[@]}")
;;
kali)
pkgs+=("${KALI_APT_PACKAGES[@]}")
;;
*)
warn "Unknown distro '$distro'; installing only COMMON_APT_PACKAGES."
;;
esac
if ((${#pkgs[@]} > 0)); then
log "Updating APT package index"
$SUDO apt-get update -y
log "Installing APT packages: ${pkgs[*]}"
$SUDO apt-get install -y "${pkgs[@]}"
else
log "No APT packages configured to install."
fi
if ((${#SNAP_PACKAGES[@]} > 0)); then
if ! command -v snap &>/dev/null; then
warn "snap not found; skipping Snap packages."
else
for snap_spec in "${SNAP_PACKAGES[@]}"; do
log "Installing Snap package: snap install ${snap_spec}"
# word splitting intentional here
snap install ${snap_spec}
done
fi
fi
}
update_paths() {
for entry in "${PATH_ENTRIES[@]}"; do
[[ -d "$entry" ]] || mkdir -p "$entry"
ensure_path_entry_in_shell_rc "$entry" "$HOME/.bashrc"
ensure_path_entry_in_shell_rc "$entry" "$HOME/.zshrc"
done
log "PATH modifications added. Open a new shell to pick them up."
}
configure_gnome() {
if ! command -v gsettings &>/dev/null; then
warn "gsettings not found; skipping GNOME configuration."
return
fi
local desktop="${XDG_CURRENT_DESKTOP:-}"
if [[ "$desktop" != *GNOME* && "$desktop" != *gnome* ]]; then
warn "XDG_CURRENT_DESKTOP does not look like GNOME ('$desktop'); continuing anyway."
fi
# Dock favorites
if ((${#GNOME_DOCK_FAVORITES[@]} > 0)); then
local favs
printf -v favs "'%s', " "${GNOME_DOCK_FAVORITES[@]}"
favs="[${favs%, }]"
log "Setting GNOME Shell favorites to: $favs"
gsettings set org.gnome.shell favorite-apps "$favs" || warn "Failed to set GNOME favorites."
fi
# Background
if [[ -f "$GNOME_BACKGROUND_IMAGE" ]]; then
local uri="file://$GNOME_BACKGROUND_IMAGE"
log "Setting GNOME background to: $uri"
gsettings set org.gnome.desktop.background picture-uri "$uri" \
|| warn "Failed to set picture-uri."
# Some GNOME versions use picture-uri-dark as well
gsettings set org.gnome.desktop.background picture-uri-dark "$uri" \
2>/dev/null || true
else
warn "Background image not found at $GNOME_BACKGROUND_IMAGE; skipping background."
fi
# GNOME Terminal profiles via dconf dump
if [[ -f "$GNOME_TERMINAL_DCONF_FILE" ]] && command -v dconf &>/dev/null; then
log "Loading GNOME Terminal profile from $GNOME_TERMINAL_DCONF_FILE"
dconf load /org/gnome/terminal/legacy/profiles:/ <"$GNOME_TERMINAL_DCONF_FILE" \
|| warn "Failed to load GNOME Terminal dconf file."
else
warn "GNOME Terminal dconf file not found or dconf missing; skipping terminal profile import."
fi
# Example: tweak dock behavior if dash-to-dock is present
if gsettings list-schemas | grep -q 'org.gnome.shell.extensions.dash-to-dock'; then
log "Configuring dash-to-dock options"
gsettings set org.gnome.shell.extensions.dash-to-dock dock-fixed true || true
gsettings set org.gnome.shell.extensions.dash-to-dock click-action 'minimize' || true
fi
}
run_custom_installers() {
log "Running custom installers"
custom_installers
}
# ================================================================
# Main
# ================================================================
usage() {
cat <<EOF
Usage: $(basename "$0") [task ...]
Tasks:
packages Install packages (APT + optional Snap)
paths Ensure PATH entries are present in shell rc files
gnome Configure GNOME (dock, background, terminal, etc.)
custom Run custom installers (curl/git/etc.)
all Run all of the above
If no task is given, 'all' is assumed.
EOF
}
main() {
if [[ "${1-}" == "-h" || "${1-}" == "--help" ]]; then
usage
exit 0
fi
local tasks=("$@")
if ((${#tasks[@]} == 0)); then
tasks=(all)
fi
for task in "${tasks[@]}"; do
case "$task" in
packages)
install_packages
;;
paths)
update_paths
;;
gnome)
configure_gnome
;;
custom)
run_custom_installers
;;
all)
install_packages
update_paths
configure_gnome
run_custom_installers
;;
*)
warn "Unknown task '$task'"
usage
exit 1
;;
esac
done
log "Done."
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment