various suggestions for mr
Adam Spiers
vcs-home at adamspiers.org
Fri Oct 28 13:24:02 CEST 2011
Hi all,
I've been tracking my dot files and related stuff since around 1999,
and was very excited to discover this mailing list two years ago.
Since then I've only been able to lurk, but finally have a bit of
spare time to participate.
Since 2005 I have been using my own (as yet unpublished) Perl suite
called "cfgctl" to manage my dot files, which serves a similar to
purpose to mr - applying operations such as "checkout", "update"
etc. to multiple repositories using multiple VCS backends. However
its shortcomings have increasingly irked me, and just when I was being
to resign myself to a complete rewrite, I noticed that mr had already
stolen most of my thunder :-)
The main differences between mr and cfgctl are:
- cfgctl's repository meta-data is written in pure Perl (and then
require'd at run-time) rather than one or more .INI-style
.mrconfig files. When I originally wrote it, needing
functionality akin to mr's "skip" parameter I naively thought that
this would give me maximum flexibility, and I mistakenly
underestimated the value of using a DSL.
- cfgctl only has a fixup hook (i.e. post-checkout/update), and no
hooks for any other actions.
- cfgctl is not nearly as extensible. It doesn't support writing
arbitrary new actions either globally or per repo.
- With hindsight cfgctl should have been made more shell-oriented in
general, because the kinds of tasks required for managing dot
files/repos, building/install packages etc. are much more easily
achieved in shell than in Perl.
- Adding support for a new VCS backend is more work in cfgctl,
because it requires writing a new Perl subclass of Cfg::Pkg::Base.
- cfgctl is harder to type than 'mr'.
So far mr is clearly winning :-) However, cfgctl does have one or two
tricks up its sleeve:
- Config modules / packages / repositories / whatever you want to
call them are indexed by name within a unique namespace, rather
than by directory path, and packages are grouped together into
sections. This allows you to easily run any of the actions on:
- all the packages (just like running mr from $HOME)
- a single package, just by specifying its name without needing
to know where it lives, e.g. "cfgctl --update zsh" would
update just the zsh repository
- a section (i.e. group of packages) just by specifying its name
(e.g. "CLI" or "mail" or "Xorg") without needing to know where
anything lives, e.g. "cfgctl --pull Xorg" would update all
repos containing config relating to my Xorg (previously X11)
environment
- any packages matching a regular expression e.g.
"cfgctl --update /emacs/"
- cfgctl integrates out of the box with GNU stow. Based on this
list's frequency of discussion on "fake bare" / detached git
repos, it seems that most people here have an aversion to the
symlink approach to managing dot files in $HOME. This surprises
me as I have been using them very successfully for 12 years,
although I will leave that debate for another thread.
- cfgctl's internals have a reasonable amount of pod / comments.
mr's code, whilst mostly self-explanatory at the high-level, does
have a few very long subroutines which I feel would gain clarity
by being refactored into some appropriately-named smaller subs.
All in all, I feel that mr has a better design than cfgctl, and has
greater longevity. So last night I spent an hour or two doing a quick
proof of concept, to see whether I could extend mr to implement the
functionality I require, in particular the integration with GNU stow.
I'm pleased to say that so far it's looking very promising :-)
This is pretty much all that's needed:
--------- 8< --------- 8< --------- 8< --------- 8< --------- 8< ---------
[DEFAULT]
lib =
STOW_DIR=$HOME/.cfg
STOW_TARGET=$HOME
MR_NAME="`basename $MR_REPO`"
#
set_stow_common_opts () {
STOW_PKG_PATH="$STOW_DIR/$MR_NAME"
stow_common_opts="-t $STOW_TARGET -d $STOW_DIR"
}
#
install () {
set_stow_common_opts
ensure_symlink_exists "$STOW_PKG_PATH" "${MR_REPO%/}"
}
#
ensure_symlink_exists () {
[ $# = 2 ] || error "CONFIG BUG: Usage: ensure_symlink_exists
SYMLINK TARGET"
symlink="$1"
required_target="$2"
if [ -L "$symlink" ]; then
actual_target="`readlink $symlink`"
if [ "$actual_target" = "$required_target" ]; then
return
else
error "Symlink $symlink already points to
$actual_target, cannot point to $required_target; aborting."
fi
fi
[ -e "$symlink" ] && error "Cannot create symlink $symlink -
already exists; aborting."
ln -s "$required_target" "$symlink"
}
#
stow () {
set_stow_common_opts
command stow $stow_common_opts "$MR_NAME"
}
restow () {
set_stow_common_opts
command stow -R -p $stow_common_opts "$MR_NAME"
}
unstow () {
set_stow_common_opts
command stow -D -p $stow_common_opts "$MR_NAME"
}
post_checkout = install && stow
post_update = install && restow
unstow = unstow
--------- 8< --------- 8< --------- 8< --------- 8< --------- 8< ---------
However, I am already beginning to miss the lack of a native namespace
for repo identifiers. For example, I would far rather type
mr update zsh emacs
than
mr -c ~/.config/mr/config.d/zsh update
mr -c ~/.config/mr/config.d/emacs update
If CLI parameters following the action verb are already reserved for
being passed on to the underlying VCS backend, then something like
this might be appropriate:
mr -f zsh,emacs update
The identifier could be declared simply in the config via a special
'name' parameter. The following example shows one of the situations
in which you might need the name to differ from the basename of the
repository path ($MR_REPO).
--------- 8< --------- 8< --------- 8< --------- 8< --------- 8< ---------
[$HOME/.git-repos/zsh]
name = zsh.git
[$HOME/.cvs-working-dirs/zsh]
name = zsh.cvs
--------- 8< --------- 8< --------- 8< --------- 8< --------- 8< ---------
Similar functionality for grouping multiple repos together could be achieved
by something like:
--------- 8< --------- 8< --------- 8< --------- 8< --------- 8< ---------
[$HOME/.config/mr/groups.d/CLI]
checkout = true
include = cat ~/.config/mr/config.d/{zsh,emacs}
--------- 8< --------- 8< --------- 8< --------- 8< --------- 8< ---------
invoked via
mr -c ~/.config/mr/groups.d/CLI update
although that's kind of ugly. Native support would be nicer, e.g.
mr -g CLI update
I'd love to hear anyone's thoughts on these suggestions!
Cheers,
Adam
More information about the vcs-home
mailing list