dotpro
Ships the dotpro CLI, the single apply engine and day-2 entry point. One discoverable command wraps the common Ansible operations (install the collection, plan, apply) and the day-2 extras aliases cannot express (doctor, vars, capture, history), so the loop no longer lives as copy-paste aliases in every personal repo.
What it does
- Installs the
dotprobinary to~/.local/bin, reinstalled on every apply so it tracks the collection version - Drops
~/.profile.d/dotpro.shexporting the repo path, playbook, and limit settings, and loading shell completion straight from the binary - Ships no aliases by default; the
dotprobinary is invoked directly (add your own todotpro_aliases) - Warns on deprecated variables a host still sets (see below)
The dotpro command
dotpro is thin orchestration over ansible-playbook / ansible-galaxy: it shells out and never reimplements them, and it never parses their stdout. The repo it operates on is $DOTPRO_PATH (exported by this role; default ~/.dotfiles).
dotpro is a smart Ansible wrapper
Every dotpro command shells out to plain Ansible, so the raw command is always one step away:
dotpro apply # ansible-playbook playbook.yaml --ask-become-pass
dotpro plan # ansible-playbook playbook.yaml --check --diff
dotpro tags git # ansible-playbook playbook.yaml --tags git
dotpro requirements --upgrade # ansible-galaxy collection install -r requirements.yml --upgradeApply engine
| Command | Does |
|---|---|
dotpro init [dir] | Scaffold a new personal repo from a starter (interactive wizard, or --scale / --roles / --name / --email / --preset for CI) |
dotpro requirements | List the collections declared in requirements.yml and the version currently installed |
dotpro requirements --upgrade | Install or refresh the collection from requirements.yml |
dotpro plan [--limit H] [--tags T] [--output M] [--upgrade] [--quiet] | Preview the apply (--check --diff). Alias: dotpro check |
dotpro apply [--limit H] [--tags T] [--output M] [--upgrade] [--quiet] [--push|--no-push] | Bring this host to its desired state; installs the collection first if missing. --push / --no-push commits and pushes the repo after a success (or not); a clean repo is a no-op and a failed push warns without failing the apply. --quiet hides ok/skipped hosts. Alias: dotpro update |
dotpro upgrade [--tags T] [--all] | Refresh the collection and re-apply the ansible + dotpro roles (fresh ansible-core and the CLI itself); --tags also upgrades those roles, --all upgrades every role |
Bare dotpro requirements only lists what's declared and installed; apply installs the collection if it is missing; requirements --upgrade is the only thing that re-pulls a moving ref like main, so "upgrade" always means the collection, never the machine's packages (which apply brings to latest as a matter of course). --upgrade on apply opts that run into the software update checks by setting dotpro_upgrade=true.
Authorization (sudo)
dotpro apply and dotpro plan prompt for the sudo password only when the run needs it. Before prompting, a static scan walks the task files of the roles this run selects (honoring --tags, include/import chains, handlers, and the collection's ansible_facts.os_family guards) and drops the prompt entirely when no selected task escalates, so dotpro tags aliases runs without asking. When the run does escalate, an Authorization section lists each role that needs sudo and the tasks that escalate before Ansible's BECOME password: prompt appears:
┏━━━━━━━━━━━━━━━━━━━┓
┃ Authorization ┃
┗━━━━━━━━━━━━━━━━━━━┛
git: Install Git, Install Git pre-commit
docker: Install Docker, Install Docker Compose
BECOME password:The scan fails open: anything it cannot prove statically (a templated include like the ansible role's dynamic role list, play-level become:, a role it cannot find on disk) keeps the prompt, so the worst case is a prompt you did not strictly need, never a run that dies mid-way on a privileged task. The prompt is also skipped when sudo already works without a password (NOPASSWD or a still-valid timestamp, see unix_sudo_timeout), for root, and on non-interactive runs (CI, containers, the apply timer). DOTPRO_NONINTERACTIVE=0 or passing -K forces the prompt; DOTPRO_NONINTERACTIVE=1 forces it off.
Inspect
| Command | Does |
|---|---|
dotpro doctor [--json] | Read-only environment health checks (toolchain, collection currency, inventory, repo cleanliness, module interpreter, output-callback availability, agents role ordering (only when the playbook includes the agents role, and config-only roles are exempt), $HOME XDG hygiene) |
dotpro vars [host] | Dump the merged vars for a host ("what will this machine get"), grouped by role (a boxed role header on a terminal, a # role comment when piped); syntax-coloured through yq on a terminal when installed (the yq role), plain when piped |
dotpro roles [--tags T] | List the roles that run on this host; --tags lists only the roles those tags select (e.g. dotpro roles --tags ansible,dotpro shows what dotpro upgrade re-applies) |
dotpro tags [name…] | List the playbook's tags, or apply only the given ones |
dotpro dotfiles [status] | List each managed dotfile and its link state (ok / broken / file / missing); status previews drift, for the dotfiles role |
dotpro history [filter] | Apply history (what ran when) |
dotpro versions [tool] | Version history of managed tools (reads ~/.versions.d/history.log) |
dotpro logs [n|-f|--all] | Surface the Ansible run log; bare logs tails the last 200 lines (pipes cleanly, lands on the failure), logs <n> tails n lines, -f follows, --all opens the pager positioned at the end |
Author
| Command | Does |
|---|---|
dotpro capture <app> | Suggest vars overrides from a live config (claude / git / vscode), secrets redacted |
dotpro edit [file] | Open $EDITOR on the playbook or a given file |
dotpro dir | Print the repo dir; use it as cd "$(dotpro dir)" |
dotpro completion [bash|zsh] | Print shell completion |
Output modes
dotpro apply --output <mode> maps a friendly name to an env bundle (callback + verbosity + color) exported before the play, so it overrides the repo ansible.cfg default for that run:
--output | Maps to |
|---|---|
progress | dotfilespro.roles.dotpro_progress — a live role list and progress bar (the interactive default) |
plain | the built-in default callback |
compact | community.general.unixy (condensed, human) |
diff | default with YAML result format (readable multi-line diffs) |
json | the json callback (implies NO_COLOR; for scripting) |
Absent an explicit --output, an interactive terminal gets progress (a pinned role list with a task/ETA bar, sized from the previous run's task count) and a non-TTY run auto-downgrades to plain, colorless output for clean logs and pipelines. dotpro --debug <command> prints the raw ansible-playbook / ansible-galaxy invocation each subcommand shells out to.
Variables
| Variable | Default | Description |
|---|---|---|
dotpro_path | ~/.dotfiles | Path to your personal dotfiles repo |
dotpro_playbook | playbook.yaml | Playbook run by dotpro apply |
dotpro_requirements | requirements.yml | Requirements file listed by dotpro requirements and installed by dotpro requirements --upgrade |
dotpro_limit_host | true | Limit dotpro apply to the current host; set false for single-host setups |
dotpro_autopush | false | Commit and push the repo after a successful dotpro apply; override per run with --push / --no-push |
dotpro_install_source | package_manager | Collection-wide install source: package_manager prefers apt/Homebrew (falling back to the upstream release where unavailable), latest always takes the newest upstream release. Override per role (<role>_install_source) or per tool (<role>_<tool>_install_source); only tools with both sources honor it |
dotpro_header | dotfiles.pro | The branded box header printed before interactive commands; set your own text to rebrand, or "" to turn it off (exported as DOTPRO_HEADER) |
dotpro_deprecated_vars | see defaults | Map of renamed/removed variable → pointer at its replacement |
Alongside the install source, each release-installed tool takes a <role>_<tool>_version variable (e.g. kubernetes_kubectl_version, agents_sd_version), defaulting to latest. Pin it to an exact version and the tool installs (or up/down-grades) to precisely that on every apply; latest only checks the remote on first install or an --upgrade run. apt/apt-repo-managed tools (ansible, docker, terraform) track their repository instead and take no such pin.
Deprecated variables
When a user-facing variable is renamed, setting the old name silently does nothing, which is easy to miss. dotpro_deprecated_vars is a registry of old name → guidance; the role prints a non-fatal notice for each one a host still sets, so the rename surfaces on the next apply instead of quietly dropping config:
DEPRECATED variable 'ansible_roles' is set — renamed to roles_extra. It is no longer
read; update your vars.The dict merges (hash_behaviour = merge), so a personal repo or company collection can register its own deprecations on top of the collection's. When you rename a variable in your own roles, add the old name here and remove its old default, so the check fires only when a user actually set it.
Examples
Point the CLI at a repo and playbook of your own:
dotpro_path: ~/projects/dotfiles # default: ~/.dotfiles
dotpro_playbook: site.yaml # default: playbook.yaml
dotpro_limit_host: false # default: true; set false to apply everything on a single-host setupAliases
The apply engine and every day-2 command live in the binary, which sits on your PATH, so the role ships no aliases by default — invoke it directly as dotpro apply, dotpro doctor, and so on. Add your own to dotpro_aliases if you want a shorthand.
Bootstrap
The first bootstrap is still the installer (install.sh) or a manual ansible-playbook run; the installer seeds the binary, then dotpro apply converges the host (or DOTPRO_APPLY=1 applies during the bootstrap). Every run after that is dotpro apply.