How I manage my dotfiles using GNU Stow (2024)

Originally published at writingco.de. This is a cross-post from my content blog. I will be publishing new content every week or so, and you can sign up to my newsletter if you'd like to receive my articles and other info tidbits directly to your inbox!

Managing your dotfiles is a fairly common thing for developers. But more often than not for many, things “just work”. If duct tape really was a developer tool, it would surely be found here. But it should not be hard to handle your dotfiles like a pro. In this article I will show you how. how to manage your dotfiles like a pro.

Developers don’t often have to deal with managing dotfiles on windows. So it might be only items such as ssh, orchestration tools like docker, and language specific things like environments in python, build targets for Rust, or setting up Go. However these tools can still be automated. Stow has other advanced usages and way to run it. On windows, you can use a tool called dploy mentioned later.

GNU Stow

First thing is first. Install the stow binary on your platform of choice.

OSX

with brew installed, you can simply run:

brew install stow

Boom you’re done! pretty simple.

Debian/Ubuntu (and derivatives)

Easy as well.

sudo apt install stow

Arch

Just as simple

sudo pacman -S stow

Creating your Dotfiles

Now let’s create a folder to get started. You can manage your dotfiles repo anywhere. I keep it along side my other code in ~/code/dotfiles. So from here on, I will just refer to it as $DOT. If you want to follow along, just run this in your terminal:

export DOT=$HOME/code/dotfiles

Just replace the path to where your dotfiles are located. A common location I see others use is $HOME/.dotfiles. Now when you paste code in a bash compliant shell it will still work. While in this directory, lets first create some directories and files

mkdir -p __setup git termite/.confg bin/bin bashtouch setup.sh

this will create a few directories in your dotfiles.

  • __setup: used for one time setups, such as package installation on different platforms.
  • git: this will be a place we can save out global git configurations like .gitconfig
  • termite/.config: this will be where we store a configuration for a terminal named termite. I am using termite here to help show how the folders are structured and why.
  • bin/bin: is where you will save command line binaries made in bash so they are available system wide. this directory will be added to your $PATH
  • bash: where we will save our bash configuration.

Git

This is simple, we want to manage and keep track of our Git configuration in our dotfiles. So we will move our global .gitconfig file into this directory

mv ~/.gitconfig $DOT/git

Now we have $HOME/code/dotfiles/git/.gitconfig. We need to use stow to install it. Lets do that. From inside the $DOT directory, run this:

➜ stow -v -R -t ~ gitLINK: .gitattributes => code/dotfiles/git/.gitattributesLINK: .gitconfig => code/dotfiles/git/.gitconfig

-v just means verbose so we can see what it is doing. -R tells stow to purge old links first making sure to clean up old references. -t ~ is the target, or where this stow should be installed to. finally, we specify the directory git for what directory we are installing.

➜ ls -la | grep .gitlrwxrwxrwx 32 shawn 17 Jun 23:05 .gitattributes -> code/dotfiles/git/.gitattributeslrwxrwxrwx 28 shawn 17 Jun 23:05 .gitconfig -> code/dotfiles/git/.gitconfig

As we can see above, it placed the files back into the home directory and symlinked them. Note that this does not work on windows but will work inside the WSL environment and does quite well.

Automate the Dotfiles

So, we know we are going to need to do this more. And if you have to do it more than once you should automate it right? Of course. Automation is the key to managing repetitive tasks so we will use a bash script to install our dotfiles folders.

Open up setup.sh and place in it the following contents:

#!/usr/bin/env bash# make sure we have pulled in and updated any submodulesgit submodule initgit submodule update# what directories should be installable by all users including the root userbase=( bash)# folders that should, or only need to be installed for a local useruseronly=( git)# run the stow command for the passed in directory ($2) in location $1stowit() { usr=$1 app=$2 # -v verbose # -R recursive # -t target stow -v -R -t ${usr} ${app}}echo ""echo "Stowing apps for user: ${whoami}"# install apps available to local users and rootfor app in ${base[@]}; do stowit "${HOME}" $app done# install only user space foldersfor app in ${useronly[@]}; do if [[! "$(whoami)" = *"root"*]]; then stowit "${HOME}" $app fidoneecho ""echo "##### ALL DONE"

In the code above, we will install the git directory for only the local user as root doesn’t need that. However bash which we will do next, can be used for both local users and root. We then create a bash function named stowit to run the actual stow command with our required arguments. If we were to call this when we installed git, it would be called as

stowit ~ git

which you can see in the loops just after that when I call stowit. It’s rather simple really. The first loop is to install folders for any user, and the second has a check to install for any user unless it is the root user. So lets setup the bash directory.

mv ~/.bashrc $DOT/bashmv ~/.bash_profile $DOT/bashmv ~/.profile $DOT/bash

OSX may not have all of these, Windows for sure does not. If you don’t have them, then you can ignore them.

Run the setup

The bash files are now being managed in your dotfiles repo. So now lets try running our setup file.

➜ chmod +x setup.sh ➜ ./setup.shStowing apps for user:LINK: .profile => code/dotfiles/bash/.profileLINK: .bashrc => code/dotfiles/bash/.bashrcLINK: .bash_profile => code/dotfiles/bash/.bash_profileUNLINK: .gitattributesUNLINK: .gitconfigLINK: .gitattributes => code/dotfiles/git/.gitattributes (reverts previous action)LINK: .gitconfig => code/dotfiles/git/.gitconfig (reverts previous action)##### ALL DONE

You can see that stow is pretty smart about linking our files and folders. It linked our new bash files. But when we ran stow again it went through our previously linked git files, re re-linked them. You can actually configure how that handles those situations with different flags. stow will also abort stowing folders when it finds new files that have not been stowed before and will tell you what files so you can fix them. If we were to run

sudo ./setup.sh

then only the bash files would be setup in the roots home directory. This is nice because often times when we have to change to the root user we lose all the cool setups we have done for our user.

The bin directory

We have two last files in our setup. bin will be easier so lets do that. inside the $DOT/bin/bin folder we can place any binary files we want to keep around across systems. Just make sure they are cross platform. This is why I like using bash, and Go binaries since you don’t need to worry about dependencies. Python is the same but it can depend on what version of python is available. As much as I love python for things, bash is best here unless there’s some magic tools in python to do what we need. Like processing CSV files or manipulating data.

In the $DOT/bin/bin folder, lets create a file named $ … YES! Just a dollar sign. You may be asking why, but you will see. Update it’s contents to be:

#!/bin/zsh# Ever pasted "$ somecommand" into the terminal and gotten this error?# -bash: $: command not found# Begone, silly errors! Lazy copy + paste forever!! ETCETERA!!!!echo 'Quit pasting in commands from the internet, you lazy bum.'"$@"

Then, in $HOME/.bashrc simply add this line:

export PATH="$HOME/bin/$:$PATH"

Run setup.sh again to link the files. Once linked we need to update our bash terminal environment. You can either restart your terminal, or run

source ~/.bashrc

to do the trick. You should be able to type:

➜ which $/home/shawn/bin/$

If so, then it works. Now say you are copying and pasting a command from stack overflow. And you accidentally paste in the $ from their terminal. Which usually denotes where their prompt starts. Normally, you would get some kind of command not found error for the $ command. But now, if you take the command $ echo "I work now" and paste it into your terminal, you get:

$ echo "I work now"Quit pasting in commands from the internet, you lazy bum.I work now

Now when you copy a command from a code sample and also copy the $ character, it will continue to run the command but will also give a message (if you wish) about making sure not to copy the $. Making it not break in many cases. Now, in the $HOME/bin directory, you can place any commands you want available system wide.

Termite

Now lets do termite. I mostly have this here to help you understand how it copies directories over. So if you don’t use termite, just read along.

The program termite keeps it’s configuration file not in $HOME, but in $HOME/.config/termite. Or, more specifically, $XDG_CONFIG_HOME/termite as $XDG_CONFIG_HOME usually defaults to $HOME/.config anyways.

When we run stow -R -t ~ termite it takes the source directory, in this case $DOT/termite and maps it’s contents to the target directory, which is ~ aka $HOME. For me that is /home/shawn. On OSX it would be /Users/shawn. Inside the termite directory is a .config folder. Since $HOME/.config is already there, we must go one level deeper.

Now it will compare $DOT/termite/.config/termite with $HOME/.config/termite and see that that it doesn’t exist yet in $HOME/.config/termite and will essentially run

ln -s $HOME/code/dotfiles/termite/.config/termite $HOME/config/termite

And when we run setup.sh again, we can see this is true

➜ pwd/home/shawn/.config ➜ ls -la | grep termitelrwxrwxrwx 40 shawn 14 Jun 9:37 termite -> ../code/dotfiles/termite/.config/termite

Conclusion

I have showed you how to manage your dotfiles like a pro. It is pretty simple. I haven’t yet talked much about the _setup directory we made. You can keep other setup files for your system that should only be run once. I use _setup/osx.sh for example to install homebrew and homebrew packages, and setup other system settings. I also have _setup/arch.sh for installing my packages using pacman or trizen.

From here, you can see how this simple setup can make it much easier to manage your dotfiles for linux and osx. A few extra steps needed to get them to work in windows. You could use something like https://github.com/arecarn/dploy which is a python port of stow but needs to have python installed on your system (please use python 3.6+). It knows how to handle things like environment variable between bash ($VAR) vs windows (%VAR%) as well as using a compatible system link. You may not use termite on windows, but some are using tools like Hyper Term which despite being built on electron, is a pretty good cross platform terminal.

You should easily now be able to take what you have for your configs like vim/neovim, tmux, or anything else and just place them into your dotfiles. If you want to use a submodule for something like tmux, just do

git submodule add [tmux-repo] tmux/

Then when running the setup.sh script it will make sure the code is local and link everything accordingly.

Looking for an Expert Developer?

Just send me a message!

CONTACT ME

The post How to manage your dotfiles like a pro with stow? appeared first on my blog over at Writing Co.de.

How I manage my dotfiles using GNU Stow (2024)

References

Top Articles
Latest Posts
Article information

Author: Zonia Mosciski DO

Last Updated:

Views: 5651

Rating: 4 / 5 (51 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Zonia Mosciski DO

Birthday: 1996-05-16

Address: Suite 228 919 Deana Ford, Lake Meridithberg, NE 60017-4257

Phone: +2613987384138

Job: Chief Retail Officer

Hobby: Tai chi, Dowsing, Poi, Letterboxing, Watching movies, Video gaming, Singing

Introduction: My name is Zonia Mosciski DO, I am a enchanting, joyous, lovely, successful, hilarious, tender, outstanding person who loves writing and wants to share my knowledge and understanding with you.