Tuckr - Stow alternative with symlink checking
16 points by RaphGL
16 points by RaphGL
I've been using Stow for a few years now. At the time (2020) Stow had a bug where it would just fail with a cryptic error and the maintainer didn't have time to fix it, the bug was there for 2 years or so. So I got fed up and decided to try and fix it but I didn't know perl nor did I want to learn it, so I decided to rewrite Stow and fix the issue. To fix it I decided that I track all symlinks and give users a nice way to see what was going on. So the entire project was based on having a nice tuckr status command.
Over time the project expanded, people asked for more things that it couldn't do before. But I didn't like nor want to have configuration files for your configurations. So I've borrowed a bit from Go and Odin's suffix idiom and some other tools here and there. I feel like the current known bugs are no longer a big deal and for most users, the tool just works.
I've been using Tuckr to manage my own dotfiles for 5 years now without any major hickups, so I'm posting it here now to get some feedback. You can check the 0.13.0 milestone to see what are the known bugs, you're unlikely to run into them unless you're trying to be too smart.
Why bother with symlinks when you can just track the files in-place?
I’ll have to try this, the simplicity is a game changer. I could never fully get into stow/chezmoi and the like, a git repo with just the files always seemed better.
I tried a similar bare git method like that a few years back, but there were enough differences from normal git usage that I felt like it didn't give me the simplicity I'd hoped for.
I still ended up with a mix of less-common git commands, special aliases/functions/scripts/etc. Not that many, but enough that I wondered if I should have just ditched it for a tool. YMMV.
I don't get along well with chezmoi either and finally figured out that it just doesn't work unless you prefix every edit with chezmoi edit --apply ~/.config/fish/fish.config. I was using the tool for ages without that flag and wondering why I got stuck in eternal update loops.
I tried the git trick in the article yesterday, I really like it. There's a tiny inconvenience where files you want to track need to be added with their absolute path. Also in a new machine/server the showUntrackedFiles no won't be set so you'll see files you don't want to on your status, you have to manually do it in each machine. But that's it!
You can track configuration drift with a simple yourdotalias diff. You can see what you're tracking with yourdotalias ls-files. If you edited files in one machine and then pull them in another you'll get a conflict like any git repo, which is actually pretty good as it stops you from clobbering stuff.
If you're good with git you can get out of any complications. I'm ashamed I went almost two decades using git without thinking of this...
Yeah I've known about this for ages now. The git bare method is basically this. I just don't like it as much. It's just a personal preference/bias.
Just some useless feedback:
I read multiple paragraphs and I have no idea of what this does. It creates symlinks? Why can't I have a simple script do that?
I was interested because dotfile management is not the most pleasant activity currently, but I was too confused so opted to just close the page.
I remember reading about Stow a long time ago and also being confused and leaving.
I think Stow (and by extension, Tuckr) has enough smarts that it's better than a script, but YMMV for dotfiles; I've never used Stow for that, and that scenario would only be using a fraction of its functionality, unless you broke your dotfiles up into multiple separate trees. (Which might be handy, if you've modularized them.) What I have used it for is managing the /usr/local tree when I was forced found it expedient to install a few things on a machine from source.
Instead of just letting all the files blend together, I'd set the prefix to be /usr/local as normal, but then the DESTDIR would be something like /usr/local/stow/packagename-1.2.3. Then Stow could be used to build a symlink farm that made it look like /usr/local had all the files from all the packages installed normally.
The big advantage to that approach is that you can then remove a package if you are done with it, or want to replace it with a new version, without the risk of leaving loose files behind. Stow is also smart enough to let you know if you have conflicts, clean up dangling links, and other maintenance tasks. Also, it can also do some clever things with making sure that a minimal number of symlinks are used, though I think that's mostly an optimization.
Came here to say this. I use Stow all the time, but for managing /usr/local/ via packages in /usr/local/stow/.  In fact, the documentation for Stow mainly describes it in terms of this use case.  I've never actually used it for my dot files.
I imagine that someone got the idea that it would work just as well for managing ~/.local where you have the same problem of also wanting to be able to remove and replace package.  And then if it works for that dot directory, why not other dot files.  But really, I see managing dot files as something of an off-label use of Stow for which it was never really intended.
Yes it just creates symlinks. I used to have a script that did everything for me but it still used stow underneath.
I recommend checking the wiki on the github repo, I go over things more deeply there.
The only thing u need to understand is that u separate ur dotfiles by directories, each directory is the name of the program those dotfiles are for. You treat these directories as if it was their own $HOME. Tuckr will then just symlink them all to home and you can then check the status to see if it's symlinked or not or if it's conflicting with an existing symlink, then u can choose to override it or not.
The core idea of the program is rather simple. If there's anything u struggle with understanding and the wiki doesn't make it clear, please create an issue and I'll see how I can improve the documentation.
This allows to group the configs as logical units (which is already much better than most other dotfile managers), but it still duplicates the target directory structure in the source directory:
Configs
├── tmux
│   └── .config
│       └── tmux
│           └── tmux.conf
└── zsh
    └── .zshrc
The .config/tmux path feels a little unnecessary, especially since this is replicated for every other grouped config file, so you end up with lots of the same nested paths.
When I wrote my symlinker 10 years ago (this was before there were 100s of the same dotfile managers), I opted for a declarative config mapping instead. The benefit is that you can adopt any source directory structure you like, including a flat map of configs grouped by logical unit:
dotfiles
├── tmux
│   └── tmux.conf
└── zsh
    └── .zshrc
This comes at the low initial cost of adding a declarative mapping from source to target, but saves the long-term headache of having to traverse the $XDG_CONFIG_HOME structure every time you edit the file.
tmux/tmux.conf -> ~/.config/tmux/tmux.conf
zsh/.zshrc -> ~/.zshrc
The symlinker itself could be a shell one-liner, but in my case grew to over 100 lines due to feature creep, including writing a compatible Ansible implementation that I don't even use anymore, because Ansible is way too slow.
You're adding a config file. My project's stated goal is "to avoid using a config file for your configs as much as possible". I've also thought about this before but I really don't like it because then you run into the trap of adding more options and people will ask for more as well. I decided to write mine not just because stow broke but also because everything else out there is too happy to introduce more configs and add more options, it just becomes overwhelming.
My solution was to add a tuckr push and tuckr pop command so you can just push and pop things into your configs by just giving their relative/absolute paths. It still stores them like you said but at least it removes the burden of having to manually create all subdirectories.
For sure, I understand your approach, but I wouldn't really classify the mapping between source and target as a configuration file in that sense: It doesn't even have the structure to add more configuration.
I didn't like [...] configuration files for your configurations
But TBH I don't really follow your thought what's supposed to be weird about configuration for configurations.
you run into the trap of adding more options and people will ask for more as well
Like it or not, users will ask for more options and features regardless of how strong your opinion against meta configuration is. If you add them as a configuration file or as flags to your command invocations doesn't make that much difference in the grand scheme of things.
But TBH I don't really follow your thought what's supposed to be weird about configuration for configurations.
It's not that configuration is bad. It's just that I didn't want to learn how to write configurations for my configurations. I looked at all the dotfile managers available out there and they all felt too complicated for me. I just don't care about 90% of the features they offer. All I care about is being able to easily deploy my dotfiles and run some setup/cleanup scripts.
users will ask for more options and features regardless of how strong your opinion against meta configuration is
Yeah, it's unavoidable. But I'm not really afraid of rejecting features that are asking for something that's outside the scope of the project. I have tried to make it so you could script things using Tuckr itself, so even if I don't support them, you could probably write a script that easily adds it.
For example, people have asked for a file system watch feature, but I haven't implemented it, I probably will reject it, not 100% sure yet. But you could just create a script that watches the file system and gives you the when files are moved, you could then use tuckr groupis to tell you what group it is from and then you can use tuckr pop to remove the old path and tuckr push to add the new one.
The main thing that I dont like just from reading the description is the use of 'blessed' dotfiles path, making the user put their dotfiles under a specific path. Its not a massive deal, but enough that im not going to switch to it from ordinary stow.
If you don't like it, you can change it: https://github.com/RaphGL/Tuckr/wiki#overriding-the-default-behavior
That's just the default location tuckr will use because that's where those OSes want you to put them and I'd rather follow standards than be in the wild west.
Ah, nice! Might want to point that out in the readme, BTW. Id agree, the only reason I dont put my dotfiles dir there is that my dotfiles repo is something I actually edit semi-frequently, so it is somewhat ironically, not a dotfile, just ~/dotfiles.
If you want you can name it as ~/.dotfiles and tuckr would find it. If you want to take the dot in the name then you would have to use the env variable.
I use this
jj() { command jj $([[ "$PWD" == "$HOME" ]] && echo "--repository .dotfiles") "$@"; }
Keep dotfiles in .dotfiles and symlink as needed. Works like a charm and can adapt to git with the -C argument.
The following part of the comment above is ambiguous to me: "Keep dotfiles in .dotfiles and symlink as needed." ... Are you saying the jj alias above handles the symlinks? I wouldn't expect that to happen. (I'm not an active jj user.)