Files
site/public/blog/2021/09/08/why-i-threw-out-my-dotfiles/index.html
2023-12-10 18:36:37 -05:00

314 lines
15 KiB
HTML

<!DOCTYPE html>
<html><head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"><title>Why I Threw Out My Dotfiles - davegallant</title><link rel="icon" type="image/png" href=https://davegallant.ca/favicon.ico /><meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="" />
<meta property="og:image" content=""/>
<meta property="og:title" content="Why I Threw Out My Dotfiles" />
<meta property="og:description" content="" />
<meta property="og:type" content="article" />
<meta property="og:url" content="/blog/2021/09/08/why-i-threw-out-my-dotfiles/" /><meta property="article:section" content="post" />
<meta property="article:published_time" content="2021-09-08T00:42:33-04:00" />
<meta property="article:modified_time" content="2021-09-08T00:42:33-04:00" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="Why I Threw Out My Dotfiles"/>
<meta name="twitter:description" content=""/>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@1,500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Fira+Sans&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.0e5aa3b634b92d61bafebfd908290cc7a034e4d50e6a0c59ce50044560179c4e.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.b11f422ffce8151207bad84653d44cb512043f9efe93a0a049f836b9cc32b34a.css" disabled />
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
tex2jax: {
inlineMath: [['$','$'], ['\\(','\\)']],
displayMath: [['$$','$$'], ['\[','\]']],
processEscapes: true,
processEnvironments: true,
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
TeX: { equationNumbers: { autoNumber: "AMS" },
extensions: ["AMSmath.js", "AMSsymbols.js"] }
}
});
</script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/contrib/auto-render.min.js" onload="renderMathInElement(document.body);"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
renderMathInElement(document.body, {
delimiters: [
{left: "$$", right: "$$", display: true},
{left: "$", right: "$", display: false}
]
});
});
</script>
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
</head>
<body>
<div class="content"><header>
<div class="main">
<a href="/">davegallant</a>
</div>
<nav>
<a href="/">Home</a>
<a href="/post">All posts</a>
<a href="/index.xml">RSS</a>
<a href="/tags">Tags</a>
<a href="/about">About</a>
| <span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</nav>
</header>
<main>
<article>
<div class="title">
<h1 class="title">Why I Threw Out My Dotfiles</h1>
<div class="meta">Posted on Sep 8, 2021</div>
</div>
<section class="body"><p>Over the years I have collected a number of dotfiles that I have shared across both Linux and macOS machines (<code>~/.zshrc</code>, <code>~/.config/git/config</code>, <code>~/.config/tmux/tmux.conf</code>, etc). I have tried several different ways to manage them, including <a href="https://www.atlassian.com/git/tutorials/dotfiles">bare git repos</a> and utilities such as <a href="https://www.gnu.org/software/stow/">GNU Stow</a>. These solutions work well enough, but I have since found what I would consider a much better solution for organizing user configuration: <a href="https://github.com/nix-community/home-manager">home-manager</a>.</p>
<h2 id="what-is-home-manager">What is home-manager?<a href="#what-is-home-manager" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>Before understanding home-manager, it is worth briefly discussing what nix is. <a href="https://nixos.org/">nix</a> is a package manager that originally spawned from a <a href="https://edolstra.github.io/pubs/phd-thesis.pdf">PhD thesis</a>. Unlike other package managers, it uses symbolic links to keep track of the currently installed packages, keeping around the old ones in case you may want to rollback.</p>
<p>For example, I have used nix to install the package <a href="https://search.nixos.org/packages?channel=unstable&amp;show=bind&amp;from=0&amp;size=50&amp;sort=relevance&amp;type=packages&amp;query=bind">bind</a> which includes <code>dig</code>. You can see that it is available on multiple platforms. The absolute path of <code>dig</code> can be found by running:</p>
<pre><code class="language-console">$ ls -lh $(which dig)
lrwxr-xr-x 73 root 31 Dec 1969 /run/current-system/sw/bin/dig -&gt; /nix/store/0r4qdyprljd3dki57jn6c6a8dh2rbg9g-bind-9.16.16-dnsutils/bin/dig
</code></pre>
<p>Notice that there is a hash included in the file path? This is a nix store path and is computed by the nix package manager. This <a href="https://nixos.org/guides/nix-pills/nix-store-paths.html">nix pill</a> does a good job explaining how this hash is computed. All of the nix pills are worth a read, if you are interested in learning more about nix itself. However, using home-manager does not require extensive knowledge of nix.</p>
<p>Part of the nix ecosystem includes <a href="https://github.com/NixOS/nixpkgs">nixpkgs</a>. Many popular tools can be found already packaged in this repository. As you can see with these <a href="https://repology.org/repositories/statistics/total">stats</a>, there is a large number of existing packages that are being maintained by the community. Contributing a new package is easy, and anyone can do it!</p>
<p>home-manager leverages the nix package manager (and nixpkgs), as well the nix language so that you can declaratively define your system configuration. I store my <a href="https://github.com/davegallant/nix-config">nix-config</a> in git so that I can keep track of my packages and configurations, and retain a clean and informative git commit history so that I can understand what changed and why.</p>
<h2 id="setting-up-home-manager">Setting up home-manager<a href="#setting-up-home-manager" class="hanchor" ariaLabel="Anchor">#</a></h2>
<blockquote>
<p>⚠️ If you run this on your main machine, make sure you backup your configuration files first. home-manager is pretty good about not overwriting existing configuration, but it is better to have a backup! Alternatively, you could test this out on a VM or cloud instance.</p>
</blockquote>
<p>The first thing you should do is <a href="https://nixos.org/guides/install-nix.html">install nix</a>:</p>
<pre><code class="language-shell">curl -L https://nixos.org/nix/install | sh
</code></pre>
<p>It&rsquo;s generally not a good idea to curl and execute files from the internet (without verifying integrity), so you might want to download the install script first and take a look before executing it!</p>
<p>Open up a new shell in your terminal and running <code>nix</code> <em>should</em> work. If not, run <code>. ~/.nix-profile/etc/profile.d/nix.sh</code></p>
<p>Now, <a href="https://github.com/nix-community/home-manager#installation">install home-manager</a>:</p>
<pre><code class="language-shell">nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager
nix-channel --update
nix-shell '&lt;home-manager&gt;' -A install
</code></pre>
<p>You should see a wave of <code>/nix/store/*</code> paths being displayed on your screen.</p>
<p>Now, to start off with a basic configuration, open up <code>~/.config/nixpkgs/home.nix</code> in the editor of your choice and paste this in (you will want to change <code>userName</code> and <code>homeDirectory</code>):</p>
<pre><code class="language-nix">{ config, pkgs, ... }:
{
programs.home-manager.enable = true;
home = {
username = &quot;dave&quot;;
homeDirectory = &quot;/home/dave&quot;;
stateVersion = &quot;21.11&quot;;
packages = with pkgs; [
bind
exa
fd
ripgrep
];
};
programs = {
git = {
enable = true;
aliases = {
aa = &quot;add -A .&quot;;
br = &quot;branch&quot;;
c = &quot;commit -S&quot;;
ca = &quot;commit -S --amend&quot;;
cb = &quot;checkout -b&quot;;
co = &quot;checkout&quot;;
d = &quot;diff&quot;;
l =
&quot;log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit&quot;;
};
delta = {
enable = true;
options = {
features = &quot;line-numbers decorations&quot;;
whitespace-error-style = &quot;22 reverse&quot;;
plus-style = &quot;green bold ul '#198214'&quot;;
decorations = {
commit-decoration-style = &quot;bold yellow box ul&quot;;
file-style = &quot;bold yellow ul&quot;;
file-decoration-style = &quot;none&quot;;
};
};
};
extraConfig = {
push = { default = &quot;current&quot;; };
pull = { rebase = true; };
};
};
starship = {
enable = true;
enableZshIntegration = true;
settings = {
add_newline = false;
scan_timeout = 10;
};
};
zsh = {
enable = true;
enableAutosuggestions = true;
enableSyntaxHighlighting = true;
history.size = 1000000;
localVariables = {
CASE_SENSITIVE = &quot;true&quot;;
DISABLE_UNTRACKED_FILES_DIRTY = &quot;true&quot;;
RPROMPT = &quot;&quot;; # override because macOS defaults to filepath
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE = &quot;fg=#838383,underline&quot;;
ZSH_DISABLE_COMPFIX = &quot;true&quot;;
};
initExtra = ''
export PAGER=less
'';
shellAliases = {
&quot;..&quot; = &quot;cd ..&quot;;
grep = &quot;rg --smart-case&quot;;
ls = &quot;exa -la --git&quot;;
};
&quot;oh-my-zsh&quot; = {
enable = true;
plugins = [
&quot;gitfast&quot;
&quot;last-working-dir&quot;
];
};
};
};
}
</code></pre>
<p>Save the file and run:</p>
<pre><code>home-manager switch
</code></pre>
<p>You should see another wave of <code>/nix/store/*</code> paths. The new configuration should now be active.</p>
<p>If you run <code>zsh</code>, you should see that you have <a href="https://starship.rs/">starship</a> and access to several other utils such as <code>rg</code>, <code>fd</code>, and <code>exa</code>.</p>
<p>This basic configuration above is also defining your <code>~/.config/git/config</code> and <code>.zshrc</code>. If you already have either of these files, home-manager will complain about them already existing.</p>
<p>If you run <code>cat ~/.zshrc</code>, you will see the way these configuration files are generated.</p>
<p>You can extend this configuration for programs such as (neo)vim, emacs, alacritty, ssh, etc. To see other programs, take a look at <a href="https://github.com/nix-community/home-manager/tree/master/modules/programs">home-manager/modules/programs</a>.</p>
<h2 id="gateway-to-nix">Gateway To Nix<a href="#gateway-to-nix" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>In ways, home-manager can be seen as a gateway to the nix ecosystem. If you have enjoyed the way you can declare user configuration with home-manager, you may be interested in expanding your configuration to include other system dependencies and configuration. For example, in Linux you can define your entire system&rsquo;s configuration (including the kernel, kernel modules, networking, filesystems, etc) in nix. For macOS, there is <a href="https://github.com/LnL7/nix-darwin">nix-darwin</a> that includes nix modules for configuring launchd, dock, and other preferences and services. You may also want to check out <a href="https://nixos.wiki/wiki/Flakes">Nix Flakes</a>: a more recent feature that allows you declare dependencies, and have them automatically pinned and hashed in <code>flake.lock</code>, similar to that of many modern package managers.</p>
<h2 id="wrapping-up">Wrapping up<a href="#wrapping-up" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>The title of this post is slightly misleading, since it&rsquo;s possible to retain some of your dotfiles and have them intermingle with home-manager by including them alongside nix. The idea of defining user configuration using nix can provide a clean way to maintain your configuration, and allow it to be portable across platforms. Is it worth the effort to migrate away from shell scripts and dotfiles? I&rsquo;d say so.</p></section>
<div class="post-tags">
<nav class="nav tags">
<ul class="tags">
<li><a href="/tags/nix">nix</a></li>
<li><a href="/tags/dotfiles">dotfiles</a></li>
<li><a href="/tags/home-manager">home-manager</a></li>
</ul>
</nav>
</div>
</article>
</main>
<section id='comments' class='comments'>
<div class='container sep-before'>
<div class='comments'><script>
var getTheme = window.localStorage && window.localStorage.getItem("theme-storage");
getTheme = getTheme == null ? 'light' : getTheme;
let theme = getTheme === 'dark' ? 'github-dark' : 'github-light';
let s = document.createElement('script');
s.src = 'https://utteranc.es/client.js';
s.setAttribute('repo', 'davegallant\/davegallant.github.io');
s.setAttribute('issue-term', 'pathname');
s.setAttribute('theme', theme);
s.setAttribute('crossorigin', 'anonymous');
s.setAttribute('async', '');
document.querySelector('div.comments').innerHTML = '';
document.querySelector('div.comments').appendChild(s);
</script>
</div>
</div>
</section><footer>
<div style="display:flex"><a class="soc" href="https://github.com/davegallant" rel="me" title="GitHub"><i data-feather="github"></i></a>
<a class="border"></a><a class="soc" href="https://twitter.com/davega11ant/" rel="me" title="Twitter"><i data-feather="twitter"></i></a>
<a class="border"></a><a class="soc" href="https://mastodon.social/@davegallant" rel="me" title="Mastodon"><i data-feather="speaker"></i></a>
<a class="border"></a><a class="soc" href="https://www.linkedin.com/in/dave-gallant/" rel="me" title="LinkedIn"><i data-feather="linkedin"></i></a>
<a class="border"></a></div>
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-V8WJDERTX9', { 'anonymize_ip': false });
}
</script>
<script>
feather.replace()
</script></div>
</body>
</html>