Over the years I have experimented with different approaches to setting up my personal infrastructure for developing software. This is the method which I currently use.
Explore my dotfiles on GitHub →
Synopsis
- Version dotfiles with Git
- List dependencies in
config.ini
- Use a setup script to install those dependencies
Dotfiles
All of my dotfiles live in ~/.dotfiles
and are symlinked to $HOME
. For example,
~/.vimrc -> ~/.dotfiles/.vimrc
Instead of managing the symlinks with GNU Stow, I now use a Python script.
#!/usr/bin/env python3
# Configuration lives in config.ini
import configparser
from os import symlink
from pathlib import Path
from subprocess import Popen
import argparse
parser = argparse.ArgumentParser(
prog="Local development setup utility",
description="Set up a local development environment with dotfiles, ports, node modules, and python packages.",
)
parser.add_argument("--macports", action="store_true")
parser.add_argument("--npm", action="store_true")
parser.add_argument("--dotfiles", action="store_true")
parser.add_argument("--pip", action="store_true")
args = parser.parse_args()
config = configparser.ConfigParser(allow_no_value=True)
config.read("config.ini")
# Install MacPorts
if args.macports:
for port in config["MacPorts"]:
print(f"Installing {port}")
macports = Popen(["port", "-N", "install", port.strip()])
# Install node modules
if args.npm:
for node_module in config["NPM"]:
print(f"Installing {node_module}")
npm = Popen(["npm", "install", "--global", node_module.strip()])
# Install Python packages
if args.pip:
for pkg in config["Pip"]:
print(f"Installing {pkg}")
pip = Popen(["pip3", "install", "--user", pkg.strip()])
# Symlink dotfiles
if args.dotfiles:
home = Path.home()
for item in config["Dotfiles"]:
item = item.strip()
src = f"{home}/.dotfiles/{item}"
dst = f"{home}/{item}"
if Path(dst).is_symlink():
print(f"{src}: symlink already exists")
else:
print(f"Linking {src} -> {dst}")
symlink(src, dst)
Running ./setup --dotfiles
symlinks all of the dotfiles listed under the Dotfiles
section of config.ini
.
config.ini
[MacPorts]
base16-vim
editorconfig-vim
fossil
hurl
jq
packer
stagit
terraform-1.2
universal-ctags
vim-dim
vim-fugitive
vim-prettier
vim-terraform
zsh-autosuggestions
zsh-syntax-highlighting
[NPM]
emmet
jedi-language-server
mermaid-cli
prettier
surge
typescript-language-server
vls
vscode-css-languageserver
vue-unused-components-checker
wrangler
yaml-language-server
[Pip]
proselint
black
flake8
[Dotfiles]
.ctags
.gitconfig
.nanorc
.npmrc
.pandoc
.tmux.conf
.vimrc
.zshenv
.zshrc
Ports, Node modules, and Python packages
config.ini
has sections for MacPorts, NPM, and Python. At the host level I install as few global dependencies as possible, preferring instead to use Ubuntu Multipass VMs for the bulk of my development work.
Custom Portfiles are used for installing Vim packages which are automatically loaded using Vim's packpath
option.
set packpath+=/opt/local/share/vim
Reasons this approach is useful
- Versioning my dotfiles with Git allows me to rollback if changes lead to undesired effects.
- Being able to
rsync
orgit clone
my dotfiles to other machines (such Multipass or Linode VMs) is useful - it provides me with familiar settings in those environments. - It simplifies clean installs and OS upgrades.
- It serves as a form of documentation; I know what is installed on my machine.
- All dependencies - ports, Node modules and Python packages are listed in a single location -
config.ini
.