xyno - Blog
https://xyno.space
2023-08-28T00:00:00Z
(c) 2024Lucy Hochkamp
A Blog about Software Engineering, Hardware, NixOS, and more
Lucy Hochkamp
blog@xyno.space
Using Parcel with Go Templates
2023-08-28T00:00:00Z
parcel-quicktemplate
<p>When I built my blog engine
(of because I build my own blog engine, because where is the fun in using a premade one)
I really wanted to try TailwindCSS for the first time.</p>
<p>You can actually use tailwind just by itself, and it works, which is what I did at the start.
I later wanted to manage fonts better than just statically serving</p>
<p><code>${pkgs.jetbrains-mono}/share/fonts/truetype/JetBrainsMono[wght].ttf</code>, so a different solution was needed.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
Tailwinds “I’m gonna search for classnames I know” thing dosen’t actually parse your html and just searches for classnames, so it even works fine with template engines
</div>
</p>
<p>In the JavaScript SPA world, the use of bundlers like webpack/vite/parcel is ubiquitous, so this is the story about how I adapted parcel to work with my blog engine.</p>
<h2 id="how-my-blog-engine-works">How my blog engine works</h2>
<p>My blog engine is build around a SQLite database storing posts, tags (that aren’t used yet) and metadata (that also isn’t used yet).
A go server reads the database and renders the markdown to HTML/RSS feeds.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
<a href="https://github.com/gomarkdown/markdown" target="_blank">gomarkdown</a> is a pretty great markdown renderer. you can easily modify it’s parser/renderer to, for example, build tangent blocks like these.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
damn, I can stack tangents.
</div>
</p>
<p>
</div>
</p>
<p>The rendered markdown then gets templated into a <a href="https://github.com/valyala/quicktemplate" target="_blank">quicktemplate</a> to generate the HTML sent to the reader.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
quicktemplate is actually not a runtime templating engine, but a code generator. The code it generates is pretty much equivalent to just <code>writer.Write("string")</code>, so it’s really fast.
</div>
</p>
<p>I wanted parcel to take the templates and modify them to include CSS, fonts and, should I ever decide to integrate frontend JS, that as well.</p>
<h2 id="what-is-a-bundler-and-why-do-i-want-to-use-it">What is a bundler and why do I want to use it</h2>
<p>A bundler takes all your JS/Fonts/CSS files and combines them to a minimum of files.
This reduces the amount of requests your browser has to make, and it makes caching easier.
It also allows the bundler to merge your code and its dependencies, so for example if you do</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">@</span><span class="k">import</span> <span class="s1">'npm:@fontsource-variable/jetbrains-mono/wght-italic.css'</span><span class="p">;</span>
</span></span></code></pre>
<p>in an included CSS file, your bundler can automagically also output the font into your application.</p>
<p>Parcel by default outputs files with hashes in their file name, so you can just tell your web server to set its cache policy to forever and bam, easy caching.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
This is so cool.
</div>
</p>
<h2 id="getting-parcel-to-play-nice-with-templates">Getting parcel to play nice with templates</h2>
<h3 id="how-parcel-works">How parcel works</h3>
<p>To understand how parcel works, reading its <a href="https://parceljs.org/plugin-system/overview/" target="_blank">Plugin System Overview</a> is probably the best resource.
But I’ll try to give a short overview to describe where I needed to hook in to make it work with <code>qtpl</code>.</p>
<p>Parcel has Resolvers and Transformers to figure out which assets make up your project</p>
<p><strong>Resolvers:</strong></p>
<p>Resolvers turn dependency requests into absolute paths. So it’ll and convert our <code>npm:@fontsource-variable/jetbrains-mono/wght-italic.css</code> import to <code><project_dir>/node_modules/@fontsource-variable/jetbrains-mono/jetbrains-mono.css</code></p>
<p><strong>Transformers:</strong></p>
<p>Transformers take a file and convert it somehow. So if you had for example a SCSS file, a transformer would convert it to CSS. Another Transformer might minimize an HTML file.
They also add dependencies to the asset graph for the resolvers to resolve. So our JetBrains-Mono gets added to the asset graph by a CSS transformer.</p>
<p>The Assets then get bundled (by Bundler plugins) to combine files where possible, named (by Namer plugins) to figure out file paths, and then they’re written to the output directory.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
There are other steps like Compressors or Validators but we’ll ignore them here.
</div>
</p>
<h3 id="the-parcel-plugins-i-needed-to-write">The Parcel Plugins I needed to write</h3>
<p>Parcel plugins are their own JS Projects with their own package.json, etc.
Using yarn workspaces this wasn’t even as painful as I had thought.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
<a href="https://parceljs.org/features/plugins/#relative-file-paths" target="_blank">now they can even be just JS Module files</a>, that would have been so much easier.
</div>
</p>
<p>The main JS file of the Plugin just has to default export the Plugin class itself.</p>
<h4 id="a-resolver-to-ignore-most-kind-of-imports-in-qtpl-files">A resolver to ignore most kind of imports in <code>.qtpl</code> files</h4>
<p>In the blog engine, links to posts/etc. get templated into the page at runtime.
Parcel tries to import anything, even links to template strings.</p>
<p>So I needed to build a resolver that just ignores imports from <code>.qtpl</code> files if the import isn’t CSS or JS</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
if you want to adopt it to other templating engines like <code>html/template</code>, just change the file endings.
</div>
</p>
<p>That’s the resolver:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1">// packages/parcel-resolver-qtpl/src/index.js
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">Resolver</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'@parcel/plugin'</span><span class="p">)</span><span class="p">;</span> <span class="c1">// cjs is ugly but it just worked and I'm lazy
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="nx">exports</span><span class="p">.</span><span class="k">default</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Resolver</span><span class="p">(</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kr">async</span> <span class="nx">resolve</span><span class="p">(</span><span class="nx">x</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">x</span><span class="p">.</span><span class="nx">dependency</span> <span class="o">|</span> <span class="o">!</span><span class="nx">x</span><span class="p">.</span><span class="nx">dependency</span><span class="p">.</span><span class="nx">sourcePath</span><span class="p">)</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// dependency can be undefined
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// make sure only css and js files are included from qtpl files
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="p">(</span><span class="nx">x</span><span class="p">.</span><span class="nx">dependency</span><span class="p">.</span><span class="nx">sourcePath</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="s2">".qtpl"</span><span class="p">)</span> <span class="o">&&</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// this will be confusing pain should I ever use scss or typescript
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="o">!</span><span class="p">(</span><span class="nx">x</span><span class="p">.</span><span class="nx">specifier</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="s2">".css"</span><span class="p">)</span> <span class="o">||</span> <span class="nx">x</span><span class="p">.</span><span class="nx">specifier</span><span class="p">.</span><span class="nx">endsWith</span><span class="p">(</span><span class="s2">".js"</span><span class="p">)</span><span class="p">)</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="p">{</span> <span class="nx">isExcluded</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="p">)</span><span class="p">;</span>
</span></span></code></pre>
<p>simple, isn’t it</p>
<h4 id="a-namer-to-place-assets-into-a-different-directory">A Namer to place assets into a different directory</h4>
<p>The default Namer just puts all your assets into the same directory. But as the output consists of both files to be read by the templating engine and assets, the files needed to be split into different directories.</p>
<ul>
<li>Templates go to <code>/templates</code> (I set this as the primary output path)</li>
<li>anything else goes to <code>/statics/dist</code></li>
</ul>
<p>Writing Namers is also surprisingly simple. You just need to return the file path you want the file to have in the end (relative to the primary output path).</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1">// packages/parcel-namer-split/src/index.js
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">{</span> <span class="nx">Namer</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'@parcel/plugin'</span><span class="p">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">path</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">'node:path'</span><span class="p">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">exports</span><span class="p">.</span><span class="k">default</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Namer</span><span class="p">(</span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">name</span><span class="p">(</span><span class="p">{</span> <span class="nx">bundle</span> <span class="p">}</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="nx">bundle</span><span class="p">.</span><span class="nx">type</span> <span class="o">!=</span> <span class="s2">"qtpl"</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">filePath</span> <span class="o">=</span> <span class="nx">bundle</span><span class="p">.</span><span class="nx">getMainEntry</span><span class="p">(</span><span class="p">)</span><span class="p">.</span><span class="nx">filePath</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">bn</span> <span class="o">=</span> <span class="nx">path</span><span class="p">.</span><span class="nx">basename</span><span class="p">(</span><span class="nx">filePath</span><span class="p">)</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="s2">"."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="kd">let</span> <span class="nx">hr</span> <span class="o">=</span> <span class="nx">bundle</span><span class="p">.</span><span class="nx">needsStableName</span> <span class="o">?</span> <span class="s2">"."</span> <span class="o">:</span> <span class="sb">`</span><span class="si">${</span><span class="nx">bundle</span><span class="p">.</span><span class="nx">hashReference</span><span class="si">}</span><span class="sb">.</span><span class="sb">`</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="sb">`</span><span class="sb">../statics/dist/</span><span class="si">${</span><span class="nx">bn</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="sb">.</span><span class="si">${</span><span class="nx">hr</span><span class="si">}</span><span class="si">${</span><span class="nx">bn</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="s2">""</span><span class="p">)</span><span class="si">}</span><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span> <span class="c1">// when the namer returns null, the next namer will be tried
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="p">)</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span></code></pre>
<h3 id="combining-plugins-to-have-a-working-parcel-configuration">Combining plugins to have a working parcel configuration</h3>
<p>That are all the needed plugins.</p>
<p>Now we just have to write a parcel configuration that combines our custom plugins with the defaults.
A parcel configuration is just a JSON5 file describing what plugins to use.</p>
<p>If you don’t have any <code>.parcelrc</code> it’ll just use <code>@parcel/config-default</code> as its configuration.</p>
<p>We’ll just extend <code>@parcel/config-default</code> because it does all the CSS transforming/… for us</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"extends"</span><span class="p">:</span> <span class="s2">"@parcel/config-default"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"resolvers"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"parcel-resolver-qtpl"</span><span class="p">,</span> <span class="s2">"..."</span><span class="p">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"transformers"</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"*.qtpl"</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"@parcel/transformer-posthtml"</span><span class="p">,</span> <span class="c1">// the default html transformers
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="s2">"@parcel/transformer-html"</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"*.jsonld"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"@parcel/transformer-raw"</span><span class="p">,</span> <span class="s2">"@parcel/transformer-inline-string"</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"packagers"</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"*.qtpl"</span><span class="p">:</span> <span class="s2">"@parcel/packager-html"</span> <span class="c1">// the default html packager
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="p">}</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"namers"</span><span class="p">:</span> <span class="p">[</span><span class="s2">"parcel-namer-split"</span><span class="p">,</span> <span class="s2">"..."</span> <span class="p">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span></code></pre>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
<code>"..."</code> just includes the defaults</p>
<p><code>@parcel/transformer-raw</code> just takes the input and returns it as an output file.</p>
<p><code>@parcel/transformer-inline-string</code> takes an input and returns it as an inline string. The HTML transformer doesn’t like to write files into itself.
</div>
</p>
<p>I needed to explicitly handle <code>jsonld</code> and tell parcel to do nothing with it, as Parcel will - by default - transform JSON-LD meta tags to resolve listed dependencies, etc.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
You’re probably wondering what <a href="https://json-ld.org/" target="_blank">JSON-LD</a> is. It’s a JSON (who would have guessed) based format for linking data.</p>
<p>It allows specifying and linking together data, so you could for example define a blog posting and their authors in JSON-LD.</p>
<p>And that’s exactly what I use it for, I define a <a href="https://schema.org/BlogPosting" target="_blank">BlogPosting</a> for every blog post of mine, because it’s an easy thing to do for some search engine optimization. (you can see it at the end of the HTML <code>head</code> of this post)
</div>
</p>
<p>I inject my JSON-LD at runtime, so it tried to parse the template string as JSON, without much success.</p>
<div style="padding-top: 1rem;"></div>
<p>Including Tailwind CSS was as easy as just following <a href="https://tailwindcss.com/docs/installation/using-postcss" target="_blank">Tailwind’s tutorial for PostCSS</a>, without installing <code>autoprefixer</code>, as Parcel already does that for us.</p>
<p>Parcel needs to also know from which files to start building the asset graph.
You can put an array of paths in your <code>package.json</code> under the key <code>source</code>.</p>
<p>Mine looks like this:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1">// package.json (excerpt)
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nt">"source"</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"./tmplsrc/basepage.qtpl"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"./tmplsrc/error.qtpl"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"./tmplsrc/index.qtpl"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"./tmplsrc/post.qtpl"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"./tmplsrc/posts.qtpl"</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"./tmplsrc/simpleMdPage.qtpl"</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>Specifying a blob pattern should also work, but it broke the nix build somehow.
Speaking of it:</p>
<h2 id="building-the-whole-thing-with-nix">Building the whole thing with nix</h2>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
You didn’t <em>really</em> think you’ll get a post without nix, did you?
</div>
</p>
<p>My build process consists out of three parts:</p>
<ol>
<li>Build the templates with parcel</li>
<li>Convert the templates to go code with quicktemplate</li>
<li>Build the go project</li>
</ol>
<h3 id="building-the-templates-with-parcel-in-nix">Building the templates with parcel in nix</h3>
<p>I’m using yarn right now, so I just tried using <code>yarn2nix</code> (included in nixpkgs) to build the yarn project with nix.</p>
<p>The parcel plugins are part of yarn workspaces, which we need to include manually with the <code>yarn.lock</code> of the root package.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1"># flake.nix (excerpt)</span>
</span></span><span class="line"><span class="cl"><span class="n">xynoblog_tmpl</span> <span class="err">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">mkYarnPackage</span> <span class="k">rec</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">pname</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">x</span><span class="s2">y</span><span class="s2">n</span><span class="s2">o</span><span class="s2">b</span><span class="s2">l</span><span class="s2">o</span><span class="s2">g</span><span class="s2">_</span><span class="s2">t</span><span class="s2">m</span><span class="s2">p</span><span class="s2">l</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">version</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">0</span><span class="s2">.</span><span class="s2">0</span><span class="s2">.</span><span class="s2">1</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="sr">./.</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">workspaceDependencies</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">map</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="n">x</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="n">pkgs</span><span class="o">.</span><span class="n">mkYarnPackage</span> <span class="p">{</span> <span class="c1"># generate a yarn package for everything</span>
</span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="s2">"</span><span class="si">${</span><span class="sr">./packages</span><span class="si">}</span><span class="s2">/</span><span class="si">${</span><span class="n">x</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">yarnLock</span> <span class="o">=</span> <span class="n">src</span> <span class="o">+</span> <span class="s2">"</span><span class="s2">/</span><span class="s2">y</span><span class="s2">a</span><span class="s2">r</span><span class="s2">n</span><span class="s2">.</span><span class="s2">l</span><span class="s2">o</span><span class="s2">c</span><span class="s2">k</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># use root lock file</span>
</span></span><span class="line"><span class="cl"> <span class="n">fixupPhase</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">r</span><span class="s2">u</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="k">inherit</span> <span class="n">version</span> <span class="n">offlineCache</span><span class="p">;</span> <span class="c1"># inherit the parents version and cache</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">(</span><span class="nb">builtins</span><span class="o">.</span><span class="n">attrNames</span> <span class="p">(</span><span class="nb">builtins</span><span class="o">.</span><span class="n">readDir</span> <span class="sr">./packages</span><span class="p">)</span><span class="p">)</span><span class="p">)</span><span class="p">;</span> <span class="c1"># import all packages in the packages directory</span>
</span></span><span class="line"><span class="cl"> <span class="n">offlineCache</span> <span class="o">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">fetchYarnDeps</span> <span class="p">{</span> <span class="c1"># this fetches yarn dependencies into nix</span>
</span></span><span class="line"><span class="cl"> <span class="n">yarnLock</span> <span class="o">=</span> <span class="n">src</span> <span class="o">+</span> <span class="s2">"</span><span class="s2">/</span><span class="s2">y</span><span class="s2">a</span><span class="s2">r</span><span class="s2">n</span><span class="s2">.</span><span class="s2">l</span><span class="s2">o</span><span class="s2">c</span><span class="s2">k</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># sha256 = pkgs.lib.fakeSha256;</span>
</span></span><span class="line"><span class="cl"> <span class="n">sha256</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">s</span><span class="s2">h</span><span class="s2">a</span><span class="s2">2</span><span class="s2">5</span><span class="s2">6</span><span class="s2">-</span><span class="s2">I</span><span class="s2">m</span><span class="s2">a</span><span class="s2">g</span><span class="s2">i</span><span class="s2">n</span><span class="s2">e</span><span class="s2">A</span><span class="s2">R</span><span class="s2">e</span><span class="s2">a</span><span class="s2">l</span><span class="s2">S</span><span class="s2">H</span><span class="s2">A</span><span class="s2">2</span><span class="s2">5</span><span class="s2">6</span><span class="s2">H</span><span class="s2">e</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">I</span><span class="s2">t</span><span class="s2">G</span><span class="s2">e</span><span class="s2">t</span><span class="s2">s</span><span class="s2">G</span><span class="s2">e</span><span class="s2">n</span><span class="s2">e</span><span class="s2">r</span><span class="s2">a</span><span class="s2">t</span><span class="s2">e</span><span class="s2">d</span><span class="s2">B</span><span class="s2">y</span><span class="s2">N</span><span class="s2">i</span><span class="s2">x</span><span class="s2">=</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># reproducible ✨</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="sr">./.</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">distPhase</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">r</span><span class="s2">u</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># we do everything in the buildPhase</span>
</span></span><span class="line"><span class="cl"> <span class="n">installPhase</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">r</span><span class="s2">u</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fixupPhase</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">r</span><span class="s2">u</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">buildPhase</span> <span class="o">=</span> <span class="s1">''</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">e</span><span class="s1">x</span><span class="s1">p</span><span class="s1">o</span><span class="s1">r</span><span class="s1">t</span><span class="s1"> </span><span class="s1">H</span><span class="s1">O</span><span class="s1">M</span><span class="s1">E</span><span class="s1">=</span><span class="s1">$(</span><span class="s1">m</span><span class="s1">k</span><span class="s1">t</span><span class="s1">e</span><span class="s1">m</span><span class="s1">p</span><span class="s1"> </span><span class="s1">-</span><span class="s1">d</span><span class="s1">)</span><span class="s1"> </span><span class="s1">#</span><span class="s1"> </span><span class="s1">y</span><span class="s1">a</span><span class="s1">r</span><span class="s1">n</span><span class="s1"> </span><span class="s1">n</span><span class="s1">e</span><span class="s1">e</span><span class="s1">d</span><span class="s1">s</span><span class="s1"> </span><span class="s1">$H</span><span class="s1">O</span><span class="s1">M</span><span class="s1">E</span><span class="s1"> </span><span class="s1">t</span><span class="s1">o</span><span class="s1"> </span><span class="s1">b</span><span class="s1">e</span><span class="s1"> </span><span class="s1">s</span><span class="s1">e</span><span class="s1">t</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">m</span><span class="s1">k</span><span class="s1">d</span><span class="s1">i</span><span class="s1">r</span><span class="s1"> </span><span class="s1">-</span><span class="s1">p</span><span class="s1"> </span><span class="s1">$o</span><span class="s1">u</span><span class="s1">t</span><span class="s1">/</span><span class="s1">t</span><span class="s1">e</span><span class="s1">m</span><span class="s1">p</span><span class="s1">l</span><span class="s1">a</span><span class="s1">t</span><span class="s1">e</span><span class="s1">s</span><span class="s1"> </span><span class="s1">#</span><span class="s1"> </span><span class="s1">c</span><span class="s1">r</span><span class="s1">e</span><span class="s1">a</span><span class="s1">t</span><span class="s1">e</span><span class="s1"> </span><span class="s1">o</span><span class="s1">u</span><span class="s1">t</span><span class="s1">p</span><span class="s1">u</span><span class="s1">t</span><span class="s1"> </span><span class="s1">d</span><span class="s1">i</span><span class="s1">r</span><span class="s1">e</span><span class="s1">c</span><span class="s1">t</span><span class="s1">o</span><span class="s1">r</span><span class="s1">y</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">y</span><span class="s1">a</span><span class="s1">r</span><span class="s1">n</span><span class="s1"> </span><span class="s1">-</span><span class="s1">-</span><span class="s1">o</span><span class="s1">f</span><span class="s1">f</span><span class="s1">l</span><span class="s1">i</span><span class="s1">n</span><span class="s1">e</span><span class="s1"> </span><span class="s1">p</span><span class="s1">a</span><span class="s1">r</span><span class="s1">c</span><span class="s1">e</span><span class="s1">l</span><span class="s1"> </span><span class="s1">b</span><span class="s1">u</span><span class="s1">i</span><span class="s1">l</span><span class="s1">d</span><span class="s1"> </span><span class="s1">-</span><span class="s1">-</span><span class="s1">d</span><span class="s1">i</span><span class="s1">s</span><span class="s1">t</span><span class="s1">-</span><span class="s1">d</span><span class="s1">i</span><span class="s1">r</span><span class="s1"> </span><span class="s1">$o</span><span class="s1">u</span><span class="s1">t</span><span class="s1">/</span><span class="s1">t</span><span class="s1">e</span><span class="s1">m</span><span class="s1">p</span><span class="s1">l</span><span class="s1">a</span><span class="s1">t</span><span class="s1">e</span><span class="s1">s</span><span class="s1"> </span><span class="s1">#</span><span class="s1"> </span><span class="s1">r</span><span class="s1">u</span><span class="s1">n</span><span class="s1"> </span><span class="s1">p</span><span class="s1">a</span><span class="s1">r</span><span class="s1">c</span><span class="s1">e</span><span class="s1">l</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="p">;</span>
</span></span></code></pre>
<p>Now all the build templates/assets are built into a nix derivation.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
In <code>pkgs.fetchYarnDeps</code>, you get the right sha256 just like you do with <code>pkgs.buildGoModule</code>.</p>
<p>Setting it to <code>pkgs.lib.fakeSha256</code> and seeing onto which sha256 it mismatches.
</div>
</p>
<h3 id="converting-templates-and-building-the-application">Converting templates and building the application</h3>
<p>I just put template copying/building into the derivation of the application itself.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1"># flake.nix (excerpt)</span>
</span></span><span class="line"><span class="cl"><span class="n">xynoblog</span> <span class="err">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">buildGoModule</span> <span class="k">rec</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">pname</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">x</span><span class="s2">y</span><span class="s2">n</span><span class="s2">o</span><span class="s2">b</span><span class="s2">l</span><span class="s2">o</span><span class="s2">g</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">version</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">0</span><span class="s2">.</span><span class="s2">0</span><span class="s2">.</span><span class="s2">1</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="sr">./.</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">nativeBuildInputs</span> <span class="o">=</span> <span class="p">[</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">quicktemplate</span> <span class="o">.</span><span class="o">.</span><span class="o">.</span> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">preConfigure</span> <span class="o">=</span> <span class="s1">''</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">c</span><span class="s1">p</span><span class="s1"> </span><span class="s1">-</span><span class="s1">r</span><span class="s1"> </span><span class="si">${</span><span class="n">self</span><span class="o">.</span><span class="n">packages</span><span class="o">.</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">system</span><span class="si">}</span><span class="o">.</span><span class="n">xynoblog_tmpl</span><span class="si">}</span><span class="s1">/</span><span class="s1">{</span><span class="s1">s</span><span class="s1">t</span><span class="s1">a</span><span class="s1">t</span><span class="s1">i</span><span class="s1">c</span><span class="s1">s</span><span class="s1">,</span><span class="s1">t</span><span class="s1">e</span><span class="s1">m</span><span class="s1">p</span><span class="s1">l</span><span class="s1">a</span><span class="s1">t</span><span class="s1">e</span><span class="s1">s</span><span class="s1">}</span><span class="s1"> </span><span class="s1">.</span><span class="s1"> </span><span class="s1">#</span><span class="s1"> </span><span class="s1">c</span><span class="s1">o</span><span class="s1">p</span><span class="s1">y</span><span class="s1"> </span><span class="s1">t</span><span class="s1">h</span><span class="s1">e</span><span class="s1"> </span><span class="s1">t</span><span class="s1">e</span><span class="s1">m</span><span class="s1">p</span><span class="s1">l</span><span class="s1">a</span><span class="s1">t</span><span class="s1">e</span><span class="s1">s</span><span class="s1"> </span><span class="s1">i</span><span class="s1">n</span><span class="s1">t</span><span class="s1">o</span><span class="s1"> </span><span class="s1">a</span><span class="s1">p</span><span class="s1">p</span><span class="s1">l</span><span class="s1">i</span><span class="s1">c</span><span class="s1">a</span><span class="s1">t</span><span class="s1">i</span><span class="s1">o</span><span class="s1">n</span><span class="s1"> </span><span class="s1">s</span><span class="s1">o</span><span class="s1">u</span><span class="s1">r</span><span class="s1">c</span><span class="s1">e</span><span class="s1">s</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">c</span><span class="s1">h</span><span class="s1">m</span><span class="s1">o</span><span class="s1">d</span><span class="s1"> </span><span class="s1">+</span><span class="s1">w</span><span class="s1"> </span><span class="s1">-</span><span class="s1">R</span><span class="s1"> </span><span class="s1">.</span><span class="s1">/</span><span class="s1">{</span><span class="s1">s</span><span class="s1">t</span><span class="s1">a</span><span class="s1">t</span><span class="s1">i</span><span class="s1">c</span><span class="s1">s</span><span class="s1">,</span><span class="s1">t</span><span class="s1">e</span><span class="s1">m</span><span class="s1">p</span><span class="s1">l</span><span class="s1">a</span><span class="s1">t</span><span class="s1">e</span><span class="s1">s</span><span class="s1">}</span><span class="s1"> </span><span class="s1">#</span><span class="s1"> </span><span class="s1">w</span><span class="s1">e</span><span class="s1"> </span><span class="s1">n</span><span class="s1">e</span><span class="s1">e</span><span class="s1">d</span><span class="s1"> </span><span class="s1">t</span><span class="s1">o</span><span class="s1"> </span><span class="s1">w</span><span class="s1">r</span><span class="s1">i</span><span class="s1">t</span><span class="s1">e</span><span class="s1"> </span><span class="s1">t</span><span class="s1">o</span><span class="s1"> </span><span class="s1">t</span><span class="s1">h</span><span class="s1">e</span><span class="s1">m</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">q</span><span class="s1">t</span><span class="s1">c</span><span class="s1"> </span><span class="s1">-</span><span class="s1">d</span><span class="s1">i</span><span class="s1">r</span><span class="s1">=</span><span class="s1">t</span><span class="s1">e</span><span class="s1">m</span><span class="s1">p</span><span class="s1">l</span><span class="s1">a</span><span class="s1">t</span><span class="s1">e</span><span class="s1">s</span><span class="s1"> </span><span class="s1">#</span><span class="s1"> </span><span class="s1">r</span><span class="s1">u</span><span class="s1">n</span><span class="s1"> </span><span class="s1">t</span><span class="s1">h</span><span class="s1">e</span><span class="s1"> </span><span class="s1">c</span><span class="s1">o</span><span class="s1">d</span><span class="s1">e</span><span class="s1"> </span><span class="s1">g</span><span class="s1">e</span><span class="s1">n</span><span class="s1">e</span><span class="s1">r</span><span class="s1">a</span><span class="s1">t</span><span class="s1">o</span><span class="s1">r</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># in the buildPhase it'll turn into a normal go application</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="o">.</span><span class="o">.</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span></code></pre>
<div style="padding-bottom: 1rem;"></div>
<hr/>
<p>That’s how I use Parcel with a template engine.
If you want to read my blogs source code, it’s open source and on <a href="https://github.com/thexyno/blog" target="_blank">GitHub</a>.</p>
<p>But please don’t base your blog engine on it, and <a href="https://xeiaso.net/blog/new-language-blog-backend-2022-03-02" target="_blank">just learn a new language, and write your own</a></p>
<p>Thank you for reading, and a big thanks to
<a href="https://nzbr.link" target="_blank">Arson</a>
for their input and help in writing this post.</p>
Using Rosetta 2 in a NixOS VM
2022-09-07T00:00:00Z
nixos-utm-rosetta
<p>
<div class="border-2 mt-2 dark:text-light3 text-l_subtext1 border-l_red dark:border-bright_red px-5 py-2">
Update (2023-02-09): <a href="https://github.com/NixOS/nixpkgs/issues/209242" target="_blank">There is a bug in Rosetta right now preventing to run some GUI applications</a></p>
<p>Update (2022-10-29): Removed parts about how everything is in beta, as macOS Ventura and UTM 4 are out of beta.</p>
<p>This only works with macOS 13 (or later) and UTM 4 (or later)
</div>
</p>
<p>With the release of macOS Ventura, Apple <em>generously</em> allows people to also use Rosetta 2 in their Linux VMs.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
It of course only works in Linux VMs on Apple Silicon macs, <a href="https://twitter.com/never_released/status/1534127641082593281" target="_blank">althrough seemingly you could just take the binary and put it onto other (ARM) computers</a>
</div>
</p>
<p>To use it, you have to use a VM Software utilizing <a href="https://developer.apple.com/documentation/virtualization" target="_blank">Virtualization.Framework</a> (not to be confused with <a href="https://developer.apple.com/documentation/hypervisor" target="_blank">Hypervisor.Framework</a>), which also exposes the option to mount the Rosetta volume, and you have to be on macOS 13 (Beta).</p>
<p>We’ll use <a href="https://mac.getutm.app/" target="_blank">UTM</a> here.
The Rosetta option is only exposed on <a href="https://github.com/utmapp/UTM/releases" target="_blank">UTM Version 4.0 and above</a> (you can also get it from the <a href="https://apps.apple.com/de/app/utm-virtuelle-maschinen/id1538878817" target="_blank">Mac App Store</a>).</p>
<p>While creating the VM, check both “Use Apple Virtualization” and “Enable Rosetta”</p>
<p>Then install NixOS like normal (using an <a href="https://nixos.wiki/wiki/NixOS_on_ARM/UEFI#Getting_the_installer_image_.28ISO.29" target="_blank">aarch64 ISO</a>).</p>
<p>To mount the Rosetta <code>virtiofs</code>, register the binary format and tell nix that you can build x86 now, put the following inside your NixOS configuration:</p>
<p>
<div class="border-2 mt-2 dark:text-light3 text-l_subtext1 dark:border-light3 border-l_surface2 px-5 py-2">
Update (2023-07-28): On current NixOS (23.05) you can just do <code>virtualisation.rosetta.enable = true;</code> instead of the following
</div>
</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">{</span> <span class="n">config</span><span class="o">,</span> <span class="n">lib</span><span class="o">,</span> <span class="o">.</span><span class="o">.</span><span class="o">.</span><span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">availableKernelModules</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">v</span><span class="s2">i</span><span class="s2">r</span><span class="s2">t</span><span class="s2">i</span><span class="s2">o</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">r</span><span class="s2">u</span><span class="s2">n</span><span class="s2">/</span><span class="s2">r</span><span class="s2">o</span><span class="s2">s</span><span class="s2">e</span><span class="s2">t</span><span class="s2">t</span><span class="s2">a</span><span class="s2">"</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">device</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">r</span><span class="s2">o</span><span class="s2">s</span><span class="s2">e</span><span class="s2">t</span><span class="s2">t</span><span class="s2">a</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">v</span><span class="s2">i</span><span class="s2">r</span><span class="s2">t</span><span class="s2">i</span><span class="s2">o</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">nix</span><span class="o">.</span><span class="n">settings</span><span class="o">.</span><span class="n">extra-platforms</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">x</span><span class="s2">8</span><span class="s2">6</span><span class="s2">_</span><span class="s2">6</span><span class="s2">4</span><span class="s2">-</span><span class="s2">l</span><span class="s2">i</span><span class="s2">n</span><span class="s2">u</span><span class="s2">x</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">nix</span><span class="o">.</span><span class="n">settings</span><span class="o">.</span><span class="n">extra-sandbox-paths</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">/</span><span class="s2">r</span><span class="s2">u</span><span class="s2">n</span><span class="s2">/</span><span class="s2">r</span><span class="s2">o</span><span class="s2">s</span><span class="s2">e</span><span class="s2">t</span><span class="s2">t</span><span class="s2">a</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">/</span><span class="s2">r</span><span class="s2">u</span><span class="s2">n</span><span class="s2">/</span><span class="s2">b</span><span class="s2">i</span><span class="s2">n</span><span class="s2">f</span><span class="s2">m</span><span class="s2">t</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">binfmt</span><span class="o">.</span><span class="n">registrations</span><span class="o">.</span><span class="s2">"</span><span class="s2">r</span><span class="s2">o</span><span class="s2">s</span><span class="s2">e</span><span class="s2">t</span><span class="s2">t</span><span class="s2">a</span><span class="s2">"</span> <span class="o">=</span> <span class="p">{</span> <span class="c1"># based on https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta#3978495</span>
</span></span><span class="line"><span class="cl"> <span class="n">interpreter</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">/</span><span class="s2">r</span><span class="s2">u</span><span class="s2">n</span><span class="s2">/</span><span class="s2">r</span><span class="s2">o</span><span class="s2">s</span><span class="s2">e</span><span class="s2">t</span><span class="s2">t</span><span class="s2">a</span><span class="s2">/</span><span class="s2">r</span><span class="s2">o</span><span class="s2">s</span><span class="s2">e</span><span class="s2">t</span><span class="s2">t</span><span class="s2">a</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fixBinary</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">wrapInterpreterInShell</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">matchCredentials</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">magicOrExtension</span> <span class="o">=</span> <span class="s1">''</span><span class="s1">\</span><span class="s1">x</span><span class="s1">7</span><span class="s1">f</span><span class="s1">E</span><span class="s1">L</span><span class="s1">F</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">2</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">1</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">1</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">2</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">3</span><span class="s1">e</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">mask</span> <span class="o">=</span> <span class="s1">''</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">e</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">e</span><span class="s1">\</span><span class="s1">x</span><span class="s1">0</span><span class="s1">0</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">e</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">\</span><span class="s1">x</span><span class="s1">f</span><span class="s1">f</span><span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p><code>nixos-rebuild</code></p>
<p>And now you can run X86 Applications</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl">$ nix-shell -E <span class="s1">'with import <nixpkgs> { system="x86_64-linux"; }; runCommand "dummy" { buildInputs = [ hello ]; } ""'</span> --command hello
</span></span><span class="line"><span class="cl">Hello, world!
</span></span><span class="line"><span class="cl">$
</span></span><span class="line"><span class="cl">$ <span class="c1"># or `nix run nixpkgs#legacyPackages.x86_64-linux.hello` if you're already using the new nix cli</span>
</span></span></code></pre>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
Using nix-command is such a breeze compared to how it was before
</div>
</p>
<p>Rosetta seems to work pretty good. In my testing (mostly building NixOS configuratinos to deploy to servers) everything just worked and UTM only crashed once.</p>
<p>Setting the VM up as a <a href="https://nixos.org/manual/nix/stable/advanced-topics/distributed-builds.html" target="_blank">remote builder</a> is also really convenient.</p>
Mounting a nix-store via NFS
2022-08-22T00:00:00Z
nix-store-nfs
<p>Lately at $work we wanted to netboot some (NixOS) Computers.
Having a full nix-store for each one on some Server seemed wasteful though, so I searched for another solution.</p>
<p>NixOS does not need a full rootfs to boot, it just needs <code>/nix</code> (and the pseudo file systems like <code>/dev</code>, <code>/proc</code>,…).
All the other directories get generated inside the activation script.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
That’s how <a href="https://github.com/nix-community/impermanence" target="_blank">Impermanence</a> (and the NixOS iso) works
</div>
</p>
<p>The path seemed clear:</p>
<ol>
<li>Netboot the kernel/initramfs from HTTP/TFTP</li>
<li>Mount the nix-store via NFS inside the initramfs</li>
<li>Profit</li>
</ol>
<p>Multiple instances of nix writing onto the same store might mean problems.
So we’ll just mount the nix store read only.</p>
<p>| But we might want to write to it</p>
<p>So we’ll overlay a rw directory over the read only store.</p>
<p>I just naively tried</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">{</span> <span class="o">.</span><span class="o">.</span><span class="o">.</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">supportedFilesystems</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">v</span><span class="s2">4</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="c1"># load needed kernel modules</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">availableKernelModules</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">v</span><span class="s2">4</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="c1"># load them again, because of cause it didn't work</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">"</span> <span class="o">=</span> <span class="p">{</span> <span class="n">device</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">m</span><span class="s2">p</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">m</span><span class="s2">p</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">s</span><span class="s2">i</span><span class="s2">z</span><span class="s2">e</span><span class="s2">=</span><span class="s2">2</span><span class="s2">G</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span> <span class="p">{</span> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">m</span><span class="s2">p</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">m</span><span class="s2">o</span><span class="s2">d</span><span class="s2">e</span><span class="s2">=</span><span class="s2">0</span><span class="s2">7</span><span class="s2">5</span><span class="s2">5</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">s</span><span class="s2">i</span><span class="s2">z</span><span class="s2">e</span><span class="s2">=</span><span class="s2">8</span><span class="s2">G</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="n">neededForBoot</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">o</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">4</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">device</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">1</span><span class="s2">9</span><span class="s2">2</span><span class="s2">.</span><span class="s2">1</span><span class="s2">6</span><span class="s2">8</span><span class="s2">.</span><span class="s2">1</span><span class="s2">.</span><span class="s2">1</span><span class="s2">:</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">r</span><span class="s2">o</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">neededForBoot</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">device</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">l</span><span class="s2">o</span><span class="s2">w</span><span class="s2">e</span><span class="s2">r</span><span class="s2">d</span><span class="s2">i</span><span class="s2">r</span><span class="s2">=</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">o</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">u</span><span class="s2">p</span><span class="s2">p</span><span class="s2">e</span><span class="s2">r</span><span class="s2">d</span><span class="s2">i</span><span class="s2">r</span><span class="s2">=</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">w</span><span class="s2">o</span><span class="s2">r</span><span class="s2">k</span><span class="s2">d</span><span class="s2">i</span><span class="s2">r</span><span class="s2">=</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">w</span><span class="s2">o</span><span class="s2">r</span><span class="s2">k</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">depends</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">o</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">w</span><span class="s2">o</span><span class="s2">r</span><span class="s2">k</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">network</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">networking</span><span class="o">.</span><span class="n">useDHCP</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>but it just didn’t boot.
The initramfs ran through, but then it just hung.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
</p>
<blockquote>
<p><code>boot.shell_on_fail</code> and the <a href="https://github.com/NixOS/nixpkgs/blob/e2f8343087e0b131562a7c1fef220abccb6d1981/nixos/modules/system/boot/stage-1-init.sh#L185" target="_blank">other cmdline options</a> really helped debugging
</div>
</p>
</blockquote>
<p>NixOS initramfs networking yeets the network configuration after booting successfully.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
</p>
<blockquote>
<p>it took me at least a solid 10h to find that out
</div>
</p>
</blockquote>
<p>You have to set <code>boot.initrd.network.flushBeforeStage2</code> to <code>false</code> to disable that.</p>
<p>And now it just works™</p>
<p>Just for completeness, a full configuration is:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">{</span> <span class="o">.</span><span class="o">.</span><span class="o">.</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">supportedFilesystems</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">v</span><span class="s2">4</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="c1"># load needed kernel modules</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">availableKernelModules</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">v</span><span class="s2">4</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="c1"># load them again, because of cause it didn't work</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">"</span> <span class="o">=</span> <span class="p">{</span> <span class="n">device</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">m</span><span class="s2">p</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">m</span><span class="s2">p</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">s</span><span class="s2">i</span><span class="s2">z</span><span class="s2">e</span><span class="s2">=</span><span class="s2">2</span><span class="s2">G</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span> <span class="p">{</span> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">t</span><span class="s2">m</span><span class="s2">p</span><span class="s2">f</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">m</span><span class="s2">o</span><span class="s2">d</span><span class="s2">e</span><span class="s2">=</span><span class="s2">0</span><span class="s2">7</span><span class="s2">5</span><span class="s2">5</span><span class="s2">"</span> <span class="s2">"</span><span class="s2">s</span><span class="s2">i</span><span class="s2">z</span><span class="s2">e</span><span class="s2">=</span><span class="s2">8</span><span class="s2">G</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span> <span class="n">neededForBoot</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">o</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">f</span><span class="s2">s</span><span class="s2">4</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">device</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">1</span><span class="s2">9</span><span class="s2">2</span><span class="s2">.</span><span class="s2">1</span><span class="s2">6</span><span class="s2">8</span><span class="s2">.</span><span class="s2">1</span><span class="s2">.</span><span class="s2">1</span><span class="s2">:</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span> <span class="s2">"</span><span class="s2">r</span><span class="s2">o</span><span class="s2">"</span> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">neededForBoot</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">fileSystems</span><span class="o">.</span><span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">fsType</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">device</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">o</span><span class="s2">v</span><span class="s2">e</span><span class="s2">r</span><span class="s2">l</span><span class="s2">a</span><span class="s2">y</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">options</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">l</span><span class="s2">o</span><span class="s2">w</span><span class="s2">e</span><span class="s2">r</span><span class="s2">d</span><span class="s2">i</span><span class="s2">r</span><span class="s2">=</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">o</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">u</span><span class="s2">p</span><span class="s2">p</span><span class="s2">e</span><span class="s2">r</span><span class="s2">d</span><span class="s2">i</span><span class="s2">r</span><span class="s2">=</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">w</span><span class="s2">o</span><span class="s2">r</span><span class="s2">k</span><span class="s2">d</span><span class="s2">i</span><span class="s2">r</span><span class="s2">=</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">w</span><span class="s2">o</span><span class="s2">r</span><span class="s2">k</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">depends</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">o</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">/</span><span class="s2">.</span><span class="s2">r</span><span class="s2">w</span><span class="s2">-</span><span class="s2">s</span><span class="s2">t</span><span class="s2">o</span><span class="s2">r</span><span class="s2">e</span><span class="s2">/</span><span class="s2">w</span><span class="s2">o</span><span class="s2">r</span><span class="s2">k</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">network</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">boot</span><span class="o">.</span><span class="n">initrd</span><span class="o">.</span><span class="n">network</span><span class="o">.</span><span class="n">flushBeforeStage2</span> <span class="o">=</span> <span class="no">false</span><span class="p">;</span> <span class="c1"># otherwise nfs dosen't work</span>
</span></span><span class="line"><span class="cl"> <span class="n">networking</span><span class="o">.</span><span class="n">useDHCP</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>The netboot stuff we built is open source and available <a href="https://github.com/nix-basement/nix-basement/" target="_blank">here</a>, but not ready for production yet.</p>
Declarative macOS Configuration Using nix-darwin And home-manager
2022-07-03T00:00:00Z
nix-darwin-introduction
<p>Last december, I bought myself an 14” M1 Pro MacBook Pro after being a linux user for eight years (the last few using NixOS on all my computers/servers and even my router).</p>
<p>Coming from NixOS, I wanted to replicate the godly feeling of declaratively configuring the entire system in every last detail with nix flakes on macOS.</p>
<p><a href="https://github.com/LnL7/nix-darwin" target="_blank">nix-darwin</a> and <a href="https://github.com/nix-community/home-manager" target="_blank">home-manager</a> are the tools to archive exactly that.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
If I didn’t knew nix-darwin existed when I needed a new laptop, I would’ve bought myself a framework.
</div>
</p>
<h2 id="why-should-anybody-want-to-do-that">Why should anybody want to do that</h2>
<ul>
<li>Reproducibility
<ul>
<li>My entire configuration is a <a href="https://github.com/thexyno/nixos-config" target="_blank">git repository</a>, on a new computer I just have to <code>darwin-rebuild</code> to get my it just as I like it.</li>
</ul></li>
<li>Reusability
<ul>
<li>I can just import my shell/editor/tmux configuration into both my laptop and my servers. Everything feels just like it should</li>
</ul></li>
<li>The ability to know what I have installed:
<ul>
<li>No 1000 forgotten packages filling up precious storage space</li>
</ul></li>
</ul>
<h2 id="explanation-of-the-nix-ecosystem">Explanation of the nix ecosystem</h2>
<h3 id="nix-nixpkgs">Nix/nixpkgs</h3>
<p>Nix is a declarative functional programming language created as part of and for the Nix package manager.</p>
<p>In Nix, packages are defined as so called <a href="https://nixos.org/manual/nix/stable/expressions/derivations.html" target="_blank">derivations</a>.
A derivation is a function with package dependencies and package configuration as its inputs, and a plan on how to build that package as its output.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
package configuration in the sense of “do you want to build vlc with chromecast support?” (similar to gentoo USE flags)
</div>
</p>
<p>Nix will evaluate that function and build it’s output. The output will then be stored in the path <code>/nix/store/${sha256 of the derivation}-${package name}</code></p>
<p>In <a href="https://github.com/NixOS/nixpkgs" target="_blank">nixpkgs</a>, the central repository for:</p>
<ul>
<li>most packages</li>
<li>a library full of helper functions that make writing nix much more enjoyable</li>
<li>and NixOS,</li>
</ul>
<p>there are helper functions to easily build such a derivation for most programming languages.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
As the naming of nix/nixpkgs can get confusing:</p>
<p>there are: nix (Programming Language), nix (Package manager), nixpkgs (Package Repository), NixOS (Linux Distribution)</p>
<p>nix (Programming language) ≠ nix (Package manager). nix (Package Manager) is the only implementation of nix (Programming Language) though.</p>
<p>nixpkgs ≠ NixOS, but NixOS ⊂ nixpkgs</p>
<p>nixpkgs ≠ nix</p>
<p>nix flakes ∈ nix ∧ nix flakes ∉ nixpkgs
</div>
</p>
<p>A simple derivation is the following:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">stdenv</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="n">gcc</span><span class="o">,</span>
</span></span><span class="line"><span class="cl"> <span class="o">.</span><span class="o">.</span><span class="o">.</span>
</span></span><span class="line"><span class="cl"><span class="p">}:</span>
</span></span><span class="line"><span class="cl"><span class="n">stdenv</span><span class="o">.</span><span class="n">mkDerivation</span> <span class="p">{</span> <span class="c1"># stdenv.mkDerivation is a nixpkgs helper function</span>
</span></span><span class="line"><span class="cl"> <span class="n">name</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">h</span><span class="s2">e</span><span class="s2">l</span><span class="s2">l</span><span class="s2">o</span><span class="s2">-</span><span class="s2">1</span><span class="s2">.</span><span class="s2">0</span><span class="s2">.</span><span class="s2">0</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># Package name</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># source files for the package</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># (can also be a git repository, a flake input, ...)</span>
</span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="sr">./src</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># inputs the builder needs to have</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># (we actually don't have to specify gcc, as a C compiler is part of the stdenv)</span>
</span></span><span class="line"><span class="cl"> <span class="n">nativeBuildInputs</span> <span class="o">=</span> <span class="p">[</span> <span class="n">gcc</span> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># tells nix to run gcc</span>
</span></span><span class="line"><span class="cl"> <span class="n">buildPhase</span> <span class="o">=</span> <span class="s1">''</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">g</span><span class="s1">c</span><span class="s1">c</span><span class="s1"> </span><span class="s1">h</span><span class="s1">e</span><span class="s1">l</span><span class="s1">l</span><span class="s1">o</span><span class="s1">.</span><span class="s1">c</span><span class="s1"> </span><span class="s1">-</span><span class="s1">o</span><span class="s1"> </span><span class="s1">.</span><span class="s1">/</span><span class="s1">h</span><span class="s1">e</span><span class="s1">l</span><span class="s1">l</span><span class="s1">o</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># tells nix to copy the binary to the right path</span>
</span></span><span class="line"><span class="cl"> <span class="n">installPhase</span> <span class="o">=</span> <span class="s1">''</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">#</span><span class="s1"> </span><span class="s1">$o</span><span class="s1">u</span><span class="s1">t</span><span class="s1"> </span><span class="s1">i</span><span class="s1">s</span><span class="s1"> </span><span class="s1">t</span><span class="s1">h</span><span class="s1">e</span><span class="s1"> </span><span class="s1">o</span><span class="s1">u</span><span class="s1">t</span><span class="s1">p</span><span class="s1">u</span><span class="s1">t</span><span class="s1"> </span><span class="s1">p</span><span class="s1">a</span><span class="s1">t</span><span class="s1">h</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">m</span><span class="s1">k</span><span class="s1">d</span><span class="s1">i</span><span class="s1">r</span><span class="s1"> </span><span class="s1">-</span><span class="s1">p</span><span class="s1"> </span><span class="s1">"</span><span class="s1">$o</span><span class="s1">u</span><span class="s1">t</span><span class="s1">/</span><span class="s1">b</span><span class="s1">i</span><span class="s1">n</span><span class="s1">"</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">c</span><span class="s1">p</span><span class="s1"> </span><span class="s1">.</span><span class="s1">/</span><span class="s1">h</span><span class="s1">e</span><span class="s1">l</span><span class="s1">l</span><span class="s1">o</span><span class="s1"> </span><span class="s1">"</span><span class="s1">$o</span><span class="s1">u</span><span class="s1">t</span><span class="s1">/</span><span class="s1">b</span><span class="s1">i</span><span class="s1">n</span><span class="s1">/</span><span class="s1">"</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
in nix, a function definition is <code>function = argument: output</code>.</p>
<p>to evaluate a function, you have to <code>function argument</code>
</div>
</p>
<p>In the above file, we define a function with the attribute set (an attribute set is basically an object) <code>{ stdenv, gcc, ... }</code> as it’s argument and <code>stdenv.mkDerivation {...}</code> as it’s output.</p>
<p>Our function will then evaluate the function <code>stdenv.mkDerivation</code> with the attribute set after it. <code>mkDerivation</code> creates a derivation out of the attribute set, and nix will build it.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
the three dots in <code>{stdenv, gcc, ...}</code> mean, that we’ll also accept an attribute set with more attributes, those will then be ignored.
</div>
</p>
<h3 id="nix-flakes">nix flakes</h3>
<p>Normally nix uses so called channels to declare what version of nixpkgs you’re running.</p>
<p>This is state external to your own package though, and it means you always have to look at multiple places to reproduce your exact configuration somewhere else.</p>
<p>To solve this problem, tools like <a href="https://github.com/nmattia/niv" target="_blank">niv</a> and <a href="https://nixos.org/manual/nix/unstable/command-ref/new-cli/nix3-flake.html" target="_blank">nix-flakes</a> were created.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
We’ll use nix-flakes here, as it’s part of nix itself</p>
<p><strong>nix-flake is an experimental feature though, so be warned</strong>
</div>
</p>
<p>A nix flake consists of two files:</p>
<p><em>flake.nix</em></p>
<p>The file where all external inputs and all outputs of your flake are defined.
For example, for a simple C program, your only input will be <code>nixpkgs</code> (as <code>stdenv.mkDerivation</code> and a C compiler are inside there), and your only output will be the derivation of your program.</p>
<p><em>flake.lock</em></p>
<p>A JSON managed by nix itself, where the exact git hash of each of your inputs will be stored. Pretty comparable to how a <code>package.lock</code> works on npm.</p>
<p>A sample flake building our little C program would look like this:</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">description</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">M</span><span class="s2">y</span><span class="s2"> </span><span class="s2">f</span><span class="s2">i</span><span class="s2">r</span><span class="s2">s</span><span class="s2">t</span><span class="s2"> </span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2"> </span><span class="s2">f</span><span class="s2">l</span><span class="s2">a</span><span class="s2">k</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">inputs</span><span class="o">.</span><span class="n">nixpkgs</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">g</span><span class="s2">i</span><span class="s2">t</span><span class="s2">h</span><span class="s2">u</span><span class="s2">b</span><span class="s2">:</span><span class="s2">N</span><span class="s2">i</span><span class="s2">x</span><span class="s2">O</span><span class="s2">S</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">p</span><span class="s2">k</span><span class="s2">g</span><span class="s2">s</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">o</span><span class="s2">s</span><span class="s2">-</span><span class="s2">2</span><span class="s2">3</span><span class="s2">.</span><span class="s2">0</span><span class="s2">5</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">outputs</span> <span class="o">=</span> <span class="p">{</span> <span class="n">self</span><span class="o">,</span> <span class="n">nixpkgs</span> <span class="p">}:</span> <span class="p">{</span> <span class="c1"># function with our inputs</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">packages</span><span class="o">.</span><span class="n">default</span><span class="o">.</span><span class="n">aarch64-darwin</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl"> <span class="k">let</span>
</span></span><span class="line"><span class="cl"> <span class="n">pkgs</span> <span class="o">=</span> <span class="kn">import</span> <span class="n">nixpkgs</span> <span class="p">{</span> <span class="n">system</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">a</span><span class="s2">a</span><span class="s2">r</span><span class="s2">c</span><span class="s2">h</span><span class="s2">6</span><span class="s2">4</span><span class="s2">-</span><span class="s2">d</span><span class="s2">a</span><span class="s2">r</span><span class="s2">w</span><span class="s2">i</span><span class="s2">n</span><span class="s2">"</span><span class="p">;</span> <span class="p">}</span><span class="p">;</span> <span class="c1"># gets ourself an instance of nixpkgs</span>
</span></span><span class="line"><span class="cl"> <span class="k">in</span>
</span></span><span class="line"><span class="cl"> <span class="n">pkgs</span><span class="o">.</span><span class="n">stdenv</span><span class="o">.</span><span class="n">mkDerivation</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">pname</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">h</span><span class="s2">e</span><span class="s2">l</span><span class="s2">l</span><span class="s2">o</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">src</span> <span class="o">=</span> <span class="sr">./src</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># the stdenv C compiler is at $CC (it's gcc on linux and clang on mac)</span>
</span></span><span class="line"><span class="cl"> <span class="n">buildPhase</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">$</span><span class="s2">C</span><span class="s2">C</span><span class="s2"> </span><span class="s2">-</span><span class="s2">o</span><span class="s2"> </span><span class="s2">h</span><span class="s2">e</span><span class="s2">l</span><span class="s2">l</span><span class="s2">o</span><span class="s2"> </span><span class="s2">.</span><span class="s2">/</span><span class="s2">h</span><span class="s2">e</span><span class="s2">l</span><span class="s2">l</span><span class="s2">o</span><span class="s2">.</span><span class="s2">c</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">installPhase</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">m</span><span class="s2">k</span><span class="s2">d</span><span class="s2">i</span><span class="s2">r</span><span class="s2"> </span><span class="s2">-</span><span class="s2">p</span><span class="s2"> </span><span class="s2">$</span><span class="s2">o</span><span class="s2">u</span><span class="s2">t</span><span class="s2">/</span><span class="s2">b</span><span class="s2">i</span><span class="s2">n</span><span class="s2">;</span><span class="s2"> </span><span class="s2">i</span><span class="s2">n</span><span class="s2">s</span><span class="s2">t</span><span class="s2">a</span><span class="s2">l</span><span class="s2">l</span><span class="s2"> </span><span class="s2">-</span><span class="s2">t</span><span class="s2"> </span><span class="s2">$</span><span class="s2">o</span><span class="s2">u</span><span class="s2">t</span><span class="s2">/</span><span class="s2">b</span><span class="s2">i</span><span class="s2">n</span><span class="s2"> </span><span class="s2">h</span><span class="s2">e</span><span class="s2">l</span><span class="s2">l</span><span class="s2">o</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>To build our flake, run <code>nix build .</code></p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
<code>.</code> means the flake at your CWD</p>
<p>Important note: if your flake is inside a git repository, nix will ignore uncommited files
</div>
</p>
<h3 id="nixos">NixOS</h3>
<p>NixOS is a complete linux distribution based on nix.</p>
<p>Instead of installing and configuring packages by hand, the complete system configuration is a function with the nixpkgs and your configuration as their inputs and a script to set the new system state (the activation script) as it’s output. (the output is more complicated then just the activation script, but we’ll ignore that for this post)</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
It dosen’t have to be just the activation script though, you can also tell nix to create a iso/img/qcow2/ami of your configuration
</div>
</p>
<p>This makes NixOS (almost) completely reproducible.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
to get all of your applications/configuration onto a new computer you just have to run <code>nixos-rebuild</code> on it and you’re done (except copying over any data, but you get the point)
</div>
</p>
<p>It also allows you to rollback to your last configuration if anything goes wrong.</p>
<h3 id="nix-darwin">nix-darwin</h3>
<p>nix-darwin ported the concept (and much of the code) of NixOS to macOS, so you can configure most of your mac with nix.</p>
<h3 id="home-manager">home-manager</h3>
<p>home-manager is similar to nix-darwin, but it “just” manages programs/configuration inside your users home directory.
It is (mostly) cross compatible between NixOS, other linux distributions and macOS.</p>
<h2 id="some-setup">some setup</h2>
<p><strong>Before doing any of this, please make sure you have a backup!!!</strong></p>
<p><code>nix</code> has to be installed to use any of this, so please follow the instructions <a href="https://nixos.org/download.html#nix-install-macos" target="_blank">here</a> to install <code>nix</code>.</p>
<h2 id="mac-configuration">mac configuration</h2>
<p>We’ll need a flake to put all our configuration into. So create a new git repository somewhere and run <code>nix flake init</code> in there.</p>
<p>To use <code>nix-darwin</code> and <code>home-manager</code> we first have to add their respective inputs and add them to the arguments of our output function.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">description</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">M</span><span class="s2">y</span><span class="s2"> </span><span class="s2">f</span><span class="s2">i</span><span class="s2">r</span><span class="s2">s</span><span class="s2">t</span><span class="s2"> </span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2"> </span><span class="s2">f</span><span class="s2">l</span><span class="s2">a</span><span class="s2">k</span><span class="s2">e</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">inputs</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">nixpkgs</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">g</span><span class="s2">i</span><span class="s2">t</span><span class="s2">h</span><span class="s2">u</span><span class="s2">b</span><span class="s2">:</span><span class="s2">N</span><span class="s2">i</span><span class="s2">x</span><span class="s2">O</span><span class="s2">S</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">p</span><span class="s2">k</span><span class="s2">g</span><span class="s2">s</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">p</span><span class="s2">k</span><span class="s2">g</span><span class="s2">s</span><span class="s2">-</span><span class="s2">2</span><span class="s2">3</span><span class="s2">.</span><span class="s2">0</span><span class="s2">5</span><span class="s2">-</span><span class="s2">d</span><span class="s2">a</span><span class="s2">r</span><span class="s2">w</span><span class="s2">i</span><span class="s2">n</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># newest version as of may 2023, probably needs to get updated in november</span>
</span></span><span class="line"><span class="cl"> <span class="n">home-manager</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">g</span><span class="s2">i</span><span class="s2">t</span><span class="s2">h</span><span class="s2">u</span><span class="s2">b</span><span class="s2">:</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">-</span><span class="s2">c</span><span class="s2">o</span><span class="s2">m</span><span class="s2">m</span><span class="s2">u</span><span class="s2">n</span><span class="s2">i</span><span class="s2">t</span><span class="s2">y</span><span class="s2">/</span><span class="s2">h</span><span class="s2">o</span><span class="s2">m</span><span class="s2">e</span><span class="s2">-</span><span class="s2">m</span><span class="s2">a</span><span class="s2">n</span><span class="s2">a</span><span class="s2">g</span><span class="s2">e</span><span class="s2">r</span><span class="s2">/</span><span class="s2">r</span><span class="s2">e</span><span class="s2">l</span><span class="s2">e</span><span class="s2">a</span><span class="s2">s</span><span class="s2">e</span><span class="s2">-</span><span class="s2">2</span><span class="s2">3</span><span class="s2">.</span><span class="s2">0</span><span class="s2">5</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl"> <span class="n">home-manager</span><span class="o">.</span><span class="n">inputs</span><span class="o">.</span><span class="n">nixpkgs</span><span class="o">.</span><span class="n">follows</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">p</span><span class="s2">k</span><span class="s2">g</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># nix will normally use the nixpkgs defined in home-managers inputs, we only want one copy of nixpkgs though</span>
</span></span><span class="line"><span class="cl"> <span class="n">darwin</span><span class="o">.</span><span class="n">url</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">g</span><span class="s2">i</span><span class="s2">t</span><span class="s2">h</span><span class="s2">u</span><span class="s2">b</span><span class="s2">:</span><span class="s2">l</span><span class="s2">n</span><span class="s2">l</span><span class="s2">7</span><span class="s2">/</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">-</span><span class="s2">d</span><span class="s2">a</span><span class="s2">r</span><span class="s2">w</span><span class="s2">i</span><span class="s2">n</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">darwin</span><span class="o">.</span><span class="n">inputs</span><span class="o">.</span><span class="n">nixpkgs</span><span class="o">.</span><span class="n">follows</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">n</span><span class="s2">i</span><span class="s2">x</span><span class="s2">p</span><span class="s2">k</span><span class="s2">g</span><span class="s2">s</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># add the inputs declared above to the argument attribute set</span>
</span></span><span class="line"><span class="cl"> <span class="n">outputs</span> <span class="o">=</span> <span class="p">{</span> <span class="n">self</span><span class="o">,</span> <span class="n">nixpkgs</span><span class="o">,</span> <span class="n">home-manager</span><span class="o">,</span> <span class="n">darwin</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># we want `nix-darwin` and not gnu hello, so the packages stuff can go</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>To actually have a nix-darwin configuration, we’ll need a <code>darwinConfiguration</code> output.</p>
<p>So let’s add one</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="n">outputs</span> <span class="err">=</span> <span class="p">{</span> <span class="n">self</span><span class="o">,</span> <span class="n">nixpkgs</span><span class="o">,</span> <span class="n">home-manager</span><span class="o">,</span> <span class="n">darwin</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">darwinConfigurations</span><span class="o">.</span><span class="s2">"</span><span class="s2">Y</span><span class="s2">o</span><span class="s2">u</span><span class="s2">r</span><span class="s2">H</span><span class="s2">o</span><span class="s2">s</span><span class="s2">t</span><span class="s2">N</span><span class="s2">a</span><span class="s2">m</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span> <span class="n">darwin</span><span class="o">.</span><span class="n">lib</span><span class="o">.</span><span class="n">darwinSystem</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># you can have multiple darwinConfigurations per flake, one per hostname</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">system</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">a</span><span class="s2">a</span><span class="s2">r</span><span class="s2">c</span><span class="s2">h</span><span class="s2">6</span><span class="s2">4</span><span class="s2">-</span><span class="s2">d</span><span class="s2">a</span><span class="s2">r</span><span class="s2">w</span><span class="s2">i</span><span class="s2">n</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># "x86_64-darwin" if you're using a pre M1 mac</span>
</span></span><span class="line"><span class="cl"> <span class="n">modules</span> <span class="o">=</span> <span class="p">[</span> <span class="sr">./hosts/YourHostName/default.nix</span> <span class="p">]</span><span class="p">;</span> <span class="c1"># will be important later</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="p">;</span>
</span></span></code></pre>
<p>I like to have a folder for each computer inside of the flake for system specific configuration.
So let’s <code>mkdir -p hosts/YourHostName</code> and create a <code>default.nix</code> there.</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1"># hosts/YourHostName/default.nix</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">.</span><span class="o">.</span><span class="o">.</span> <span class="p">}:</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Make sure the nix daemon always runs</span>
</span></span><span class="line"><span class="cl"> <span class="n">services</span><span class="o">.</span><span class="n">nix-daemon</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># Installs a version of nix, that dosen't need "experimental-features = nix-command flakes" in /etc/nix/nix.conf</span>
</span></span><span class="line"><span class="cl"> <span class="n">services</span><span class="o">.</span><span class="n">nix-daemon</span><span class="o">.</span><span class="n">package</span> <span class="o">=</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">nixFlakes</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># if you use zsh (the default on new macOS installations),</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># you'll need to enable this so nix-darwin creates a zshrc sourcing needed environment changes</span>
</span></span><span class="line"><span class="cl"> <span class="n">programs</span><span class="o">.</span><span class="n">zsh</span><span class="o">.</span><span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># bash is enabled by default</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>If you used NixOS before, this file is like a NixOS <code>configuration.nix</code>, just with fewer and partly different options.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
You can find all options in the <a href="https://daiderd.com/nix-darwin/manual/index.html#sec-options" target="_blank">documentation</a>
</div>
</p>
<p>A few options I want to make sure everybody using <code>nix-darwin</code> knows of are <code>homebrew</code> and <code>system.defaults</code>.</p>
<h3 id="homebrew">homebrew</h3>
<p>The <code>homebrew</code> module lets you install software from <a href="https://brew.sh" target="_blank">brew.sh</a> declaratively, which makes it much less a chore to deal with.</p>
<p>Using <code>nix</code> and something like <code>homebrew</code> or <code>macports</code> together it kinda required IMO, because most GUI applications for mac aren’t inside nixpkgs (as nixpkgs can’t legally ship Xcode, which is needed for mac native GUI stuff)</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
The module just runs a system installed <code>brew</code> inside the activation script (meaning you’ll have to install homebrew beforehand)
</div>
</p>
<p>So if you want to install your favorite casks, you can just</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1"># hosts/YourHostName/default.nix - inside the returning attribute set</span>
</span></span><span class="line"><span class="cl"><span class="n">homebrew</span> <span class="err">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">autoUpdate</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># updates homebrew packages on activation,</span>
</span></span><span class="line"><span class="cl"> <span class="c1"># can make darwin-rebuild much slower (otherwise i'd forget to do it ever though)</span>
</span></span><span class="line"><span class="cl"> <span class="n">casks</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">h</span><span class="s2">a</span><span class="s2">m</span><span class="s2">m</span><span class="s2">e</span><span class="s2">r</span><span class="s2">s</span><span class="s2">p</span><span class="s2">o</span><span class="s2">o</span><span class="s2">n</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">a</span><span class="s2">m</span><span class="s2">e</span><span class="s2">t</span><span class="s2">h</span><span class="s2">y</span><span class="s2">s</span><span class="s2">t</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">a</span><span class="s2">l</span><span class="s2">f</span><span class="s2">r</span><span class="s2">e</span><span class="s2">d</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">l</span><span class="s2">o</span><span class="s2">g</span><span class="s2">s</span><span class="s2">e</span><span class="s2">q</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">d</span><span class="s2">i</span><span class="s2">s</span><span class="s2">c</span><span class="s2">o</span><span class="s2">r</span><span class="s2">d</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="s2">"</span><span class="s2">i</span><span class="s2">i</span><span class="s2">n</span><span class="s2">a</span><span class="s2">"</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="p">;</span>
</span></span></code></pre>
<p>inside your host configuration</p>
<h3 id="system-defaults">system.defaults</h3>
<p>The <code>system.defaults</code> module allows you to set macOS settings.
(defaults(1) is the macOS/NeXTStep central user configuration system, like GNOME dconf or the Windows Registry)</p>
<p>For example you can set <code>system.defaults.dock.autohide = true;</code> to autohide the dock.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
All supported options are ofc. listed inside the <a href="https://daiderd.com/nix-darwin/manual/index.html#opt-system.defaults..GlobalPreferences.com.apple.sound.beep.sound" target="_blank">options documentation</a>
</div>
</p>
<h2 id="home-manager-1">home-manager</h2>
<p>I wanted to use <code>nix-darwin</code> together with <code>home-manager</code>, so I can code-share between my servers and my laptop.
You don’t have to do this though.
If your only computer is your mac, just use nix-darwin by itself.</p>
<p>To use it, we’ll have to add the <code>home-manager</code> module to our <code>darwinConfiguration</code></p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1"># flake.nix</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># ...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">outputs</span> <span class="o">=</span> <span class="p">{</span> <span class="n">self</span><span class="o">,</span> <span class="n">nixpkgs</span><span class="o">,</span> <span class="n">home-manager</span><span class="o">,</span> <span class="n">darwin</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1"># Deleted the `packages` and `defaultPackage` outputs, as they aren't needed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">darwinConfiguration</span><span class="o">.</span><span class="s2">"</span><span class="s2">Y</span><span class="s2">o</span><span class="s2">u</span><span class="s2">r</span><span class="s2">H</span><span class="s2">o</span><span class="s2">s</span><span class="s2">t</span><span class="s2">N</span><span class="s2">a</span><span class="s2">m</span><span class="s2">e</span><span class="s2">"</span> <span class="o">=</span> <span class="n">darwin</span><span class="o">.</span><span class="n">lib</span><span class="o">.</span><span class="n">darwinSystem</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="n">system</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">a</span><span class="s2">a</span><span class="s2">r</span><span class="s2">c</span><span class="s2">h</span><span class="s2">6</span><span class="s2">4</span><span class="s2">-</span><span class="s2">d</span><span class="s2">a</span><span class="s2">r</span><span class="s2">w</span><span class="s2">i</span><span class="s2">n</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># "x86_64-darwin" if you're using a pre M1 mac</span>
</span></span><span class="line"><span class="cl"> <span class="n">modules</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="n">home-manager</span><span class="o">.</span><span class="n">darwinModules</span><span class="o">.</span><span class="n">home-manager</span>
</span></span><span class="line"><span class="cl"> <span class="sr">./hosts/YourHostName/default.nix</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">;</span> <span class="c1"># will be important later</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre>
<p>Now we can use <code>home-manager</code> in our system configuration, just like on NixOS</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1"># hosts/YourHostName/default.nix - inside the returning attribute set</span>
</span></span><span class="line"><span class="cl"><span class="n">home-manager</span><span class="o">.</span><span class="n">useGlobalPkgs</span> <span class="err">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">home-manager</span><span class="o">.</span><span class="n">useUserPackages</span> <span class="err">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="n">home-manager</span><span class="o">.</span><span class="n">users</span><span class="o">.</span><span class="n">YourUserName</span> <span class="err">=</span> <span class="p">{</span> <span class="n">pkgs</span><span class="o">,</span> <span class="o">.</span><span class="o">.</span><span class="o">.</span> <span class="p">}:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">stateVersion</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">2</span><span class="s2">3</span><span class="s2">.</span><span class="s2">0</span><span class="s2">5</span><span class="s2">"</span><span class="p">;</span> <span class="c1"># read below</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="n">programs</span><span class="o">.</span><span class="n">tmux</span> <span class="o">=</span> <span class="p">{</span> <span class="c1"># my tmux configuration, for example</span>
</span></span><span class="line"><span class="cl"> <span class="n">enable</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">keyMode</span> <span class="o">=</span> <span class="s2">"</span><span class="s2">v</span><span class="s2">i</span><span class="s2">"</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">clock24</span> <span class="o">=</span> <span class="no">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">historyLimit</span> <span class="o">=</span> <span class="mi">10000</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">plugins</span> <span class="o">=</span> <span class="k">with</span> <span class="n">pkgs</span><span class="o">.</span><span class="n">tmuxPlugins</span><span class="p">;</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl"> <span class="n">vim-tmux-navigator</span>
</span></span><span class="line"><span class="cl"> <span class="n">gruvbox</span>
</span></span><span class="line"><span class="cl"> <span class="p">]</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="n">extraConfig</span> <span class="o">=</span> <span class="s1">''</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">n</span><span class="s1">e</span><span class="s1">w</span><span class="s1">-</span><span class="s1">s</span><span class="s1">e</span><span class="s1">s</span><span class="s1">s</span><span class="s1">i</span><span class="s1">o</span><span class="s1">n</span><span class="s1"> </span><span class="s1">-</span><span class="s1">s</span><span class="s1"> </span><span class="s1">m</span><span class="s1">a</span><span class="s1">i</span><span class="s1">n</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">b</span><span class="s1">i</span><span class="s1">n</span><span class="s1">d</span><span class="s1">-</span><span class="s1">k</span><span class="s1">e</span><span class="s1">y</span><span class="s1"> </span><span class="s1">-</span><span class="s1">n</span><span class="s1"> </span><span class="s1">C</span><span class="s1">-</span><span class="s1">a</span><span class="s1"> </span><span class="s1">s</span><span class="s1">e</span><span class="s1">n</span><span class="s1">d</span><span class="s1">-</span><span class="s1">p</span><span class="s1">r</span><span class="s1">e</span><span class="s1">f</span><span class="s1">i</span><span class="s1">x</span><span class="s1">
</span></span></span><span class="line"><span class="cl"><span class="s1"></span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1"> </span><span class="s1">''</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="p">;</span>
</span></span></code></pre>
<p>The <code>stateVersion</code> attribute is described <a href="https://search.nixos.org/options?channel=22.05&show=system.stateVersion&from=0&size=50&sort=relevance&type=packages&query=system.stateVersion" target="_blank">here</a></p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
All the home-manager options are listed in their <a href="https://nix-community.github.io/home-manager/options.html" target="_blank">documentation</a></p>
<p>home-manager <code>services</code> only work on linux though (as they use systemd, which is a linux only thing)
</div>
</p>
<h2 id="installation">Installation</h2>
<p>To install our newly created <code>nix-darwin</code> configuration, we have to first build it, and then run darwin-rebuild from there.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
There is also a nix-darwin installer. It dosen’t work with flakes though.
</div>
</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="c1"># builds the darwinConfiguration.</span>
</span></span><span class="line"><span class="cl"><span class="c1"># after insalling nix-darwin, we won't need to specify extra-experimental-features anymore</span>
</span></span><span class="line"><span class="cl">nix build .#darwinConfigurations.YourHostName.system --extra-experimental-features <span class="s2">"nix-command flakes"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># the plan is to now run this to install nix-darwin with our configuration</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ./result/sw/bin/darwin-rebuild switch --flake . # this will fail as we first have to do the following lines</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">printf</span> <span class="s1">'run\tprivate/var/run\n'</span> <span class="p">|</span> sudo tee -a /etc/synthetic.conf <span class="c1"># read below</span>
</span></span><span class="line"><span class="cl">/System/Library/Filesystems/apfs.fs/Contents/Resources/apfs.util -t <span class="c1"># read below</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># now we can finally darwin-rebuild</span>
</span></span><span class="line"><span class="cl">./result/sw/bin/darwin-rebuild switch --flake . <span class="c1"># install nix-darwin</span>
</span></span></code></pre>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
macOS dosen’t allow any software to write to <code>/</code>.
Instead you can write directory names or symlinks to <code>/etc/synthetic.conf</code>.</p>
<p>macOS will then create those files/symlinks on boot. (rebooting is boring, so we’ll just run <code>apfs.util -t</code> to create them immediately)</p>
<p>nix itself has just “nix” inside <code>/etc/synthetic.conf</code> (an empty folder at <code>/nix</code>), it’ll then mount an apfs volume containing your nix store above it.</p>
<p>nix-darwin needs a symlink from <code>/run</code> to <code>/private/var/run</code> to function, that’s whats added in the printf|tee line
</div>
</p>
<p>Your shell needs to source an rc file from nix-darwin to set up your environment variables. <code>/etc/static/zshrc</code> for zsh and <code>/etc/static/bashrc</code> for bash.</p>
<blockquote>
<p>that’s what the <code>programs.zsh.enable</code> was for</p>
</blockquote>
<p>so run</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">'if test -e /etc/static/bashrc; then . /etc/static/bashrc; fi'</span> <span class="p">|</span> sudo tee -a /etc/bashrc
</span></span></code></pre>
<p>on bash and</p>
<pre tabindex="0" class="chroma"><code><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">'if test -e /etc/static/zshrc; then . /etc/static/zshrc; fi'</span> <span class="p">|</span> sudo tee -a /etc/zshrc
</span></span></code></pre>
<p>on zsh.
You may have to rerun this on macOS updates.</p>
<p><br></p>
<p>If you make changes to your configuration, you just have to commit them and run <code>darwin-rebuild switch --flake .</code> inside the repo.</p>
<p>
<div class="border-2 px-5 py-2 my-2 dark:text-light3 text-l_subtext1 border-l_surface2 dark:border-light3">
<div class="block -mt-5 px-2 dark:bg-dark0 bg-l_base w-min">tangent</div>
<code>nixpkgs</code> and <code>home-manager</code> release new (stable) versions twice a year, currently in May and November. that’s what the <code>23.05</code> in flake inputes is for.</p>
<p>To update or install a newer version you have to increase the version number, do <code>nix flake update</code> and <code>darwin-rebuild</code></p>
<p>Doing an occasional <code>nix flake update</code> is wise
</div>
</p>
<hr/>
<p>I hope this post will guide some of you to the <a href="https://www.reddit.com/r/NixOS/comments/kauf1m/dealing_with_post_nixflake_god_complex/" target="_blank">god complex</a>.</p>
<p>When I find the time, I’ll try guiding you to manage multiple systems in a single flake.</p>
<p><br></p>
<p>You can find all the code of this post <a href="https://github.com/thexyno/blogpages/tree/main/nix-darwin-introduction" target="_blank">here</a>.</p>
<p>And my NixOS/nix-darwin configurations <a href="https://github.com/thexyno/nixos-config" target="_blank">here</a></p>