My cross-platform bootstrap scripts. In a nutshell, these scripts do the super low-level plumbing that needs to happen before dotfiles can be applied. Supports Linux and macOS.
The scripts here all use the shebang #!/usr/bin/env sh, and when calling out to POSIX-compliant tools
such as sed or grep, we attempt to use them in the most POSIX-compliant way possible. No Bash/ZSH'isms,
we use $() intead of backticks, we use POSIX-compliant test syntax, globbing syntax, pattern matching,
. instead of source, etc.
That also means that we strive to use Posix BRE
for grep/sed, and we avoid GNU extensions to POSIX commands that can be found in the GNU coreutils.
This script does a small handful of things:
- Ensures that
homebrew(https://brew.sh) is installed - Ensures that
mise(https://mise.jdx.dev)is installed - Ensures that the 1Password CLI (https://developer.1password.com/docs/cli/) is installed
- Ensures that
age(https://github.com/FiloSottile/age) is installed - Export SSH keys from 1Password (via the CLI) to local disk
- Ensures that
chezmoi(https://www.chezmoi.io/) is installed - Bootstraps
chezmoidotfiles or grab (and optionally apply)chezmoidotfiles from a provided location
- Clone the repo
- Ensure the main
bootstrap.shfile in the repo root is executable - Run the main
bootstrap.shfile in the repo root
Many of the bootstrapping steps can have some of their behavior modified using flags.
For example, you can typically control how something is installed if there are multiple options. As an example,
you can control whether or not things like mise or chezmoi are installed using their curl | sh installers,
or via homebrew.
Use ./bootstrap.sh --help for more information on configuring behavior.
If a tool has its own installer, and that's the installtion method that is used, then you should likely be able to use that installer's env var options as well. See the install documentation for individual tools for more information.
./
├── _functions.d/
├── bootstrap.d/
├── darwin/
│ ├── bootstrap.d/
│ └── bootstrap.sh
├── linux/
│ ├── bootstrap.d/
│ └── bootstrap.sh
├── _functions.sh
├── bootstrap.sh*
└── README.md./bootstrap.sh is the entrypoint of the script. It handles argument parsing and establishing the environment used
by the rest of the configuration scripts.
_functions.sh establishes a handful of utility functions; it also automatically sourceses the files in the ./_functions.d
directory.
After ./bootstrap.sh sets up the execution environment for the bootstrapping scripts, it will execute the files in
bootstrap.d as well as any OS-specific files located in their respective directories. OS-specific configurations can contain
a root bootstrap.sh, and they can also contain their own bootstrap.d directories.
- on macOS, the OS-specific configs live in the
darwindirectory - on Linux, the OS-specific configs live in the
linuxdirectory
We utilize the convention of sorting files alphanumerically and using 2-digit prefixes to control execution order.
Important
Note that the sorting here is alphanumeric, not numeric. That means that 10 comes before 2, which is
why all script files should use a two-digit prefix to control priority; a leading 0 is used for single digit
priorities, i.e., 02 instead of 2.
The files in bootstrap.d are executed before the OS-specific files.
In other words, this is the file execution order:
bootstrap.shwill import_functions.shand_functions.d/*.shearly in executionbootstrap.shwill configure the execution environmentbootstrap.shwill execute all of the scripts inbootstrap.d/in alphanumerical orderbootstrap.shwill execute<os flavor>/bootstrap.shif it existsbootstrap.shwill execute<os flavor>/bootstrap.d/*.shin alphanumerical order, if any existbootstrap.shwill callchezmoi init --apply(or whatever chezmoi command is configured via the script flags)
_functions.sh establishes a message formatting system for reporting on events during script execution, availailble to all scripts.
The following functions are available:
infofor informational logswarnfor warning logserrorfor reporting critical errorsabortfor reporting a fatal error and then immediately exiting with return code1debugfor debug messages, which are not printed by defaultprintfis aliased toinforawprintprovides access to the unaliased/originalprintflinebreakis a helper function for inserting a blank line if and only if log messages have been emitted since the last timelinebreakwas called
There is no log level filtering outside of whether or not debug logs are printed. Debug logging is enabled with DEBUG_BOOTSTRAP=1
Additionally, the following variables are also available for ANSI text coloring:
# Activate bold foreground/text
BOLD
# Activate red foreground/text
RED
# Activate green foreground/text
GREEN
# Activate yellow foreground/text
YELLOW
# Activate blue foreground/text
BLUE
# Activate magenta foreground/text
MAGENTA
# Activate cyan foreground/text
CYAN
# Activate white foreground/text
WHITE
# Reset the foreground color/bold/etc. to normal
RESETCommand options/arguments are parsed and propagated to other scripts using environment variables, but after utility functions
have been imported. In other words, the following environment variables are exported by bootstrap.sh before other scripts are executed,
but they are not available to the _functions utlities, as those are imported before the environment is configured.
# The current directory of the shell session that the command was invoked from (i.e. `$PWD` or `/bin/pwd`)
export WORKDIR
# Absolute canonical path to the parent directory of the top-level `./bootstrap.sh` script on disk
export SCRIPTDIR
# A temporary dir created with `mktemp -d` for storing scratch files
export DLDIR
# Either `darwin` or `linux`
export OS_FLAVOR
# Normalized string representing the CPU Architecture; either `arm32`, `arm64`, or `amd64` (other architectures aren't supported)
export CPU_ARCH
# String combining the OS and Arch in the form `${OS_FLAVOR}_${CPU_ARCH}, e.g. `darwin_arm64` or `linux_amd64`
export MACHINE
# The positional argument that will be passed to `chezmoi init`
export CHEZMOI_DOTFILES_ARG
# Directory to install the `chezmoi` binary when using the curl | sh installation method
export CHEZMOI_INSTALL_PATH
# 0 or 1, whether or not to pass `--apply` to `chezmoi` (default 1)
export CHEZMOI_APPLY
# 0 or 1, whether or not to pass `--purge` to `chezmoi` (default 0)
export PURGE
# 0 or 1, whether or not to pass `--purge-binary` to `chezmoi` (default 0)
export PURGE_BINARY
# 0 or 1, whether or not to install `mise` using `brew` (default 0)
export BREW_MISE
# 0 or 1, whether or not to install `chezmoi` using `brew` (default 0)
export BREW_CHEZMOI
# 0 or 1, whether or not to install `chezmoi` using `mise use --global` (default 0)
export MISE_CHEZMOI
# directory to install the OP CLI binary if using the .zip installation method
export OP_CLI_INSTALL_PATH
# 0 or 1, whether or not to install `op` using `homebrew` (default 0)
export BREW_OP_CLI
# 0 or 1, whether or not to install `op` using the macOS .pkg installer (default 0)
export PKG_OP_CLI
# Optional, The URL of the 1Password sign-in server to use
export OP_SIGN_IN_ADDRESS
# Optional, The email address for the 1Password account
export OP_EMAIL
# Optional, The password for the 1Password account
export OP_PASSWORD
# Optional, The secret key for the 1Password account
export OP_SECRET_KEY
# 0 or 1, whether or not to download SSH private keys to disk in addition to the public keys (default 0)
export DOWNLOAD_PRIVATE_KEYS