top | item 41453264

Better Dotfiles

153 points| dansalias | 1 year ago |iamdan.me

107 comments

order

dharbin|1 year ago

I’m a big fan of chezmoi (https://www.chezmoi.io/) which is a very capable dotfile manager. Chezmoi supports some useful advanced capabilities like work/home profiles and secrets manager integration.

kstrauser|1 year ago

Same for me. I'd done the same thing as the author with various methods like stow, symlink farms, etc. over the years. Chezmoi is good enough that I'm willing to let someone else handle maintaining all logic.

ashconnor|1 year ago

I migrated to chezmoi recently my only gripe is `chezmoi cd` opening in a new shell but `chezmoi git` usually is what I need. The age [0] integration is nice.

[0] - https://github.com/FiloSottile/age

samgranieri|1 year ago

Chezmoi is amazing. I dabbled with Stow, but Chezmoi is the way to go.

wyclif|1 year ago

Hey, I had never heard about chezmoi before reading your comment, but I just installed it. Took less than 10 minutes to set up from start to finish. I noticed that if you choose to use it to manage your `~/.ssh/config/`, by default chezmoi sets it up as `private_dot_ssh/` and so if your dotfiles are public it doesn't expose sensitive data like private key files such as `~/.ssh/id_rsa`. Smart!

Barrin92|1 year ago

also a big fan of it because the templating feature makes it very easy to handle dotfiles with different locations on multiple machines and if you use multiple operating systems. Really not that many tools around that have good windows support.

dolni|1 year ago

What happens when you need to link a file that does not support comments like that? For example, something which stores its config as plain JSON.

Or how about when you want to symlink an entire directory? For example something like neovim, considering that you may want to split config into separate files for organization. My neovim configuration has an "autoload setup" so any lua files inside the config directory are automatically required.

Lastly, this approach does not appear to support running commands. My dotfile install script ensures that tmux plugins are installed, the terminal font I use is available, and some other stuff that you need to invoke a command or script to achieve.

I like that the approach is simple, but I do not think it can support even relatively common use cases very well.

blueflow|1 year ago

I'm not using a dotfiles manager, i track my ~/.config in git and have a script that globs for ~/.config/*/dot.* files to create symlinks for them. Like ~/.config/bash/dot.bashrc . Works with directories.

dansalias|1 year ago

The JSON hiccup occured to me - I don't have any JSON dotfiles but I guess I'd store it as hjson (https://hjson.github.io/) and add a conversion step.

Symlinking a directory - admittedly didn't come up for my dotfiles, maybe `.ln` files with a similar format in dir roots.

Commands - yes, I still keep a set of shell scripts alongside my config files.

MrDresden|1 year ago

Sounds like a job for a personal Ansible playbook.

fmbb|1 year ago

Every time I see these tools for “managing” dotfiles, or something for “managing” notes I get a bit perplexed as to what the use is, but then I am reminded of and impressed with how different people’s brains are and how we work and think.

SoftTalker|1 year ago

I always think "managing dotfiles" is the path that leads to Windows Registry if you follow it too far.

heads|1 year ago

What I realized after 25 years is that configuration comes in three parts:

1/ the defaults, either built in or read from /etc;

2/ my defaults, included in each file (or with ssh, at the bottom) with that particular config’s native version of #include; and

3/ local specifics that are rarely if ever used anywhere else, or trivially short as to be copy-paste-able.

Almost everything I want to customize goes into (2) so I wrote a single Python function that manages a block at the top (or with ssh, at the bottom) of each config file:

  # BEGIN my foo stuff
  include = /my/repo/foo/config
  # END my foo stuff
That way foo starts out with (1) the system defaults; then adds (2) my personal foo defaults as defined in a working copy at /my/repo; (3) anything else I insert in the file after that which isn’t centrally managed and that’s ok.

I haven’t ever needed anything more complicated. I do not have any work specific configs that I need to gate. I no longer have to manage different configs based on whether I am using Debian, Debian (old), Debian (very old), SunOS (very very old), or AIX (very very very old) because those days are behind me.

If you do still need to manage slightly different but ethereally different configs on different hosts then I’m sorry to hear that. Rationalising my computing life so that I use the latest version of some Linux distribution everywhere has been very helpful!

b3lm0nt|1 year ago

I use Gary Bernhardt's (non)-method, quoted here:

IMO you don't need a special tool to manage your home directory / dotfiles. Git is the tool. Your home directory is a repo with a .git directory like any other repo. No other tools; no symlinks; nothing else. Commit what you want and gitignore the rest. I've done this since 2008.

Never had a problem with it.

JoshTriplett|1 year ago

That's what I do as well. Since you can .gitignore entire directories, that makes it easy. And one major advantage is having `git status` tell you if new things show up, so you can decide whether to track or ignore them (or change how/whether they're created in the first place).

sneak|1 year ago

yadm implements this model, but also allows for machine-specific branching and encrypted secrets (no secrets should be directly committed in git).

jackhalford|1 year ago

I just put dotfiles directly where they are and majr a .git directory in my $HOME. .gitignore everything and git add -f files when I need to. no symlinking or anything.

rfoo|1 year ago

For those who are convinced and want a slightly more convenient tool for doing this: try yadm.

mixmastamyk|1 year ago

If you want to avoid force:

    git config --local status.showUntrackedFiles no

dansalias|1 year ago

(OP) appreciate all the feedback. I tried stow and chezmoi, neither felt right to me. Evidently that puts me in a small minority!

On the off chance there's anyone else who sees this as worth exploring here's a <1min demo video - https://www.reddit.com/r/unixporn/comments/1f9u1xk/oc_better...

000ooo000|1 year ago

Some responders here almost seem offended you wouldn't use chezmoi. Kind of strange. I couldn't care less about someone using chezmoi/tool x - big deal - but I'm always interested to read how people approach automation of their workflows, the tradeoffs, any cool tricks they employed that I might not have heard of before. Are engineers discouraged from problem solving now? It's not like youre doing this on company time; you are allowed to do things for fun/interest's sake..

belthesar|1 year ago

I know a lot of folks are talking about what they do, or what dot file managers they use, but there's something to be said for building a workflow that works for you. Pretty clever setup!

dngray|1 year ago

> what they do, or what dot file managers they use, but there's something to be said for building a workflow that works for you

Like consuming time. If there is a tool which does what you need like chezmoi then you should use it, so that you don't have to spend maintaining something bespoke which consumes your time that could be better spent on other things.

andrewla|1 year ago

Especially given the fact that things are moving (too slowly) towards the XDG specification, my dotfiles repo is just my ~/.config directory, with a policy of basically ignoring everything except those things that I want to track.

So my .gitignore looks like

     /*
     !.gitignore
     !/dotfiles
     !/install_dotfiles.sh
     !/i3
     !/git
     ...
I then have a directory under .config, .config/dotfiles, where I have all of my unfixable dotfiles without the leading ., so to install them I have a script that just does `ln -snf ./$x ~/.$x` instead of messing with sed scripts.

This is both self-contained and allows me to manage both XDG-style config and traditional dotfiles.

setopt|1 year ago

I do exactly the same: I have a Git repo that is cloned into ~/.config and that covers most of the terminal apps that I use.

For the holdouts that don’t yet support that config directory, I have a short Makefile that sets up the required symlinks. So running “make” makes the links I’ll most likely need on a new server, while e.g. “make ssh” makes only the links required for that specific program.

Now that tmux supports ~/.config, and vim just added support as well, that Makefile is shrinking.

dimator|1 year ago

> I avoided superfluous dotfile managers and the limitations of the "version control $HOME" method by instead including first-line comments

ok...

> The files can be scanned and symlinks can be created with some awk magic:

wut.

this is just bespoke file management scheme. we've all been there, but there are better tools for this, as other comments have mentioned.

flkiwi|1 year ago

It's interesting and a fun hacking challenge for someone so inclined but it also seems slightly ... I don't know ... extraneous? But the community that needs dotfiles is diverse enough to support everything from Linux From Scratch devotees to chezmoi users, so that's cool.

frou_dh|1 year ago

Nice tricks, though in a sane world this would be metadata on the config files and not in-band data.

Of course, (ab)using comment syntax for structured machine directives is something many programming languages end up doing too. Here's a recent example: https://peps.python.org/pep-0723/#example. Surrounded with a pair of "# ///"? It must be something to do with Adidas.

ctur|1 year ago

But not all things you might do with a dotfile (or, more generally, per-user customization) are just replacing files. Things like cronjobs, brew installs, `defaults` in MacOS, etc. Viewing dotfile-based customization as strictly files to obliterate with pre-existing files is needlessly myopic.

For this broader problem, there are other more complete solutions that are more robust and flexible. Personally I like dotbot (https://github.com/anishathalye/dotbot) as a balance between power and simplicity, particularly when managing files across multiple OS homedirs (e.g. linux server, macos laptop).

stryan|1 year ago

I'm not suggesting you do this (and I certainly don't) but arguably you could still manage that with just files on Linux boxes:

1. Cronjobs replaced with systemd user timers

2. User packages (i.e. brew install or $HOME/bin) with systemd user services and distrobox manifest files

3. I don't think there's a `defaults` equivalent on Linux or at least not one that isn't file based (and thus manageable through dotfiles)

So maybe that's just an OSX concern.

skydhash|1 year ago

That's provisioning, not dotfiles management. My dotfiles only includes config files. I'd just use the package manager to install packages and I'd just use the relevant program to enable stuff. As I use stow, I just create different configurations for different OS if they differ too much. At most, a handful of scripts to customize my user account.

beepbooptheory|1 year ago

What are doing with a dotfile that needs to install a package?

nbobko|1 year ago

I just use one-liner git command that I put into a script

    git -c status.showUntrackedFiles=no --git-dir=$HOME/.dotfiles --work-tree=$HOME "$@"

jmarcher|1 year ago

+1 I've been using this for almost ten years. It's straightforward, easy to add files, and there are no symlinks to deal with.

When people ask me which tool I use to manage my dotfiles, I tell them about this little-known tool called `git`. ;)

Side note: I use an shell alias instead, but it's pretty much the same.

`alias home="git --work-tree=$HOME --git-dir=$HOME/.files.git"`

imglorp|1 year ago

OP's first sentence refers to limitations of "version control $HOME" ... What are the limitations?

vereis|1 year ago

or use home-manager via nix!

nixosbestos|1 year ago

Glad someone else already posted it. I understand if Nix is too much for folks, but it repeatedly is absolutely end-game for stuff like this. I have centralized, unified dotfiles, with all of the power of Nix to have one-off config flexed in. No extra templating or hacky interpolation.

mixmastamyk|1 year ago

I use git and and a hub site:

- Create repo in ~ with a few dot files, push

- Make a new system setup script, run on other machine:

    # install git; cd ~
    # I use a read-only token, optional:
    git clone "https://x-token-auth:${TOKEN}@bitbucket.org/you/dot_repo.git" dot_repo

    # move into $HOME
    cp -afv dot_repo/. .
    rm -rf dot_repo

    git config --local status.showUntrackedFiles no
Later on, if I want to write to the repo on this machine I run ssh-keygen, copy the public key to the remote and remove the token from .git/config and use ssh access instead.

I used to have everything excluded in .gitignore and force add files to the repo, but prefer status.showUntrackedFiles instead. There are still a few edge cases but they don't bother you everyday like having to force every operation.

Some of my scripts have things like, if dist fedora, do this, else debian, do this, else Mac, do that, when they differ.

joemi|1 year ago

I like the storing of the ln command in the file itself. I think I might implement something like that myself with my own dotfiles. I've been keeping the ln commands in a readme but I never really liked having to update that readme each time I add or remove a file to my dotfiles repo (and using a dotfile manager like those mentioned in other comments here does not appeal to me at all). This is a nice solution I hadn't considered. Now I can just replace the list of ln commands in my readme with a one-liner to run that'll show the ln commands from the files. (I don't actually want to automate the actual running of the commands, though.)

fsckboy|1 year ago

>fta: first-line comments of the form <comment delimiter> ln <link name> in files which needed to be symlinked.

if you're talking about symlinks, you ought to say "ln -s" shouldn't you?

(also, I think this is the tip of another iceberg: learn to say "link" when you mean "hardlink" because that's what a link is in unix filesystem. not saying "stamp out hardlink", I'm saying if you feel comfortable using it correctly yourself, it will help you help other people to disambiguate what they say and not get progressively sloppier. you may not like what these words mean, but that ship is well at sea)

kstrauser|1 year ago

I disagree with the latter. You're correct, but I think it overlooks human factors. If I can say or write one more syllable and remove all shadow of a doubt that I do, in fact, mean a hard link, then that's a syllable well spent. Until we can get 100% of people to immediately think of the hard link concept when someone says "link" in a Unix context, not spelling it out leaves too much room for misinterpretation.

immibis|1 year ago

Should we also say unlink instead of delete?

tristan957|1 year ago

What are the advantages of this over stow(1)?

skydhash|1 year ago

I was tempted to the the same thing as OP, but decided to go with stow, because its concept is more sound than whatever I may concoct. Currently, my dotfiles are packages (folder) which targets $HOME. I clone the repo to $HOME/dotfiles, then I can use `stow <package>` inside the folder to install each package easily. I treat it like dpkg|rpm|... but for my configuration. I'm trying to make each package its own thing, like mail, sway, i3, etc,...

williamcotton|1 year ago

I've had very little issue making a ~/dotfiles directory and just manually adding to my `~/.zshrc`:

  source ~/dotfiles/.zshrc
This is how I manage different environments. There are work/home/remote specific things in each domain's `~/.zshrc` files!

mattbillenstein|1 year ago

Clever, but doesn't work for directories.

I just have a small shell script which symlinks everything into my home dir - also serves as a reference as to what actually gets put where.

kkfx|1 year ago

I've tried something similar, and finally I've choose to avoid dotfiles, tangling them from org-mode notes. I keep notes, not their tangled output.

pvtmert|1 year ago

well, you can do even better by taking all this parsing and complexity out of the comment sections.

just add a makefile, add your mappings. done!

example:

    ~/.gitignore: ./dotfiles/gitignore
        ln -s $< $@
you can even add something like

    ~/.%: ./dotfiles/%:
        ln -s $< $@
and just have 1:1 mapping...

SubiculumCode|1 year ago

Curious. What config do you mess with aside from .bashrc and .profile (or i3 config if applicable)? Do people sync their.ssh?

dpc_01234|1 year ago

This is just a custom dotfile manager implemented in shell/awk. Nothing inherently "better" about it, AFAICT.

cbarrick|1 year ago

Big +1 to forgetting about dotfile managers, and scripting it out.

I just keep my dotfiles repo in the same tree structure as the home directory, and loop over the tree to create symlinks. Plus some miscellaneous commands to set some other things up.

https://github.com/cbarrick/dotfiles

quectophoton|1 year ago

> I just keep my dotfiles repo in the same tree structure as the home directory, and loop over the tree to create symlinks.

Depending on the use case, the `/etc/skel` directory (and equivalents depending on the distro/OS) might be useful. When creating a user, the files in $HOME are copied from such "skeleton" directory, and there's usually a way to tell that command to use a different skeleton directory.

So a different way (not better, just different) would be to have a directory already setup with symlinks and all, and use that directory as the skeleton when creating the user, so its $HOME gets created all ready with symlinks and all.

godelski|1 year ago

I think I'm doing my dotfiles wrong.

In my dotfiles I have a couple folders which includes rcfiles and configs. rcfiles includes things like {bash,zsh}rc, .vim{,rc}, tmux.conf, and so on, as well as folders like zsh that include things I import like aliases I have for specific linux machines (e.g. ubuntu has batcat instead of bat...) or osx. Then in config I have folders that contains all the things I would have under ~/.config (starship.toml, ipython_config.py, wezterm/<only lunatics have a single config if you have more than 50 total lines>, and so on. Then I just

  $ find "${DOTFILES_DIR%/}"/rc_files ! -name "README.md" ! -name "*root" -depth 1 -exec bash -c  'ln -sf "${0}" "${HOME%/}"/."${0##*/}"' {} \;
  $ ln -sf "${DOTFILES_DIR5/}"/configs/* "${HOME%/}/.configs/"
I mean I have other folders too like scripts, skels, templates, systemd configs, notes (notes in dotfiles is underappreciated!), and so on. What are you all using these managers for? Are they replacements for bash scripting? And also, find is super powerful and I think under appreciated. It really is worth learning. If you jump into the deepend I think you can get good at it in an afternoon.

Quekid5|1 year ago

> I think I'm doing my dotfiles wrong.

The problem isn't you. It's the way Unix-inspired systems do things that are entirely wrong. It's an insane mess of all of the things you mentioned because all the programs could do whatever they wanted. Bad conventions emerged and were propagated throughout the ecosystem.

Here's a fun exercise: try to change your home directory's physical location on a *nix system and see if anything works afterwards. It won't because every config thinks your files are in /home/me while you've changed your user name/home directory to /home/new-me. Windows (eventually) actually got this (approximately) right with a 'virtual directory' for 'my home directory' regardless of where exactly it is on disk. Programs refer to that virtual location.

The current status quo on any *nix is absurd.

(Just FTR, I only use Linux systems, personally. My criticism is borne from a place of aspiration/hope.)

jm4|1 year ago

Why do it this way instead of just using GNU stow?

zzzbra|1 year ago

[deleted]

dansalias|1 year ago

Ha forgive me, yes, British schooling, I put at least a full minute's deliberation into my first word for this very reason! Evidently chose wrong.