Adds a shared ensure_flatpak helper in lib.sh (installs flatpak + adds the Flathub remote) and reuses it from both the slack module and desnap.sh's unmapped-snap fallback instead of duplicating the setup.
dotfiles
My configuration files, plus a modular installer for the tools I use on a
fresh Ubuntu box (targeting 26.04). Everything installs from each vendor's
official source — apt repos with their real signing keys, or their official
install scripts/tarballs. No PPAs of unclear provenance, no snaps unless the
vendor's own instructions use one (Ubuntu's own default snaps are actively
replaced — see desnap.sh below).
Quick start
On a fresh Ubuntu install:
git clone <this-repo> ~/dotfiles
cd ~/dotfiles
./bootstrap.sh
That will:
- Install
stow,zsh,curl,git. - Symlink the dotfiles (
zsh/,git/,tmux/,vscode/) into$HOMEwith GNU Stow. - Prompt once for your git name/email/signing key, saved to
~/.gitconfig.local(gitignored — never committed, so this repo stays shareable). - Set zsh as your default shell.
- Run
install/install.sh, which shows a checklist (see below) of every tool to install.
Open a new shell afterwards (or exec zsh) to pick up PATH changes.
Picking what to install
Run interactively with no arguments and install/install.sh shows a
whiptail checklist — space to toggle, enter to confirm. Everything's
checked by default; uncheck what you don't want. If whiptail isn't
available it's installed automatically; if that's not possible either it
falls back to a plain numbered prompt.
Non-interactive options:
./bootstrap.sh nvm node pnpm docker # only these modules (+ base deps)
./install/install.sh --list # list all module names
./install/install.sh --except discord,slack,spotify
./install/install.sh --all # skip the checklist, install everything
./install/install.sh --dry-run # show what would run without doing it
--dry-run works with any of the above (--dry-run --all,
--dry-run nvm docker, ...) and also works on bootstrap.sh and
desnap.sh.
Resilience: a failed module doesn't kill the run
Every module runs in its own subshell with errexit on. If a command in a
module fails partway through, that module's own changes (packages
installed, repos added, directories created, group memberships granted —
whatever it called rollback_push for) are unwound in reverse order, the
failure is recorded, and the next module still runs. At the end you get
a summary of what succeeded and what failed, and the script exits nonzero
only if something actually failed. Every module is also idempotent — safe
to re-run individually or all together.
What gets installed
| Category | Tools |
|---|---|
| System | apt mirror benchmarking, ~/.ssh/~/.gnupg permission fixes, eza/ripgrep |
| JS/TS runtimes | nvm, node (via nvm, LTS), pnpm, deno, bun |
| Python | pyenv, pipx, pipenv, poetry, uv |
| Systems languages | go, rust (rustup), zig + zls |
| Dev tooling | VS Code (+ your extensions/settings, mirrored to Cursor), Cursor, GitHub CLI (gh), Docker Engine |
| Apps | Chrome, Firefox, Discord, Slack, Postman, JetBrains Toolbox, mpv, Spotify, Telegram, Signal, ProtonVPN, OneDrive (abraunegg) |
| Desktop | GNOME Tweaks, Extension Manager, AppIndicator support |
| Misc | Wakatime API key setup |
Layout
bootstrap.sh entrypoint: stows dotfiles, then runs install/install.sh
desnap.sh standalone: purges snapd, replaces default snaps with apt/Flatpak
install/
lib.sh shared shell helpers (apt/repo/rollback/profile-line helpers)
install.sh module runner — TUI checklist, dry-run, rollback, summary
modules/*.sh one file per tool, each defines install_<name>()
zsh/ git/ tmux/ vscode/ stow packages — mirror their target layout under $HOME
To add a new tool: drop a new install/modules/<name>.sh defining
install_<name>(), and add <name> (+ a description) to ALL_MODULES /
MODULE_DESC in install/install.sh.
desnap.sh
Purges snapd entirely and pins it out of apt so it can't come back on upgrade. Before removing anything it works out, per installed snap, whether there's a real replacement:
- Snaps we already have an official-source module for (Firefox) get that module.
- Known default-Ubuntu snaps with a plain apt equivalent (Thunderbird, snap-store → gnome-software) get that apt package.
- GNOME/core runtime snaps (content-only, no user-facing app) are skipped.
- Anything else gets Flatpak + Flathub set up and is listed at the end so
you can
flatpak search/installit yourself, rather than guessing an app id.
Destructive and asks for confirmation (-y/--yes to skip the prompt);
supports --dry-run to preview the plan first.
./desnap.sh --dry-run
./desnap.sh
Notes
mirrorsbenchmarks official Ubuntu mirrors (speed + a freshness check onLast-Modified) and rewrites apt's sources to the fastest one — handles both the legacysources.listand the 24.04+/26.04 deb822ubuntu.sourcesformat. Runs early so the rest of the install benefits.security.ubuntu.comis left alone on purpose.firefoxremoves the transitional apt stub / snap package first and pins Mozilla's official apt repo, since stock Ubuntu ships Firefox as a snap.docker,onedriveeach check whether their upstream repo has packages for the running Ubuntu codename/version yet and fall back to the most recent known-supported one if it's too new (relevant right after a fresh 26.04 install).vscodeinstalls every extension listed invscode/extensions.txt(exported withcode --list-extensions) and mirrorssettings.jsonto Cursor via symlink, so both stay in sync from one source of truth.wakatimeprompts once for your API key and writes~/.wakatime.cfg(chmod 600); leave the prompt blank to skip.- Tool PATH/init lines are appended idempotently to the end of
zsh/.zshrcbyprofile_addininstall/lib.sh— re-running a module never duplicates a line.