dotcli: fix help falling through to git.

This commit is contained in:
2026-07-04 01:17:04 -04:00
parent 6ad0f109df
commit 80581f5029
6 changed files with 150 additions and 44 deletions

View File

@@ -3,14 +3,24 @@
## Architecture ## Architecture
`dot` is defined in one file: `~/.config/fish/functions/dot.fish`. It holds `dot` is defined in one file: `~/.config/fish/functions/dot.fish`. It holds
two functions: three functions:
- `dot` (`--wraps=git`) — dispatches `init` and any file found under - `dot` (`--wraps=git`) — dispatches `init`, `help`, and any file found under
`~/.config/dot/commands/`, otherwise forwards everything to `~/.config/dot/commands/`, otherwise forwards everything to
`git --git-dir=~/.dotfiles --work-tree=$HOME $argv` (full passthrough). `git --git-dir=~/.dotfiles --work-tree=$HOME $argv` (full passthrough).
- `__dot_init` — the bootstrap logic, inlined in the same file rather than - `__dot_init` — the bootstrap logic, inlined in the same file rather than
autoloaded separately, because it's the one subcommand that must work autoloaded separately, because it's the one subcommand that must work
before the dotfiles repo has ever been cloned onto a machine. before the dotfiles repo has ever been cloned onto a machine.
- `__dot_help` — prints usage: the built-in commands plus whatever is
currently found under `~/.config/dot/commands/`, generated by globbing that
directory rather than a hardcoded list, so it can't drift from reality.
`__dot_help`'s glob over `~/.config/dot/commands/*.fish` is duplicated in
`~/.config/fish/completions/dot.fish`'s `__dot_custom_subcommands` rather than
shared: fish only autoloads a function from a file named after that function,
so a helper defined inside `dot.fish` would be undefined if tab-completion
ran before `dot` had ever been sourced in the session. Keep both copies in
sync when the listing logic changes.
`dot init`: `dot init`:
@@ -33,9 +43,9 @@ of `dot` itself.
1. Create `~/.config/dot/commands/<name>.fish` defining a `_dot_<name>` 1. Create `~/.config/dot/commands/<name>.fish` defining a `_dot_<name>`
function. function.
2. Confirm `dot <name>` dispatches to it. No other wiring is needed — 2. Confirm `dot <name>` dispatches to it. No other wiring is needed —
`~/.config/fish/completions/dot.fish` discovers new command files by `~/.config/fish/completions/dot.fish` and `__dot_help` both discover new
globbing that directory, and `--wraps=git` still covers raw git command files by globbing that directory, and `--wraps=git` still covers
subcommands. raw git subcommands.
3. Add a case to `~/.config/dot/tests/dot.fish` covering it and run 3. Add a case to `~/.config/dot/tests/dot.fish` covering it and run
`fishtape ~/.config/dot/tests/dot.fish` until it passes. `fishtape ~/.config/dot/tests/dot.fish` until it passes.

View File

@@ -70,3 +70,22 @@ end" >$HOME/.config/dot/commands/mark.fish
dot mark >/dev/null 2>&1 dot mark >/dev/null 2>&1
@test "dispatches to a command file under ~/.config/dot/commands/" (cat $marker) = marked @test "dispatches to a command file under ~/.config/dot/commands/" (cat $marker) = marked
# --- dot help ---
set -gx HOME (mktemp -d)
dot init --url $remote >/dev/null 2>&1
set -l help_output (dot help)
set -l help_status $status
@test "dot help succeeds" $help_status -eq 0
@test "dot help lists init" (string match -q '*init*' -- $help_output; echo $status) -eq 0
@test "dot help mentions git passthrough" (string match -q '*git*' -- $help_output; echo $status) -eq 0
mkdir -p $HOME/.config/dot/commands
echo "function _dot_mark
echo marked
end" >$HOME/.config/dot/commands/mark.fish
set -l help_with_custom (dot help)
@test "dot help lists custom commands found under ~/.config/dot/commands/" (string match -q '*mark*' -- $help_with_custom; echo $status) -eq 0

View File

@@ -1,5 +1,6 @@
function __dot_custom_subcommands function __dot_custom_subcommands
echo init echo init
echo help
path basename $HOME/.config/dot/commands/*.fish 2>/dev/null | path change-extension '' path basename $HOME/.config/dot/commands/*.fish 2>/dev/null | path change-extension ''
end end

View File

@@ -0,0 +1,80 @@
# This file contains fish universal variable definitions.
# VERSION: 3.0
SETUVAR __done_min_cmd_duration:10000
SETUVAR __done_notification_urgency_level:low
SETUVAR _fisher_jorgebucaran_2F_fishtape_files:\x7e/\x2econfig/fish/functions/fishtape\x2efish\x1e\x7e/\x2econfig/fish/completions/fishtape\x2efish
SETUVAR _fisher_plugins:jorgebucaran/fishtape
SETUVAR _fisher_upgraded_to_4_4:\x1d
SETUVAR pure_begin_prompt_with_current_directory:true
SETUVAR pure_check_for_new_release:false
SETUVAR pure_color_at_sign:pure_color_mute
SETUVAR pure_color_aws_profile:pure_color_warning
SETUVAR pure_color_command_duration:pure_color_warning
SETUVAR pure_color_current_directory:pure_color_primary
SETUVAR pure_color_danger:red
SETUVAR pure_color_dark:black
SETUVAR pure_color_exit_status:pure_color_danger
SETUVAR pure_color_git_branch:pure_color_mute
SETUVAR pure_color_git_dirty:pure_color_mute
SETUVAR pure_color_git_stash:pure_color_info
SETUVAR pure_color_git_unpulled_commits:pure_color_info
SETUVAR pure_color_git_unpushed_commits:pure_color_info
SETUVAR pure_color_hostname:pure_color_mute
SETUVAR pure_color_info:cyan
SETUVAR pure_color_jobs:pure_color_normal
SETUVAR pure_color_k8s_context:pure_color_success
SETUVAR pure_color_k8s_namespace:pure_color_primary
SETUVAR pure_color_k8s_prefix:pure_color_info
SETUVAR pure_color_light:white
SETUVAR pure_color_mute:brblack
SETUVAR pure_color_nixdevshell_prefix:pure_color_info
SETUVAR pure_color_nixdevshell_symbol:pure_color_mute
SETUVAR pure_color_normal:normal
SETUVAR pure_color_prefix_root_prompt:pure_color_danger
SETUVAR pure_color_primary:blue
SETUVAR pure_color_prompt_on_error:pure_color_danger
SETUVAR pure_color_prompt_on_success:pure_color_success
SETUVAR pure_color_success:magenta
SETUVAR pure_color_system_time:pure_color_mute
SETUVAR pure_color_username_normal:pure_color_mute
SETUVAR pure_color_username_root:pure_color_light
SETUVAR pure_color_virtualenv:pure_color_mute
SETUVAR pure_color_warning:yellow
SETUVAR pure_convert_exit_status_to_signal:false
SETUVAR pure_enable_aws_profile:true
SETUVAR pure_enable_container_detection:true
SETUVAR pure_enable_git:true
SETUVAR pure_enable_k8s:false
SETUVAR pure_enable_nixdevshell:false
SETUVAR pure_enable_single_line_prompt:false
SETUVAR pure_enable_virtualenv:true
SETUVAR pure_reverse_prompt_symbol_in_vimode:true
SETUVAR pure_separate_prompt_on_error:false
SETUVAR pure_shorten_prompt_current_directory_length:0
SETUVAR pure_shorten_window_title_current_directory_length:0
SETUVAR pure_show_exit_status:false
SETUVAR pure_show_jobs:false
SETUVAR pure_show_numbered_git_indicator:false
SETUVAR pure_show_prefix_root_prompt:false
SETUVAR pure_show_subsecond_command_duration:false
SETUVAR pure_show_system_time:false
SETUVAR pure_symbol_aws_profile_prefix:
SETUVAR pure_symbol_container_prefix:
SETUVAR pure_symbol_exit_status_prefix:\x7c
SETUVAR pure_symbol_exit_status_separator:\x7c
SETUVAR pure_symbol_git_dirty:\x2a
SETUVAR pure_symbol_git_stash:\u2261
SETUVAR pure_symbol_git_unpulled_commits:\u21e3
SETUVAR pure_symbol_git_unpushed_commits:\u21e1
SETUVAR pure_symbol_k8s_prefix:\u2638
SETUVAR pure_symbol_nixdevshell_prefix:\u2744\ufe0f
SETUVAR pure_symbol_prefix_root_prompt:\x23
SETUVAR pure_symbol_prompt:\u276f
SETUVAR pure_symbol_reverse_prompt:\u276e
SETUVAR pure_symbol_ssh_prefix:
SETUVAR pure_symbol_title_bar_separator:\x2d
SETUVAR pure_symbol_virtualenv_prefix:
SETUVAR pure_system_time_format:\x2b\x25T
SETUVAR pure_threshold_command_duration:5
SETUVAR pure_truncate_prompt_current_directory_keeps:\x2d1
SETUVAR pure_truncate_window_title_current_directory_keeps:\x2d1

View File

@@ -7,6 +7,11 @@ function dot --wraps=git --description 'Manage dotfiles via a bare repo checked
return $status return $status
end end
if test "$argv[1]" = help
__dot_help
return $status
end
set -l commands_dir $HOME/.config/dot/commands set -l commands_dir $HOME/.config/dot/commands
set -l command_file $commands_dir/$argv[1].fish set -l command_file $commands_dir/$argv[1].fish
@@ -87,3 +92,23 @@ function __dot_init
echo "dot init: bootstrapped $dotfiles_dir from $url" echo "dot init: bootstrapped $dotfiles_dir from $url"
end end
# The custom-subcommand glob is duplicated (not shared with
# completions/dot.fish) because fish only autoloads a function from a file
# named after that function; a shared helper would go undefined if `dot help`
# ran in a completion context before `dot` itself had ever been sourced.
function __dot_help
echo "dot: manage dotfiles via a bare repo checked out over \$HOME"
echo
echo "Commands:"
echo " init bootstrap the dotfiles repo on a new machine"
echo " help show this message"
for f in $HOME/.config/dot/commands/*.fish
test -e $f; or continue
echo " "(path basename $f | path change-extension '')
end
echo
echo "Any other command is passed through to git (dot status, dot add, dot commit, dot push, ...)."
end

View File

@@ -1,27 +1,10 @@
# dotfiles # dotfiles
Managed as a bare git repo checked out over `$HOME`. Not cloned in the usual Dotfiles managed as a bare git repo checked out over `$HOME`, for machines
sense — `git --git-dir=~/.dotfiles --work-tree=$HOME` treats `$HOME` itself as running CachyOS with KDE Plasma.
the working tree.
## Everyday use
A fish function named `dot` wraps that invocation:
```fish
dot status
dot add .bashrc
dot commit -m 'update bashrc'
dot push
```
Any git subcommand works — `dot` forwards whatever you type straight to git.
## Bootstrapping a new machine ## Bootstrapping a new machine
Before `dot` exists there's nothing to autoload it from, so the very first
step is fetching that one file by hand:
```sh ```sh
mkdir -p ~/.config/fish/functions mkdir -p ~/.config/fish/functions
curl -fsSL https://git.alexion.dev/alexion/dotfiles/raw/branch/main/.config/fish/functions/dot.fish \ curl -fsSL https://git.alexion.dev/alexion/dotfiles/raw/branch/main/.config/fish/functions/dot.fish \
@@ -29,25 +12,13 @@ curl -fsSL https://git.alexion.dev/alexion/dotfiles/raw/branch/main/.config/fish
fish -c 'dot init' fish -c 'dot init'
``` ```
`dot init`: ## Commands
- clones the bare repo to `~/.dotfiles` | Command | Description |
- backs up any pre-existing files that would be overwritten by checkout into | ----------- | ----------------------------------------------- |
`~/.dotfiles-backup/<timestamp>/` | `dot help` | Lists available commands. |
- checks out the tracked files onto `$HOME` | `dot init` | Bootstraps the dotfiles repo on a new machine. |
- sets `status.showUntrackedFiles=no` so `dot status` doesn't list all of | `dot <git>` | Everything else is passed to `git`. |
`$HOME`
It refuses to run if `~/.dotfiles` already exists, and it never falls back to See [DOT-CLI.md](../.claude/skills/dotfiles/DOT-CLI.md) for the `dot` tool's
creating a fresh empty repo if the clone fails. internal architecture, bootstrap logic, subcommand dispatch, and test suite.
## Adding new subcommands
Beyond `init`, `dot` looks for `~/.config/dot/commands/<name>.fish`. Each file
should define a `_dot_<name>` function; `dot <name> args...` sources the file
and calls it. These files are deliberately *not* under
`~/.config/fish/functions/`, so they never become independently invokable
commands or clutter tab-completion outside of `dot` itself.
`~/.config/fish/completions/dot.fish` discovers them automatically by
scanning that directory, so a new command file gets tab-completion for free.