This repository automates the setup of my personal machines, complete with my preferred dotfiles. On Ubuntu, an Ansible playbook installs my tooling and configuration; on macOS, a Homebrew script does the same. It also includes cross-platform LazyVim (Neovim) installers. This makes it easy to get started on a fresh install -- or reset an existing system -- on either platform.
To set up my personal Ubuntu machine, follow these steps:
- Run
setup.shto install Ansible and its dependencies. - Run
generate_ssh_github.shto generate an SSH key and add it to my GitHub account. - Run
download_appimages.shto download and configure necessary AppImages.
Note: Change GTK theme to Arc-Dark using gnome-tweaks.
If I'm on a Mac, follow these steps:
- Run
mac_scripts/setup_brew_mac.shto set up Homebrew and essential tools. - Run
generate_ssh_github.shto generate an SSH key and add it to my GitHub account. - Use GNU Stow to manage dotfiles:
stow git -t $HOME/ # Apply git configuration stow xre_zsh -t $HOME/ # Apply zsh configuration stow ghostty -t $HOME/ # Apply ghostty configuration stow aerospace -t $HOME/ # Apply aerospace configuration
Note: Neovim is no longer managed via Stow. Use the LazyVim script installation in the ⚡ LazyVim Setup section below.
- Manually install Aliento.
Switch keyboard layout to US (if using a US keyboard)
Use US, not US International. US International treats quote and apostrophe keys as dead keys (they underline and wait for a follow-up keystroke), which breaks normal typing.
- Go to System Settings → Keyboard → Input Sources
- Click +, search for English, and select US
- Click Add, then remove any other layouts (e.g. British, US International) using −
Fix Vim key-repeat (disable press-and-hold accent picker)
macOS intercepts held keys to show a diacritic popup, which breaks
held j/k navigation in Vim. Disable it with:
defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool falseRestart VSCode after running. To revert:
defaults delete NSGlobalDomain ApplePressAndHoldEnabledKey repeat speed
In System Settings → Keyboard, set:
- Key Repeat Rate → Fast
- Delay Until Repeat → Short
VimTeX (in Neovim) compiles with latexmk, which ships with MacTeX/TeX Live but
often isn't on the shell PATH. First locate it:
# Most reliable -- searches all the usual TeX locations
find /Library/TeX /usr/local/texlive /usr/local/bin /opt/homebrew -name "latexmk" 2>/dev/null
# Quick check if it's already on your shell PATH
which latexmk
# If `which` comes up empty, check if the TeX bin dir exists at all
ls /Library/TeX/texbin/ 2>/dev/null | head -5Symlinking the TeX bin into /usr/local/bin is the cleanest fix -- it also makes
pdflatex, bibtex, etc. work in Neovim without any further config. Bulk-symlink
the whole TeX bin (adjust the year/arch to match your install):
sudo ln -s /usr/local/texlive/2026/bin/universal-darwin/* /usr/local/bin/The lazyvim/ folder contains one-shot installers that set up
LazyVim along with my personal customisations on a
fresh machine. Pick the script for my platform:
| Platform | Script | Package manager |
|---|---|---|
| macOS | lazyvim/install_lazyvim_mac.sh |
Homebrew |
| Ubuntu | lazyvim/install_lazyvim_ubuntu.sh |
apt-get |
| Windows | lazyvim/install_lazyvim_windows.ps1 |
winget |
What each script does:
- Installs Neovim and LazyVim's recommended dependencies (
git,ripgrep,fd,fzf,lazygit, a C compiler, and a Nerd Font). On Ubuntu, Neovim comes from the officialneovim-ppa/unstablePPA (apt's default is usually too old for LazyVim);lazygitand the Nerd Font come from upstream releases since they aren't in apt; andfdis exposed as a shim for apt'sfdfind. - Backs up any existing Neovim config/state (timestamped
.bak.<date>), so re-running never clobbers an existing setup. - Clones the LazyVim starter into the
config dir (
~/.config/nvimon macOS/Ubuntu,%LOCALAPPDATA%\nvimon Windows) and strips its.git. - Copies my custom plugin specs from
lazyvim/nvim/lua/plugins/into the config. - Appends my auto-centring cursor keymaps (
lazyvim/keymaps-snippet.lua) tolua/config/keymaps.lua, guarded by a marker comment so re-runs don't duplicate them. - (Windows only) Writes
lua/config/options.luaso Neovim's built-in terminal uses PowerShell 7 (pwsh) instead of the legacycmd.exe/Windows PowerShell, guarded by the same marker-comment pattern so re-runs don't duplicate it.
My customisations (see each file in lazyvim/nvim/lua/plugins/):
- auto-save.nvim -- saves on leaving Insert mode / text change (1s debounce).
- noice.nvim -- disabled.
- hardtime.nvim -- enforces better Vim motion habits.
- nvim-surround -- add/change/delete surrounding brackets, quotes, tags.
- Auto-centring keymaps --
zzafter<C-d>/<C-u>,n/N,G,*/#. - flash.nvim -- built into LazyVim, no install needed (
sto jump). - snacks.nvim projects picker -- points the projects picker at my dev
folders. The path is resolved at runtime (no hardcoded absolute path): set
the
NVIM_DEV_DIRenvironment variable to override it, otherwise it defaults to~/Downloads/Reposon Windows, or~/Documents/dev/reposand~/Documents/reposelsewhere. Missing directories are ignored. Note: match the directory's real casing -- the picker dedupes by exact path string, so on Windows (case-insensitive lookups) a casing mismatch with therecentsource makes a repo show up twice.
macOS:
./lazyvim/install_lazyvim_mac.shUbuntu:
./lazyvim/install_lazyvim_ubuntu.shWindows (PowerShell):
# If scripts are blocked, allow for the current session first:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
./lazyvim/install_lazyvim_windows.ps1After running: launch nvim, let plugins install, then run :Lazy sync. Set
the terminal font to a Nerd Font so icons render.
The .github/workflows/lazyvim.yml workflow tests both installers whenever
files under lazyvim/ change (and on manual dispatch):
- Lint --
shellcheckon the macOS/Ubuntu scripts andPSScriptAnalyzeron the Windows script (runs on Linux, fast). - Smoke test -- actually runs each installer on macOS, Ubuntu, and Windows
runners, performs a headless
:Lazy sync, and verifies the custom plugins were cloned and the keymaps applied (the Windows run additionally checks that the PowerShell 7 shell options were written tooptions.lua).
I can test the Ansible playbook locally using Docker. This allows me to ensure that my playbook is working correctly before deploying it to my Ubuntu machine.
Build the Docker image with the following command:
docker build -t ansible-build -f Dockerfile .If I need to build the image without using cache, I can use the --no-cache flag:
docker build -t ansible-build -f Dockerfile . --no-cacheThe image has an ENTRYPOINT of entrypoint.sh, so running it directly runs
the Ansible playbook (prompting for the vault password since VAULT_PASS isn't
set locally):
docker run -it ansible-buildTo instead drop into a shell and explore the container -- e.g. to verify that the playbook applied correctly -- override the entrypoint:
docker run -it --entrypoint bash ansible-buildThe .github/workflows/ci.yml workflow is the CI gate for the Ansible setup.
It runs the same Docker-based test as above, but automatically in GitHub
Actions, so a broken playbook is caught before it ever reaches a real machine.
What it does:
- Checks out the repository on an
ubuntu-24.04runner. - Builds the Docker image from
Dockerfile, passing theVAULT_PASSsecret as a build argument (used to decrypt Ansible Vault files). - Runs the container, which executes
entrypoint.shand applies the full Ansible playbook (main.yml+ everything undertasks/).
What it checks: that the image builds and the playbook runs to completion without errors -- i.e. the dotfiles/tooling setup is still valid end to end.
When it runs: on every push to main, except when the only changes are
docs or platform-specific scripts (README.md, lazyvim/**, mac_scripts/**),
plus a manual workflow_dispatch button. This keeps the (relatively slow)
Docker build from running on changes that can't affect it.
The
VAULT_PASSsecret must be configured in the repository's Settings → Secrets and variables → Actions for the build to succeed.
The .github/workflows/shellcheck.yml workflow lints the repository's shell
scripts (setup.sh, entrypoint.sh, download_appimages.sh, and the
mac_scripts/ scripts) with shellcheck --severity=warning. It runs whenever
one of those scripts changes. The LazyVim shell script is linted separately by
lazyvim.yml, and the Vault-encrypted generate_ssh_github.sh is skipped.
.github/dependabot.yml enables weekly Dependabot updates for the
github-actions ecosystem, so the action versions used across these workflows
(e.g. actions/checkout) stay current automatically.
To decrypt a file that has been encrypted with Ansible Vault, use the following command:
ansible-vault decrypt generate_ssh_github.shYou will be prompted for the vault password. After providing the correct password,
the file generate_ssh_github.sh will be decrypted.
entrypoint.sh: Script that serves as the starting point for the Docker container.generate_ssh_github.sh: Script to generate an SSH key and add it to GitHub.setup.sh: Bash script to install Ansible and its dependencies on Ubuntu.download_appimages.sh: Script to download and configure AppImages.main.yml: Main Ansible playbook that automates the setup of the Ubuntu machine.tasks/: Ansible task files included bymain.yml(core setup, miniconda, R, desktop, dotfiles, rust, deb-get).README.md: Documentation for the repository.Dockerfile: Dockerfile for building a Docker image of the setup (used locally and by CI).secrets.yml: Encrypted file containing sensitive information, managed with Ansible Vault.mac_scripts/setup_brew_mac.sh: Bash script to set up Homebrew and essential tools on macOS.mac_scripts: Folder containing scripts for macOS.lazyvim/: Folder with LazyVim installers and shared Neovim config.lazyvim/install_lazyvim_mac.sh: Installs Neovim + LazyVim + customisations on macOS via Homebrew.lazyvim/install_lazyvim_ubuntu.sh: Installs Neovim + LazyVim + customisations on Ubuntu via apt-get.lazyvim/install_lazyvim_windows.ps1: Installs Neovim + LazyVim + customisations on Windows via winget.lazyvim/nvim/lua/plugins/: Custom LazyVim plugin specs copied into the Neovim config.lazyvim/keymaps-snippet.lua: Auto-centring cursor keymaps appended tokeymaps.lua..github/workflows/ci.yml: CI that builds the Docker image and runs the Ansible playbook end to end..github/workflows/shellcheck.yml: CI that lints the repository's shell scripts with ShellCheck..github/workflows/lazyvim.yml: CI that lints and smoke-tests the LazyVim installers..github/dependabot.yml: Weekly Dependabot updates for GitHub Actions versions.
This repository is tailored to my personal preferences and is not intended for general use.