dotcli: add dot setup folders (XDG short-name migration)
Adds a dot setup subcommand family (idempotent, re-runnable machine-setup tasks) with a folders task that brings the 8 standard XDG user directories under a fixed short-name convention (Desktop -> .desktop, Documents -> doc, Downloads -> dwn, Music -> mus, Pictures -> pic, Videos -> vid, Templates/Public -> .ignoreme), hardcoded rather than read from ~/.config/user-dirs.dirs -- that file is a separate, manually tracked dotfile whose values can drift or go stale, and the migration must not depend on it being correct. The nested Pictures/Screenshots folder is renamed to pic/screenshots in the same pass. Content left behind in a legacy full-named folder by a fresh XDG-defaults install -- empty or not -- is merged into its short-named replacement. A same-named collision between a legacy folder and its target is never overwritten: it is skipped, reported, and the legacy folder is left in place rather than deleted while something in it could not be merged. --dry-run previews what would move/skip without touching the filesystem. xdg-user-dirs-update runs once afterward to notify running apps/portals.
This commit is contained in:
134
.config/dot/commands/setup/folders.fish
Normal file
134
.config/dot/commands/setup/folders.fish
Normal file
@@ -0,0 +1,134 @@
|
||||
function _dot_setup_folders_usage
|
||||
echo "usage: dot setup folders [--dry-run]
|
||||
|
||||
Brings the 8 standard XDG user directories under the project's fixed
|
||||
short-name convention (Desktop -> .desktop, Documents -> doc, Downloads ->
|
||||
dwn, Music -> mus, Pictures -> pic, Videos -> vid, Templates/Public ->
|
||||
.ignoreme). This mapping is fixed and does not depend on
|
||||
~/.config/user-dirs.dirs, which is a separate, manually tracked dotfile
|
||||
this command never reads or writes.
|
||||
|
||||
Content left behind in a legacy full-named folder (e.g. ~/Documents) by a
|
||||
fresh XDG-defaults install -- empty or not -- is merged into its short-named
|
||||
replacement. A nested Pictures/Screenshots folder is renamed to
|
||||
pic/screenshots as part of the same pass.
|
||||
|
||||
An entry that collides by name with something already in the short-named
|
||||
target is never overwritten: it's skipped, reported, and its legacy folder is
|
||||
left in place (not removed) even when everything else in it migrated.
|
||||
|
||||
--dry-run report what would move and what would be skipped as a
|
||||
collision, without changing anything on disk
|
||||
|
||||
Runs xdg-user-dirs-update once afterwards to notify running apps/portals
|
||||
(skipped under --dry-run)."
|
||||
end
|
||||
|
||||
function _dot_setup_folders
|
||||
if test "$argv[1]" = help
|
||||
_dot_setup_folders_usage
|
||||
return 0
|
||||
end
|
||||
|
||||
argparse 'dry-run' -- $argv
|
||||
or return 1
|
||||
|
||||
# Fixed legacy-name -> short-name mapping. Deliberately hardcoded rather
|
||||
# than read from ~/.config/user-dirs.dirs: that file is a separate,
|
||||
# manually tracked dotfile whose XDG_*_DIR values can drift or go stale
|
||||
# (or never get edited to the short names at all), and this command's
|
||||
# own migration logic must not depend on it being correct.
|
||||
set -l legacy_names Desktop Documents Downloads Music Pictures Videos Templates Public
|
||||
set -l target_names .desktop doc dwn mus pic vid .ignoreme .ignoreme
|
||||
|
||||
for i in (seq (count $legacy_names))
|
||||
set -l legacy_name $legacy_names[$i]
|
||||
set -l target_rel $target_names[$i]
|
||||
set -l target_path $HOME/$target_rel
|
||||
set -l legacy_path $HOME/$legacy_name
|
||||
|
||||
if not set -q _flag_dry_run
|
||||
mkdir -p $target_path
|
||||
end
|
||||
|
||||
if not test -d $legacy_path
|
||||
continue
|
||||
end
|
||||
|
||||
# Screenshots is always moved as one atomic unit (renamed to
|
||||
# lowercase screenshots), so its individual files must never appear
|
||||
# as separate move/report entries.
|
||||
set -l screenshots_path $legacy_path/Screenshots
|
||||
set -l top_level_entries (find $legacy_path -mindepth 1 -maxdepth 1 -not -name Screenshots)
|
||||
|
||||
# No-clobber: an entry whose name already exists in the target is
|
||||
# never moved over. It's collected here and reported below; its
|
||||
# legacy folder is left in place (not removed) if any collision
|
||||
# occurred, even though everything else in it migrated successfully.
|
||||
set -l collisions
|
||||
set -l movable_entries
|
||||
set -l screenshots_movable 0
|
||||
|
||||
if test -d $screenshots_path
|
||||
if test -e $target_path/screenshots
|
||||
set -a collisions $screenshots_path
|
||||
else
|
||||
set screenshots_movable 1
|
||||
end
|
||||
end
|
||||
|
||||
for entry in $top_level_entries
|
||||
if test -e $target_path/(path basename $entry)
|
||||
set -a collisions $entry
|
||||
else
|
||||
set -a movable_entries $entry
|
||||
end
|
||||
end
|
||||
|
||||
set -l movable_count (count $movable_entries)
|
||||
set -l entry_word entries
|
||||
test $movable_count -eq 1
|
||||
and set entry_word entry
|
||||
|
||||
if set -q _flag_dry_run
|
||||
if test $screenshots_movable -eq 1
|
||||
echo "dot setup folders: would move $screenshots_path to $target_path/screenshots"
|
||||
end
|
||||
if test $movable_count -gt 0
|
||||
echo "dot setup folders: would move $movable_count $entry_word from ~/$legacy_name to ~/$target_rel"
|
||||
end
|
||||
if test (count $collisions) -gt 0
|
||||
echo "dot setup folders: ~/$legacy_name has entries already present in ~/$target_rel, would skip (not overwritten):"
|
||||
for c in $collisions
|
||||
echo " $c"
|
||||
end
|
||||
echo "dot setup folders: ~/$legacy_name would remain in place due to the collision(s) above"
|
||||
end
|
||||
continue
|
||||
end
|
||||
|
||||
if test $screenshots_movable -eq 1
|
||||
mv -n $screenshots_path $target_path/screenshots
|
||||
echo "dot setup folders: moved $screenshots_path to $target_path/screenshots"
|
||||
end
|
||||
|
||||
if test $movable_count -gt 0
|
||||
mv -n $movable_entries $target_path/
|
||||
echo "dot setup folders: moved $movable_count $entry_word from ~/$legacy_name to ~/$target_rel"
|
||||
end
|
||||
|
||||
if test (count $collisions) -gt 0
|
||||
echo "dot setup folders: ~/$legacy_name has entries already present in ~/$target_rel, skipping (not overwritten):"
|
||||
for c in $collisions
|
||||
echo " $c"
|
||||
end
|
||||
echo "dot setup folders: leaving ~/$legacy_name in place due to the collision(s) above"
|
||||
else
|
||||
rmdir $legacy_path
|
||||
end
|
||||
end
|
||||
|
||||
if not set -q _flag_dry_run
|
||||
xdg-user-dirs-update
|
||||
end
|
||||
end
|
||||
35
.config/dot/commands/setup/setup.fish
Normal file
35
.config/dot/commands/setup/setup.fish
Normal file
@@ -0,0 +1,35 @@
|
||||
function _dot_setup_usage
|
||||
echo "usage: dot setup [<task>]
|
||||
|
||||
Tasks:
|
||||
folders bring the 8 standard XDG user directories under the short-name convention
|
||||
help show this message
|
||||
|
||||
Run 'dot setup <task> help' for details on a specific task.
|
||||
|
||||
With no task given, runs every setup task."
|
||||
end
|
||||
|
||||
function _dot_setup
|
||||
if test "$argv[1]" = help
|
||||
_dot_setup_usage
|
||||
return 0
|
||||
end
|
||||
|
||||
set -l helper_dir (status dirname)
|
||||
source $helper_dir/folders.fish
|
||||
|
||||
if test -z "$argv[1]"
|
||||
_dot_setup_folders
|
||||
return $status
|
||||
end
|
||||
|
||||
switch $argv[1]
|
||||
case folders
|
||||
_dot_setup_folders $argv[2..-1]
|
||||
return $status
|
||||
case '*'
|
||||
_dot_setup_usage
|
||||
return 1
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user