Set up a workstation¶
Use the review-first path for an existing machine. The one-line installer is intended for a new machine or for a configuration you already trust.
Clone the repository, inspect the configuration, and run the local checkout.
Fresh or already trusted machine Use the fast pathRun the hosted bootstrap and apply the complete configuration.
Before you begin¶
The full setup can:
- install apt packages, Homebrew packages, and macOS applications;
- change your login shell to fish;
- initialize chezmoi and force-apply files from this repository;
- install tmux and Neovim plugins;
- install Rancher Desktop on macOS; and
- change macOS defaults, Dock contents, and keyboard shortcuts.
Back up your existing configuration before continuing. At minimum, inspect
config.yaml
and the files under
config/.
Platform requirements¶
Install available system updates and the Xcode Command Line Tools on a fresh machine:
sudo softwareupdate -i -a
xcode-select --install
Use an account with sudo access. The bootstrap installs the apt
prerequisites, adds the Ansible PPA, and then installs Homebrew.
Apple Silicon macOS is the supported workstation target. Ubuntu 24.04 ARM64 is the continuously tested container and integration target. The setup may work on other Debian-family systems, but those systems are best effort. Other operating systems are rejected.
Recommended: review, then run¶
Clone the repository so you can inspect exactly what the setup will run. Pay
particular attention to
config.yaml
and the
main.yaml
playbook:
git clone https://github.com/shmileee/dotfiles.git
cd dotfiles
less scripts/common/ansible/config.yaml
less scripts/common/ansible/main.yaml
When you are comfortable with the configuration, run the complete setup:
./scripts/setup.sh --all
The script uses the current checkout, so local changes to package lists, Ansible roles, or managed dotfiles are included.
Fast path: bootstrap a new machine¶
curl -fsSL oponomarov.com/d | sh -s -- --all
The short URL redirects to
scripts/setup.sh
on the master branch. The
script downloads that branch into a unique temporary directory, runs the full
setup, and removes the temporary checkout when it exits.
Download the script before running it
If you want the convenience of the bootstrap without piping directly into a shell:
curl -fsSL https://raw.githubusercontent.com/shmileee/dotfiles/master/scripts/setup.sh > setup.sh
less setup.sh
chmod +x setup.sh
./setup.sh --all
What --all does¶
-
01
Validate the platform
Continue only on macOS or Linux, then locate the current checkout or download one.
-
02
Prepare Ubuntu
On the tested Linux target, install the required apt packages and Ansible before the shared setup begins.
-
03
Prepare Homebrew
Install Homebrew if necessary, add it to the current process, and disable analytics.
-
04
Run Ansible
Install the required collections, prompt for a sudo password if needed, and run the local playbook.
Run one stage¶
Stage flags must be run from a repository checkout.
| Command | Purpose |
|---|---|
./scripts/setup.sh --deps |
Install apt prerequisites on Linux; tested on Ubuntu 24.04 ARM64. |
./scripts/setup.sh --brew |
Install Homebrew if it is missing. |
./scripts/setup.sh --ansible |
Install Ansible collections and run every role. |
./scripts/setup.sh --all |
Run the complete platform-specific sequence. |
./scripts/setup.sh |
Same as --all. |
If the prerequisites are already installed, use Ansible to preview supported changes:
./scripts/common/ansible.sh --all --check
Check mode has limits
Ansible check mode is useful for file and package changes, but command-based tasks cannot always predict their effects. Treat it as a preview, not a full simulation.
Customize the setup¶
Make changes in four places:
| Area | Source of truth | Typical changes |
|---|---|---|
| Packages and applications | scripts/common/ansible/config.yaml |
Homebrew packages, casks, Dock items, keyboard shortcuts |
| System behavior | scripts/common/ansible/roles/ |
Installation logic and macOS defaults |
| Home-directory files | config/ |
fish, Git, tmux, Neovim, Alacritty, OpenCode |
| Tool versions | config/private_dot_config/mise/config.toml |
Language runtimes and developer tools |
The dotfiles.checkout value in
config.yaml
points chezmoi at the current repository checkout. The role force-applies that
checkout's config/
directory, so a fork or local changes can be tested without changing a separate
repository or branch setting.
Ansible roles¶
| Role | Responsibility |
|---|---|
common |
Install shared command-line tools and platform-specific packages and applications |
fonts |
Install developer fonts on macOS or the Ubuntu integration environment |
dotfiles |
Install chezmoi and apply the current checkout |
fish |
Install fish, make it the login shell, and synchronize Fisher plugins |
mise |
Install the tools declared in the mise configuration |
neovim |
Install LazyVim and its plugins in headless mode |
docker |
Install Rancher Desktop on macOS |
tmux |
Install tmux, TPM, and declared plugins |
system_defaults |
Apply macOS preferences, Dock items, and keyboard settings |
Routine work with mise¶
The initial setup installs mise and the tools pinned by this repository.
After that bootstrap, run routine workflows from the repository checkout with
mise run. List the available tasks and their descriptions at any time:
mise tasks
| Command | Purpose |
|---|---|
mise run reconcile |
Install the required Ansible collections and reconcile the machine. |
mise run reconcile:check |
Preview the reconciliation using Ansible check mode. |
mise run status |
Show differences between the chezmoi source and files in the home directory. |
mise run import |
Import all modified, non-template managed files into config/. |
mise run import ~/.config/nvim |
Import one managed file or directory. |
mise run docs |
Serve the documentation at http://localhost:8000 and rebuild it on changes. |
mise run docs:build |
Run the strict documentation build used by CI. |
Bootstrap before using tasks
mise is the task runner, but it is also installed by Ansible. On a new
machine, run ./scripts/setup.sh --all first. The mise tasks are the
post-bootstrap interface, not a replacement for initial setup.
Task execution installs any missing tools declared in mise.toml
automatically.
Import local dotfile changes¶
When a managed file was edited directly in the home directory, inspect the differences before copying them back into the repository:
mise run status
mise run import ~/.config/nvim
git diff -- config
Omit the path to import every modified managed file. The task uses
chezmoi re-add, which deliberately does not overwrite template source files.
For a rendered file backed by config/**/*.tmpl, reconcile the local change
with its template explicitly. Always review the resulting Git diff before
committing.
Preview the documentation¶
Start the local Zensical server with:
mise run docs
Open http://localhost:8000. The preview rebuilds when documentation, configuration, templates, CSS, or JavaScript change. Before committing a docs change, run the same strict build as CI:
mise run docs:build
Reapply after an update¶
After the initial setup has installed mise, pull the latest changes, review
them, and rerun the Ansible stage:
git pull --ff-only
git diff HEAD@{1} -- scripts/common/ansible config
mise run reconcile
The roles are designed to be rerun, and a repeated run should leave converged state unchanged. Setup may still refresh Homebrew metadata and update managed formulae or versioned casks, so review changes before rerunning after a long gap.
There is no repository-wide upgrade command. Use brew upgrade for an
intentional full Homebrew upgrade, let Renovate propose changes to versions
declared in the repository, and use Lazy, Fisher, or TPM for intentional plugin
updates. Review and commit any resulting lockfile, manifest, or Ansible pin
changes. The checked-out chezmoi source remains authoritative and is
force-applied during setup.
Try the Linux path in Docker¶
Run the published image:
docker run --rm -it shmileee/dotfiles
Or build the current checkout:
docker buildx build --platform linux/arm64 -t dotfiles --progress plain .
The image uses Ubuntu 24.04 ARM64 and runs the full Ansible installation as the
non-root linuxbrew user. Its smoke test verifies the installed tools and a
second, idempotent provisioning pass. The docker role is intentionally
skipped inside the container.
Validate changes locally¶
Run the same static checks and Ansible syntax validation used by CI:
mise install
mise exec -- scripts/common/ansible.sh --install
mise exec -- prek run --all-files
mise exec -- env ANSIBLE_CONFIG=scripts/common/ansible/ansible.cfg \
ansible-playbook --inventory '127.0.0.1,' \
--syntax-check scripts/common/ansible/main.yaml
If the prerequisites are installed, preview the playbook too:
mise run reconcile:check
Why Ansible and chezmoi?¶
Ansible owns machine state: packages, applications, services, shell setup, and operating-system preferences. Its roles make the order and platform conditions explicit, and repeated runs provide a practical convergence check.
chezmoi owns files in the home directory. It renders templates using facts such as the operating system and architecture, which keeps one source tree useful across macOS and the Ubuntu integration environment. Keeping these responsibilities separate makes it clear whether a change belongs to the machine or to the user's configuration.