dotfiles and packages

[categories] posts

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 or git 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.