Setting Up A New Dev Environment With Existing Dotfiles (via rcm)

I started a new job recently (as a Developer Experience Engineer at Camunda!). One of my favorite things about starting a new job is setting up my dev environment. It gives me a chance to look through my previous dev environment and do some housekeeping — what do I really need to bring along with me, what can I drop, and what do I want to replace with something similar or better? It's like spring cleaning...but just like with my house, I only do it every few years.

During my previous job at Artsy, my BFF Jon taught me all about the importance of archiving your dotfiles with rcm, as a means to (a) share cool aliases and tools with each other, and (b) make it easier to set up a new environment. "Ok cool," I said, "I'll do whatever you tell me to, Jon," and I pushed all my dotfiles to a public repo.

But a dotfiles repo as a means to set up a new environment is a lot like backing up a database — if you don't actually practice restoring it, how do you know what you're backing up? And how do you know if the restore will actually work? I'll admit that I did not really know how well transferring my dotfiles would work, as I had not practiced it. I just did what Jon told me to do...in my defense, that works out 99% of the time.

One fresh laptop later, and I've got a good idea of what this side of rcm & dotfiles management looks like. It took a journey to get there, and I'll share that in a bit — but here's a TL;DR if you don't care for drama.

TL;DR

I settled on this process for setting up my environment with an existing dotfiles repo:

1. Clone my dotfiles repo.

From my root:

~> git clone git@github.com:pepopowitz/dotfiles.git ./.dotfiles

That last argument (./.dotfiles) specifies a folder to clone into. My repo is named dotfiles, with no . prefix. rcm, out of the box, wants a folder named ~/.dotfiles, with the . prefix. There's a flag to tell rcm to use something different, but I don't like to argue with the robots.

2. Install rcm.

I chose to install via homebrew. You do you.

~> brew install rcm

3. For files that should use the dotfiles repo as the source:

a. Use the rcup command to pull files from ~/.dotfiles to the root:

~> rcup filename

Note that there's no . at the beginning of the filename when sync'ing in this direction. rcm is looking for a file in your ~/.dotfiles repo with this name, and nothing in your repo will have the . prefix.

b. Make necessary adjustments to the ~/.filename file.

c. git commit and git push from your ~/.dotfiles repo.

4. For files that should use a new local file as the source

a. Use the mkrc command to push the file from ~/ into your ~/.dotfiles repo:

~> mkrc .filename

Note that there is a . at the beginning of the filename when sync'ing in this direction. This time, rcm is looking for a file in your ~ root with this name, and your config files in ~ will all have the . prefix.

b. Browse the git diff of ~/.dotfiles to make sure you didn't miss anything important

For example, when I did my .zshrc file, I noticed that git was planning to remove a few setup lines that I hadn't accounted for — a setup line for the version manager asdf, a configuration setting for oh-my-zsh that I wanted to port to plain old zsh, and an imported file that I knew I'd eventually want.

c. git commit and git push from the ~/.dotfiles repo.

🎉! Repeat steps 3 and 4 until everything's sync'ed up with your dotfiles repo.


Phew, now that half my readers got what they needed and moved on, here's a recap of my journey for those with more time.

I don't get it, what do you mean dotfiles and what is rcm?

Okay, some definitions.

Dotfiles

Dotfiles is a cute little name to encapsulate all those configuration files in the root of your environment, many of whose names begin with a dot (.). Things like .gitconfig, .zshrc or .bashrc, etc. These files define all the settings that make your terminal your terminal.

A nice thing to do with your dotfiles, or more specifically the ones that don't contain sensitive data, is to lump them into a single repo and share them on GitHub. (For the sensitive data, you should extract that stuff to a separate file that isn't included in your dotfiles repo, and include it in your shell setup script. You'll have it on your machine, but no one else will ever see it.)

I say this is a nice thing to do because sharing is caring. Are you as disappointed in macos's bluetooth support as I am? Here's an alias you can borrow to reset the coreaudio and bluetooth processes. Here's an alias for displaying a pretty git log tree, demonstrating that my friend Roop is cool and has some deep knowledge of formatting git logs.

rcm

rcm is a tool you can use to maintain your dotfiles. It sym-links the dotfiles in your root directory to files in another folder, which you can then initialize as a git repository and push up to GitHub.

But I'm not gonna lie....those rcm docs are hard for me to read and understand. They're basically manpages, and I don't do well with those. I can never find the information I'm looking for. And that's why I was a bit scared going through this process — yes, I can see the documentation for the tools provided by rcm....no, I can't see how they fit together in a real-world scenario.

My journey to set up my new environment

I started with my existing dotfiles repo. It was very Artsy, and I wasn't sure how much I wanted to carry over. I used oh-my-zsh for basically only the git plugin, and I had a bunch of aliases that were specific to Artsy. If I'd wanted to do this quickly, I likely could have cloned the repo to ~/.dotfiles, run one rcm command (I'm not sure which one), and called it a day. But I took my time instead. If you know me well, you're thinking "of course you took your time, Steve."

As mentioned above, the first couple things I did were:

1. I cloned my dotfiles repo into ~./.dotfiles

~> git clone git@github.com:pepopowitz/dotfiles.git ./.dotfiles

2. I installed rcm

~> brew install rcm

Before I could get comfortable with the process of setting up my environment based on existing dotfiles, I needed to mess around. So I experimented with some files.

3. I started with a file I knew I'd be overwriting: Brewfile.

In my dotfiles I had a file named Brewfile, which was a dumped homebrew bundle file containing all the tools I'd installed on my previous laptop. I used rcm's rcup command to bring it from my ~/.dotfiles repo into ~:

~> rcup Brewfile

This resulted in a .Brewfile, not Brewfile, in my root (the difference being the preceding .). I had hoped this rename would happen; it was nice to see it in action. rcm must assume that I want (a) the file in my root to be named with a preceding ., and (b) the file in my ~/.dotfiles repo to be named without a ., and handle that translation for me. This step proved to me that this was the case.

Further confirmation — if I look at all the files managed by rcm, I can see how they link a file in the root with a . in the name to a file in /.dotfiles without a .. I can see which files rcm is managing with the lsrc command:

~> lsrc
/Users/stevenhicks/.Brewfile:/Users/stevenhicks/.dotfiles/Brewfile
/Users/stevenhicks/.README.md:/Users/stevenhicks/.dotfiles/README.md
/Users/stevenhicks/.aliases-force.zsh:/Users/stevenhicks/.dotfiles/aliases-force.zsh
/Users/stevenhicks/.aliases.zsh:/Users/stevenhicks/.dotfiles/aliases.zsh
/Users/stevenhicks/.functions.zsh:/Users/stevenhicks/.dotfiles/functions.zsh
/Users/stevenhicks/.gitconfig:/Users/stevenhicks/.dotfiles/gitconfig
/Users/stevenhicks/.hyper.js:/Users/stevenhicks/.dotfiles/hyper.js
/Users/stevenhicks/.pear-data:/Users/stevenhicks/.dotfiles/pear-data

In this case, I really just wanted to see what would happen; I knew that I didn't want the ~/.Brewfile verbatim. So...

4. I got a fresh export from homebrew and replaced it:

~> rm .Brewfile
~> brew bundle dump --file=~/.Brewfile

~/.Brewfile now contained my new bundle, but the homebrew dump had replaced the sym-link from ~/.Brewfile to ~/.dotfiles/Brewfile. So I needed to hand control back over to rcm. To do this, I used a different command from rcm: mkrc.

~> mkrc .Brewfile

mkrc takes a file in your root (~), moves it to ~/.dotfiles, and sym-links the original root file to point at the one in your ~/.dotfiles. In this case, it points ~/.Brewfile at ~/.dotfiles/Brewfile.

Confirming that my ~/.dotfiles repo included my new changes:

~> cd .dotfiles

~/.dotfiles> git status
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   Brewfile

no changes added to commit (use "git add" and/or "git commit -a")

💪🏻💪🏻💪🏻💪🏻💪🏻

5. I pushed my Brewfile changes up to GitHub.

~/.dotfiles> git commit -am "remove a bunch of stuff from brewfile"
~/.dotfiles> git push origin

I removed the output from these commands because they're pretty noisy. But while doing this, it gave me an error that I hadn't yet set up my committer name and email in a global .gitconfig! That makes sense — I'd not yet synced my .gitconfig from my dotfiles. So I did that next.

6. Sync ~/.dotfiles/gitconfig into a new ~/.gitconfig.

I kind of got lucky, since this second file was the opposite direction — whereas with ~/.Brewfile I wanted to use a new file as the source, in this case I wanted to use the ~/.dotfiles version as the source.

There's another rcm command for sync'ing in this direction: rcup. This one takes a file from the ~/.dotfiles directory and creates a sym-link in ~ to point at it. In this case, I told it to point a sym-link from ~/.gitconfig to ~/.dotfiles/gitconfig:

~> rcup gitconfig

I then made some changes to the .gitconfig, like updating my email address and removing a couple unused aliases. These changes needed to be committed to my GitHub repo, so I did a git commit and a git push.

Like I said, I was lucky. The first two files I tried were intended to sync in opposite directions, with one being based on the dotfiles version, and one being based on a new file. At this point I was feeling pretty comfortable with the flow for the rest of my dotfiles...which I described above in the TL;DR.


Have you used rcm to manage your dotfiles? Am I doing something different than you are? I'd love to hear about how.