Managing my dotfiles for MacOS and Linux.
For me, on a new system run the following:
curl -fsSL https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install |
bash -s -- -Pmpgn peter.nvimOr:
wget -qO- https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install |
bash -s -- -Pmpgn peter.nvimRead install script help information.
Bootstrap script.
Without any options provided, this script will check if required tools are
installed and will then clone or pull dotfiles repository to ~/.dotfiles.
Options can be provided to enable extra functionality.
Usage:
./install [-P] [-m] [-p] [-g] [-n <repo>] [-h]
Options:
-P Prime system to ensure system has required packages installed.
-m Install modules from MANIFEST.* files.
-p Install packages from MANIFEST.* files.
-g Authenticate with GitHub on this device.
-n <repo> Clone Neovim config as specified by <repo>.
<repo> can be one of three forms:
- A short name (e.g. "peter.nvim"), resolved as:
https://github.com/peter-bread/<repo>.git
- A full "owner/repo" pair (e.g. "someone/other.nvim"), resolved as:
https://github.com/<repo>.git
- A full Git URL (e.g. "https://github.com/user/repo.git"), HTTPS or SSH, used as-is
-h Print this help message.
Environment Variables:
These variables mirror the options above. Flags provided on the command line
take precedence over environment variables. All boolean variables default to 0
(disabled) unless otherwise noted.
Option Flags (0 = disable, 1 = enable):
PRIME -P
MODULES -m
PACKAGES -p
GITHUB -g
Option Arguments (string; default = empty):
NVIM -n <repo>
Logging and Output:
ENABLE_COLOR Enable colored logging output (default: 1)
ENABLE_DEBUG Enable debug logging (default: 0)
Developer / Advanced:
DEV_USE_LOCAL Developer mode: do not attempt to clone/pull dotfiles repo.
Useful when testing WIP scripts in a Docker container.
DEV_USE_TEST Developer mode: only use MANIFEST.test.
Ensure bash is available.
Download and execute install.
If on a super minimal system, you may need to download on a another machine and transfer with a USB.
If no options are provided, the script will:
- Check requirements (will exit if any errors are reported)
- Clone or update existing
~/.dotfilesrepo
Using curl:
curl -fsSL https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install | bashUsing wget:
wget -qO- https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install | bashUsing curl:
curl -fsSL https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install |
bash -s -- [options]Using wget:
wget -qO- https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install |
bash -s -- [options]The following examples will just print help information.
Using curl:
curl -fsSLo install https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install
chmod u+x ./install
./install -hUsing wget:
wget -q https://raw.githubusercontent.com/peter-bread/.dotfiles/refs/heads/main/install
chmod u+x ./install
./install -hWarning
If the script detects that the repo is out of date, it will pull changes then
restart itself. When it restarts, it will always use the version in
~/.dotfiles. If you downloaded the script and it fails after the first run,
be sure to delete it and only use the one in ~/.dotfiles in future.
See this issue.
This repository has an intentionally shallow file structure.
The intent is to make navigating to specific config files quicker, then relying
on install scripts to create symlinks and handle "real" directory structure.
The top level consists of a few key things:
| Item | Role |
|---|---|
install |
Entry point; clones repo, options to do more |
MANIFEST.* |
Files containing lists of modules and packages to be installed |
_* |
Repo utilities; NOT modules |
| other directories | Modules containing config files and install scripts |
Each module consists of:
| Item | Role |
|---|---|
install |
Script that creates directories and symlinks; can contain any logic |
README.md |
Documentation for the module |
| config files | Self-explanatory |
Manifest files describe what modules and packages should be installed for a
given system. MANIFEST.common is for configuration shared across operating
systems and will be read unconditionally. For MacOS and Linux, MANIFEST.macos
and MANIFEST.linux are read, respectively.
The format of a MANIFEST file is shown below:
# Lines starting with a hash are comments.
# The general structure for an entry in this file is:
# <category>:<name>
# Lines starting with 'mod:' are treated as modules. Whatever follows is the
# name of the module to install. The name should exactly match one of the
# top-level module directories in this repository.
mod:bash
# Lines starting with 'pkg:' are treated as packages. Whatever follows is the
# method and name of the module to install. This section should exactly match
# the file system in the packages directory.
pkg:manual/rustup
pkg:brew/Brewfile
There are three types of modules:
- Config
- Data
- Actions
| Module Type | Files | Install | Description |
|---|---|---|---|
| Config | ✅ | ✅ | Config files for tools that need to be symlinked to correct locations |
| Data | ✅ | ❌ | Files that are used by other modules or scripts |
| Actions | ❌ | ✅ | Perform operations, e.g. creating directories |
Data and Actions are classed as "Special" modules.
Data modules should NOT be included in MANIFEST files.
Most modules are Config modules, however there are some Special modules.
Packages contains files and scripts used to install software. It
can be used by using the -p option with the top-level install script.
See below for more information.
Env provides a unified environment layer that centralises shell-agnostic environment settings. Currently it defines environment variables, but it may later include aliases and POSIX-compatible helper functions for cross-shell consistency.
These scripts are sourced by shell startup files in the bash and zsh
modules, not executed.
Dirs is responsible for creating directories to ensure a consistent work environment.
Packages contains files and scripts used to install software. It
can be used by using the -p option with the top-level install script.
Packages to install are declared in manifest files using the pkg: prefix. The
data that follows is a relative path from packages/ pointing
to the file or directory that describes how a package or packages should be
installed (TODO: or
updated). In the packages
directory, there is a subdirectory for each installation method.
The following sections go into more detail about the supported installation methods.
Note
It is likely that more methods will be added in the future, but probably only
when I actually need them. The categories will probably just be package
managers for different Linux distros with files containing lists of packages
to install. For Void this could also contain xbps-src package templates
too. Alternatively, have a list category, and each file inside can
correspond to a package manager.
There could also be ways to manage package managers for programming
languages, e.g. cargo, npm, uv etc. Some of these can be handled with
Brewfiles, but I will work out exactly how to handle it in the future.
Potentially some kind of Nix packages but this will likely go in its own module directory or even a separate repository.
See tracking issue.
Manual packages are those that need to be installed without a package manager
for some reason. In packages/manual there is a directory for each package.
Within these directories, there are up to five scripts, three of which can
currently be used for installation, and a patches/ directory containing any
patches that may need to be applied by one of the scripts. The scripts are:
install, postinstall and completions (update and uninstall are not
used during installtion).
If pkg:manual/foo is found in a MANIFEST file, the following occurs, where
all scripts are relative to packages/manual/foo/:
- Attempt to find and run
install, - Attempt to find and run
postinstallif and only ifinstallran successfully, - Attempt to find and run
completions.
The completions script does not depend on the previous two scripts running,
just that no failures occur. This is because some packages, for example Zig,
can be installed and upgraded via Homebrew, but completions need to be manually
installed.
In most cases, the install script should check if a package is already
installed, and if it is, exit cleanly. This means any out of date packages will
be left as they are and will not be updated. See this
issue.
Note
I may in the future add the ability to only run certain scripts, for example maybe on MacOS I only want Zig completions, but on Linux I also want to run an actual install script too.
See tracking issue.
Note
In the future I may also create a small CLI interface for interacting with manually managed packages. (I could even then use that in the main install script when installing from a manifest).
See tracking issue.
Brew packages are those that can be installed with Homebrew. This is done using the Homebrew Bundle feature. This is a snippet from the official docs:
Homebrew Bundle is run with the
brew bundlecommand.It uses
Brewfiles to provide a declarative interface for installing/upgrading packages with Homebrew and starting services withbrew services.Rather than specifying the
brewcommands you wish to run, you can specify the state you wish to reach.
If pkg:brew/Brewfile is found in a MANIFEST file, the following command is
run:
brew bundle --file="packages/brew/Brewfile" --verboseThis command will install any missing packages and, by default, upgrade out of date packages. See this issue.
My Neovim configuration is in its own repository here.
It can be cloned manually or by using the -n option with the top-level
install script.