A reference kit showing the folder structure, file formats, and execution order used by dotkit.
curl dotkit.run/sh | shPass options with --. See dotkit-run/install for more details.
curl -fsSL dotkit.run/sh | sh -s -- --defaultsThe examples below are assuming an active profile is set. So all "paths" are relative to that active profile. In this repo, the active profile would be called kit and its base dir would be ./profiles/kit.
# re-apply symlinks
dotkit link dotfiles
# re-run a package file
# tries to resolve missing file extension
# in order of .sh, .txt, then .md
dotkit run packages/brew
# add data to a run script's map block
# and execute added data with its parent map template
# this is a good way to update your dotkit
# and install packages on your system
dotkit add kit/packages/brewdotkit apply processes folders by default in this order:
dotkit/
profiles/
kit/
configs/
tools/
packages/
<other run dirs>/
<link dirs>/
<todo dirs>/
profile.env
.gitignore
dotkit.envconfigs- System preferences or that which requires no additional dependenciestools- Package managers and CLI toolchains (e.g.brew,npm,cargocan be used to install global packages)packages- Apps and packages installed via tools- Other run folders, alphabetically
- Folders with
link.envfile - symlink farms - Folders with
todo.env- manual checklists
configs, tools, and packages are reserved names always run in that order, but their presence is not required. You could just as easily have a tasks folder and put everything in there instead, and it will be executed alphabetically in that subfolder.
The ordering can also be overridden with an order.txt file. You could place a tasks folder in between tools and packages as such:
> cat kit/order.txt
configs
tools
tasks
packagesEven more simply, it will identify that the reserved name packages has been ordered differently. The below example will execute in the same order as the example above:
> cat kit/order.txt
tasks
packagesThere are three kinds of folders:
- run (default)
- link (symlink farms)
- todo (manual checklists)
They can be denoted by an .env file at the root of each folder. If no .env file is found, it will default to a "run" folder. If more than one applicaple .env file is found, the program will throw an error.
dotkit apply will source any dotkit.env in the root folder, followed by the profile's profile.env. And then any additional run, link, todo .env files will be sourced for each subfolder under profiles/kit/.
| File | Type | Effect |
|---|---|---|
link.env |
link | dotkit link - symlinks your dotfiles |
todo.env |
todo | dotkit todo - generates a checklist |
run.env |
run | env vars sourced before running scripts |
| (none) | run | files executed with dotkit run |
Run folders are only executed at that level. Subfolders are not executed. That way you can place additional info/data/runners inside subfolders for your base-dirs run files.
# env vars available to all scripts in this folder
DEV_DIR=$HOME/dev
GITHUB_USER=yournameA link.env file can designate a target location. Therefore running dotkit link on that folder will automatically apply it to the correct location.
# destination for symlinks
DEST=~
# same thing, also equivalent to
DEST=$HOMEA todo.env can designate a custom output location as well. If you want the To-do to show up on your desktop, you can set it to ~/Desktop or such. Since generating these files can clutter your personal dotkit's repo, it is recommended to have a .gitignore file with a line like **/TODO.md in it.
# output location (relative to todo.env)
DEST=..
# output filename (default: TODO.md)
NAME=MANUAL.md
# H1 heading (default: folder name)
TITLE="My Setup Checklist"configs/ - Applied first because they have no dependencies. System preferences (dock layout, keyboard repeat rate, default browser) can be set on a brand new machine before anything is installed. Running these early means your environment feels right from the start, even mid-setup.
tools/ - The bootstrap layer. These install the package managers and toolchains everything else depends on: Homebrew, Rust/cargo, Node/fnm, Python/uv. A tools file can also install formulae that unlock later steps - for example, installing mas (Mac App Store CLI) here so the packages step can use it.
packages/ - With tools in place, install everything else: applications, global packages, fonts, casks. These files can use the package managers installed in the tools step.
Any remaining folders - Any additional folders without a type marker (or with run.env) run next.
Link folders - Folders with a link.env file are treated as symlink farms. This link.env file is required! Dotkit cannot parse the folder properly without it. It will not know whether to treat it as a run folder or not. In folders with a link.evn, dotkit link is called for each, symlinking every file to the configured destination. These run after all plain run folders because some tools or packages may need to be in place for the shell config to work correctly. Any existing file will be backed up in the same place before placing a symlink at its location.
Todo folders - Folders with a todo.env file generate a TODO.md checklist of manual steps that can't be automated: signing in to accounts, configuring VPN, installing apps that need a GUI, etc. These run last.
Dotkit files come in two flavors - .txt for structured blocks, .md for readable markdown. Both produce the same result. Dotkit cli also comes with helpful functions for shell scripts as well (e.g. dotkit map). Any .sh file found in a run folder will also be executed.
All txt and md files are parsed into shell scripts. Text files separate blocks by --- lines and Markdown files separate based on ## headers. Block types:
try <cmd>- run fallback commands in block only ifcmdin first line failsmap <template>- run template once per data line, substituting{{1}},{{2}},{{3}}...etc.- In map blocks
itemandkeyare aliases for1.valueis an alias for2. - Elements of map block data lines are delimited by space-padded equal signs
=. ask- can be one of three kinds of interactive prompts.- Anything not starting their block with a
try,map, oraskis plain shell, run all lines in that block as-is.
Blocks are separated by ---. Lines beginning with # are comments. The first non-comment line of each block sets the type:
try command -v brew
NONINTERACTIVE=1
dotkit install https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh
for p in /opt/homebrew /usr/local ~/.linuxbrew; do eval "$($p/bin/brew shellenv 2>/dev/null)" 2>/dev/null || true; done
---
map brew install {{item}}
gh
git
ripgrep
neovim
---
map brew install --cask {{item}}
firefox
visual-studio-codeYou can see here that a tools folder isn't necessary to pre-install homebrew. It can be part of your packages file as a try block. If it doesn't find it, it will install it for you, if it's found on the system, it skips that block and goes straight into the brew install {{item}} section. You could put in shell commands as well. For example, if you know you'll need mas to install Mac App Store, you could place that before the first map block as it's own line brew intall mas. Since brew install mas doesn't begin with a try, map, or ask, it will be executed as a shell command. And at the very bottom you could add another map block for mas items such as:
map mas install -y {{item}}
# Xcode
497799835
# WhatsApp
3106339972nd-level Headers mark the block type. The first bullet is the rest of the command. Subsequent bullets are used for commands/data lines. Map templates do not use {{ }} but rather are marked in ** ** bold.
Lines without any ## or - at the start, are treated as comments. So a top-level header like # Title is not parsed by dotkit. Neither is something like Use github script: on it's own line.
Markdown links are parsed into plain URLs (whatever was in the parentheses).
# Install Homebrew & Formulae
This will check if homebrew is installed on the machine and also installs formulae.
## try
- command -v brew
- dotkit install [install.sh](https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)
## map
- brew install **item**
- gh
- ripgrep**item** in markdown maps to {{item}} in the text format.
This repo is licensed under the BSD Zero Clause License, which is a modified ISC license. It is a permissive one that does not require any attribution. So forks/derivatives of this repo may add/modify/delete any and all files including the LICENSE itself.