Readit News logoReadit News
staticshock · 4 months ago
I do this and like it: https://github.com/staticshock/dotfiles/blob/main/Makefile

I also tend to put a Makefile into the root of any repo I work in (ignored via .git/info/exclude), so that shell commands relevant to my workflow can accumulate under that Makefile. I've been doing this for at least 10 years and love it. Most repos have some sort of a cli interface, but that doesn't mean that they're any good. Make is nice way to wrap all of them, and to standardize on at least the build/test/run commands across many disparate repos.

Here's an example of one of those from a long-abandoned project: https://gist.github.com/staticshock/0e88a3232038d14a2817e105...

cortesoft · 4 months ago
Is there a reason to not include your makefile in the git repo? Is it not useful to anyone else?
belden · 4 months ago
Speaking for myself, reasons why I don’t immediately share my own tooling:

Perhaps I want to hold it to my own standard for tooling, not the team’s.

Perhaps I want to write it in a language that the team doesn’t really embrace.

I might not think it’s worth anyone’s time to code review changes I make to my own tools.

I might want to do something dumb like bake an API key into the tool.

Maybe the project already has a similar tool, and I don’t want to explain why I am wrapping/ignoring it.

In short: sometimes the cost to collaborate on idiosyncratic tools is just higher than I’m willing to pay.

staticshock · 4 months ago
Have you ever tried to submit a PR of your Makefile to the maintainers of, say, an open source npm package? If you have, what compelling story were you able to tell about that Makefile that got the maintainers to merge your PR? And after they successfully merged it, did you continue putting up PR after PR for each successive round of tweaks to that Makefile?

What an extraordinary amount of unnecessary effort that would be. My workflow does not belong to the repo, it belongs to me. The only thing that belongs in the repo is all the shared workflows (or the elements they comprise.)

obezyian · 4 months ago
Articles like this one are more likely to drive people away from make(1) than teach them to appreciate it.

This Makefile is anything but simple. It uses advanced features that are meant for complicated situations, just to create a few symlinks. The same symlinks, every time. And if you introduce a new dotfile to the repo, you have to update the Makefile too.

It also makes no use of the main feature of make(1) - to track files for changes, and update them.

For demonstration, here is the same functionality, in sh(1):

  files=$(find files -type f | sed s=^files/==)
  echo "$files" | sed s=[^/]*\$== | sort -u | { cd; xargs mkdir -p; }
  echo "$files" | xargs -I{} -n1 ln -sf $PWD/files/{} ~/{}
Doesn't use advanced features, shorter, and you don't need to update the script for new dotfiles. And more "ubiquitous" than GNU make.

globular-toast · 4 months ago
What's funny is people use make as just an imperative task runner. For some reason people prefer to write a makefile with 10 commands rather than write 10 short shell scripts.

The real point of make is it's a declarative build system. You just tell it to make and it will do what needs to be done by figuring out dependencies and rebuilds.

cassianoleal · 4 months ago
Make is also a highly discoverable entry point to a project.

A Makefile with tasks that just run external scripts is much easier to find than the scripts directly. Add some help texts and it’s a great DevEx multiplier.

blop · 4 months ago
I think people like using makefile as a simple task runner because it's pretty much ubiquitous and also a kind of auto-descriptive standard. Interactive shells usually do autocompletion on makefile targets so it's easy to see what you can run on a project (more so on old or foreign projects)
lloeki · 4 months ago
> This Makefile is anything but simple.

This makefile does look simple to me, although it is not easy; but TFA never claimed it was supposed to be "easy".

The shell you mentioned is not in any way "simpler" nor even "easier". The same thing can be done with bash substitutions and loops (all supported by zsh), and someone would argue that it'd be simpler by virtue of not suffering e.g gnu vs bsd vs posix sed nor needing xargs; also, pipefail and such.

Also it doesn't do "the same thing": you have to be at a specific pwd and not in any subdir + you can't do a subset (e.g "make git")

Note I'm not saying it's better or worse.

I really like the article as it showcased a few advanced make features in a concrete and limited use case (symlinks). The result is simple to use and maintain.

obezyian · 4 months ago
> but TFA never claimed it was supposed to be "easy".

TFA> Another reason to use it is this turned out to be a surprisingly easy task.

matheusmoreira · 4 months ago
> And if you introduce a new dotfile to the repo, you have to update the Makefile too.

Not at all. The rules are generic. I can give make any path inside my home directory and it will try to match it against an equivalent file in the repository.

  $ make /home/matheus/.vimrc
  ln -snf /home/matheus/.files/~/.vimrc /home/matheus/.vimrc
I only need to update the makefile if I want easy to use phony targets. These phony targets in turn simply depend on the paths to the real files which activate the generic rules I described in the article.

It looks complicated because I used functions to compute all these paths from the items in the repository. Here's what the final result looks like:

  $ phony-targets GNUmakefile
  git
          /home/matheus/.config/git/config
  mpv
          /home/matheus/.config/mpv/mpv.conf
  vim
          /home/matheus/.vimrc
  # ...
I could have used any of those paths and it would have worked. The phony targets are nice but not strictly needed.

The bulk of the makefile is dealing with environment variables such as XDG_*_HOME and GNUPGHOME variables which affect where the links are supposed to end up.

robenkleene · 4 months ago
Adding to the sh implementation, `find -L "$HOME" -maxdepth 1 -type l -exec rm {} +` will clean up dead symlinks in your home directory, e.g., if a dotfile is deleted because it's not needed anymore.
haolez · 4 months ago
What worked best for me is to turn my user's home dir into a Git repository. My .gitignore contains this:

*

And if I need to add something, I just "git add -f ...". Works surprisingly well. Combines well with git-secret for things like SSH keys, certificates and API keys.

lucasoshiro · 4 months ago
I wouldn't trust that... If you by mistake run `git clean -xfd` in your home, well, you know what happens
Ferret7446 · 4 months ago
Same thing as running `rm -rf *` by mistake. Don't do that and keep backups.
fouc · 4 months ago
Ha, I'll probably never run that command but perhaps some git commands can be disabled if they're run in ~/
fouc · 4 months ago
dp-hackernews · 4 months ago
Or git-crypt or SOPS
kryptn · 4 months ago
my dotfiles follow atlassian's tutorial where they do this, but put the git dir somewhere else and alias away the git command. It has worked pretty well for me.

https://www.atlassian.com/git/tutorials/dotfiles

RichardLake · 4 months ago
Only downside to this are tools that default to only acting on stuff under version control. Whenever I use rg inside my home directory I'm caught out by this.
skydhash · 4 months ago
I was caught by that too. But a recent read has given me a tip. Add a ripgrep specific ignore files that undo the home gitignore.
wingmanjd · 4 months ago
I found Andrew Burgess's method approachable and ended up cribbing from his dotfiles concept [1] and making my own repository [2]. His method uses bash instead of make to create the symlinks defined in the links.prop file in each subdir of the repo.

I typically checkout the dotfiles repo to a personal project folder on every new OS build and then run the installer to get all my configs in place.

- [1] https://shaky.sh/simple-dotfiles/ - [2] https://gitlab.com/jgonyea/dotfiles

mcgrath_sh · 4 months ago
I use a combination of stow and make to manage my dotfiles. I added a makefile well after using stow for a decade. The makefile is more for new system setup than day to day management. I might try out replacing stow with make based on this blog, more for fun than anything. I'm a bit reluctant to replace what has been working so well for a decade, but I'm very intrigued by this. Make has always interested me. It seems like it could be incredibly powerful in the right hands.
dinkleberg · 4 months ago
Nothing beats nix + home manager. But it is fun to see how many different ways people go about solving this particular problem. All of them have their tradeoffs and annoyances.
whacked_new · 4 months ago
I advocate (and use) nix + home manager too but when you need a one-off change or test and realize you need to do the whole commit and switch thing, or when you are debugging and spelunk through the read only nix store, or when you set up a new (non-nix) computer and do the nix install, home-manager install, run the switch and get a deluge of errors...

it's simultaneously awesome but "can I really recommend this to <colleague>?"

AgentElement · 4 months ago
With nix + home manager, you can use `mkOutOfStoreSymlink` to make symlinks between the dotfile repo and the target destination in `.config`. I've found this to be the most ergonomic way to have nix-managed dotfiles. Because the out-of-store dotfile repo is symlinked, you can make little changes to your system without doing the whole commit and switch dance.

For example, here's a snippet pulled from my dotfiles that does this for multiple dotfiles at once:

  home.file =
    builtins.mapAttrs
      (key: value: {
        # symlink ~/dotfiles/configs/{value} to ~/{key}
        source = config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/dotfiles/configs/${value}";
      })
      {
        ".zshrc"                                    = "zsh/zshrc";
        ".p10k.zsh"                                 = "zsh/p10k.zsh";
        ".config/sway/config"                       = "sway/config";
        ".config/nvim/init.lua"                     = "nvim/init.lua";
      };

nothrabannosir · 4 months ago
The configs I tweak are git, bash and emacs, and each has their own way to load extra config from a local file. You can use this for stateful config local to a machine and out of the nix store.

It depends on buy-in from the tool so it’s not a panacea but it works for my use cases at least. I also don’t like to switch config for every change and it turns out I rarely have to.

nothrabannosir · 4 months ago
This whole thread is a case study in “nix is worse ROI for every of the individual things it can do, but it’s the same I for all of them”.

Nix is definitely The Way to solve this problem, but I wouldn’t recommend it if the only thing you’ll ever use it for is home-manager. Once you’ve paid the investment, though… I never want to rsync a dotfile ever again in my life.

kesor · 4 months ago
Advantages of using nix are so numerous, it would be a sin to even try to list all of them. Suffice it to say that using HM and Nix to manage dotfiles is the ultimate peak of what one can do. I just wanted to write +1 but thought that would be too short.
lloeki · 4 months ago
Until and unless you're on a system which doesn't or can't have nix (RAM size preventing evaluation, $job policy, ...)

I really appreciate and use nixpkgs+nix-darwin as well as NixOS and flakes and per-project nix-shell/nix develop through and through; even then, Home Manager is a bridge too far for me.

One of the features of my dotfiles setup is that it has zero dependencies (beyond a POSIX shell).

doubled112 · 4 months ago
I've been using Chezmoi for a little while now. It simplifies what I want to do better than anything else I have tried.
thayne · 4 months ago
Chezmoi is probably the best thing I've found. But I do have aome complaints about it. I don't love the filenames, and using symlinks often feels like a second class citizen.

It also has the downside that you have to install and set up chezmoi first, and it isn't included in the debian or ubuntu repos.

bbkane · 4 months ago
This is very clever, but I'd have to relearn Make's subtleties each time I tried to debug it or add a feature (count me in the "prefer to avoid it" crowd).

I ended up writing a Go CLI to symlink my dotfiles. It's probably 10x more lines of code, but it uses a "plan, then execute" pattern, similar to Terraform that I find easier to test and clearer to use too. And it's a single binary so it's easy to install on Windows too.