index.html (19026B)
1 <!DOCTYPE html> 2 3 <html lang="en"> 4 5 <head> 6 <meta charset="utf-8"> 7 <meta http-equiv="content-type" content="text/html; charset=utf-8" /> 8 9 <link rel="start" href="https://vincent.demeester.fr" /> 10 11 <title>Vincent Demeester</title> 12 <link rel="canonical" href="https://vincent.demeester.fr/posts/2019-01-26-nix-run-alias/"> 13 <link href="https://vincent.demeester.fr/index.xml" rel="alternate" type="application/rss+xml" title="Vincent Demeester" /> 14 15 <link rel="openid.server" href="https://indieauth.com/openid" /> 16 <link rel="openid.delegate" href="http://vincent.demeester.fr/" /> 17 <link rel="shortcut icon" type="image/x-icon" href="favicon.ico"> 18 19 <link rel="stylesheet" href="/css/screen.css" type="text/css" /> 20 <link rel="stylesheet" href="/css/sbrain.css" type="text/css" /> 21 <link rel="stylesheet" href="/css/syntax.css" type="text/css" /> 22 23 </head> 24 25 <body lang=""/> 26 27 28 29 30 31 32 <div id="main-container"> 33 <div id="page"> 34 <article class="post"> 35 <header> 36 <h1 class="emphnext">nix run aliases</h1><a href='https://vincent.demeester.fr/posts/2019-01-26-nix-run-alias/'></a> 37 <address class="signature"> 38 <span class="date">Sat, 26 January, 2019</span> 39 <span class="words">(1300 Words)</span> 40 </address> 41 <ul class="tag_box inline"> 42 43 44 45 46 47 <li class="tag tag-nixos"><a href="/tags/#nixos">nixos<span>1</span></a></li> 48 49 50 <li class="tag tag-fish"><a href="/tags/#fish">fish<span>1</span></a></li> 51 52 53 <li class="tag tag-alias"><a href="/tags/#alias">alias<span>1</span></a></li> 54 55 56 <li class="tag tag-nix"><a href="/tags/#nix">nix<span>3</span></a></li> 57 58 59 <li class="tag tag-shell"><a href="/tags/#shell">shell<span>2</span></a></li> 60 61 62 <li class="tag tag-home"><a href="/tags/#home">home<span>1</span></a></li> 63 64 65 <li class="tag tag-manager"><a href="/tags/#manager">manager<span>1</span></a></li> 66 67 <br/> 68 69 </ul> 70 </header> 71 72 73 74 75 76 <p>I use <a href="https://nixos.org/"><code>NixOS</code></a> each and every day, everywhere. One really cool feature of <code>nix</code> is 77 <code>nix-shell</code> and more recently (with <code>nix</code> >= <code>2.0.0</code>), <code>nix run</code>.</p> 78 <div class="highlight"><pre class="chroma"><code class="language-man" data-lang="man">Usage: nix run <FLAGS>... <INSTALLABLES>... 79 80 Summary: run a shell in which the specified packages are available. 81 82 Flags: 83 --arg <NAME> <EXPR> argument to be passed to Nix functions 84 --argstr <NAME> <STRING> string-valued argument to be passed to Nix functions 85 -c, --command <COMMAND> <ARGS> command and arguments to be executed; defaults to 'bash' 86 -f, --file <FILE> evaluate FILE rather than the default 87 -i, --ignore-environment clear the entire environment (except those specified with --keep) 88 -I, --include <PATH> add a path to the list of locations used to look up <...> file names 89 -k, --keep <NAME> keep specified environment variable 90 -u, --unset <NAME> unset specified environment variable 91 92 Examples: 93 94 To start a shell providing GNU Hello from NixOS 17.03: 95 $ nix run -f channel:nixos-17.03 hello 96 97 To start a shell providing youtube-dl from your 'nixpkgs' channel: 98 $ nix run nixpkgs.youtube-dl 99 100 To run GNU Hello: 101 $ nix run nixpkgs.hello -c hello --greeting 'Hi everybody!' 102 103 To run GNU Hello in a chroot store: 104 $ nix run --store ~/my-nix nixpkgs.hello -c hello 105 106 Note: this program is EXPERIMENTAL and subject to change.</code></pre></div> 107 <p>As you can see from the <code>-h</code> summary, it makes it really easy to run a shell or a command 108 with some packages that are not in your main configuration. It will download the 109 package(s) if there are not available in the Nix store (<code>/nix/store/</code>).</p> 110 111 <p>A few month ago I decided it would be a perfect use-case for command I do not run 112 often. My idea was, let’s define <code>aliases</code> (in the shell) that would make a simple command 113 call, like <code>ncdu</code>, become <code>nix run nixpkgs.ncdu -c ndcu</code>. My <em>shell of choice</em> is <a href="https://fishshell.com/">fish</a>, so 114 I decided to dig into the <em>language</em> in order to implement that.</p> 115 116 <p>The use case is the following :</p> 117 118 <ul> 119 <li>When I type <code>foo</code>, I want the command <code>foo</code> in package <code>bar</code> to be executed.</li> 120 <li>I want to be able to pin a channel for the package — I’m using <a href="https://matthewbauer.us/">Matthew Bauer</a> <a href="https://matthewbauer.us/blog/channel-changing.html">Channel 121 Changing with Nix</a> setup for pin-pointing a given channel.</li> 122 </ul> 123 124 <h2 id="fish-aliases-experimentation">Fish aliases experimentation</h2> 125 126 <p>I had a feeling the built-in <code>alias</code> would not work so I ended up trying to define a 127 <em>dynamic</em> function that would be the name of the command. That’s the beauty of the shell, 128 everything is a command, even function appears as commands. If you define a function 129 <code>foo()</code>, you will be able to run <code>foo</code> in your shell, <strong>and</strong> it will take precedence over 130 the <code>foo</code> executable file that would be in your <code>PATH</code>.</p> 131 132 <p>I ended up with two main helper function that would create those <em>alias</em> function.</p> 133 <div class="highlight"><pre class="chroma"><code class="language-fish" data-lang="fish"><span class="k">function</span> _nix_run_package 134 <span class="k">set</span> -l s <span class="nv">$argv</span><span class="o">[</span>1<span class="o">]</span> 135 <span class="k">set</span> -l package <span class="o">(</span>string split <span class="s2">":"</span> <span class="nv">$s</span><span class="o">)</span> 136 <span class="k">switch</span> <span class="o">(</span><span class="k">count</span> <span class="nv">$package</span><span class="o">)</span> 137 <span class="k">case</span> 1 138 _nix_run <span class="nv">$s</span> <span class="nv">$s</span> <span class="nv">$argv</span><span class="o">[</span>2<span class="o">]</span> <span class="nv">$argv</span><span class="o">[</span>3<span class="o">]</span> 139 <span class="k">case</span> 2 140 _nix_run <span class="nv">$package</span><span class="o">[</span>1<span class="o">]</span> <span class="nv">$package</span><span class="o">[</span>2<span class="o">]</span> <span class="nv">$argv</span><span class="o">[</span>2<span class="o">]</span> <span class="nv">$argv</span><span class="o">[</span>3<span class="o">]</span> 141 <span class="k">end</span> 142 <span class="k">end</span> 143 144 <span class="k">function</span> _nix_run 145 <span class="k">set</span> -l c <span class="nv">$argv</span><span class="o">[</span>1<span class="o">]</span> 146 <span class="k">set</span> -l p <span class="nv">$argv</span><span class="o">[</span>2<span class="o">]</span> 147 <span class="k">set</span> -l channel <span class="nv">$argv</span><span class="o">[</span>3<span class="o">]</span> 148 <span class="k">set</span> -l channelsfile <span class="nv">$argv</span><span class="o">[</span>4<span class="o">]</span> 149 <span class="k">function</span> <span class="nv">$c</span> --inherit-variable c --inherit-variable p --inherit-variable channel --inherit-variable channelsfile 150 <span class="k">set</span> -l cmd nix run 151 <span class="k">if</span> <span class="k">test</span> -n <span class="s2">"</span><span class="nv">$channelsfile</span><span class="s2">"</span> 152 <span class="k">set</span> cmd <span class="nv">$cmd</span> -f <span class="nv">$channelsfile</span> 153 <span class="k">end</span> 154 <span class="nb">eval</span> <span class="nv">$cmd</span> <span class="nv">$channel</span>.<span class="nv">$p</span> -c <span class="nv">$c</span> <span class="nv">$argv</span> 155 <span class="k">end</span> 156 <span class="k">end</span></code></pre></div> 157 <p>In a nutshell, <code>_nix_run</code> is the function that create the alias function. There is so 158 condition in there depending on whether we gave it a channel or not. So, a call like 159 <code>_nix_run foo bar unstable channels.nix</code> would, in the end generate a function <code>foo</code> with 160 the following call : <code>nix run -f channels.nix unstable.bar -c foo</code>.</p> 161 162 <p>The other function, <code>_nix_run_package</code> is there to make me write less when I define those 163 aliases — aka if the command and the package share the same name, I don’t want to write it 164 twice. So, a call like <code>_nix_run_package foo nixpkgs</code> would result in a <code>_nix_run foo foo 165 nixpkgs</code>, whereas a call like <code>_nix_run_package foo:bar unstable channels.nix</code> would 166 result in a <code>_nix_run foo bar unstable channels.nix</code>.</p> 167 168 <p>An example is gonna be better than the above paragraphs. This is what I used to have in my 169 fish configuration.</p> 170 <div class="highlight"><pre class="chroma"><code class="language-fish" data-lang="fish"><span class="k">function</span> _def_nix_run_aliases 171 <span class="k">set</span> -l stable mr sshfs ncdu wakeonlan:python36Packages.wakeonlan lspci:pciutils lsusb:usbutils beet:beets gotop virt-manager:virtmanager pandoc nix-prefetch-git:nix-prefetch-scripts nix-prefetch-hg:nix-prefetch-scripts 172 <span class="k">set</span> -l unstable op:_1password update-desktop-database:desktop-file-utils lgogdownloader 173 <span class="k">for</span> s <span class="k">in</span> <span class="nv">$stable</span> 174 _nix_run_package <span class="nv">$s</span> nixpkgs 175 <span class="k">end</span> 176 <span class="k">for</span> s <span class="k">in</span> <span class="nv">$unstable</span> 177 _nix_run_package <span class="nv">$s</span> unstable ~/.config/nixpkgs/channels.nix 178 <span class="k">end</span> 179 <span class="k">end</span> 180 <span class="c"># Call the function to create the aliases 181 </span><span class="c"></span>_def_nix_run_aliases</code></pre></div> 182 <p>This works like a charm, and for a while, I was happy. But I soon realized something : I’m 183 not always on my shell — like, I tend to spend more and more time in <code>eshell</code>. This also 184 doesn’t work with graphic tools like <a href="https://github.com/DaveDavenport/rofi"><code>rofi</code></a>. I needed actual command, so that external 185 tools would benefit from that. I ended up writing a small tool, <a href="https://github.com/vdemeester/nr"><code>nr</code></a> that integrates 186 nicely with <code>nix</code> and <a href="https://github.com/rycee/home-manager"><code>home-manager</code></a>.</p> 187 188 <h2 id="a-proper-tool-nr">A proper tool : <code>nr</code></h2> 189 190 <p>The gist for this tool is simple :</p> 191 192 <ul> 193 <li>create an executable script that will call <code>nix run ...</code> instead of the command</li> 194 <li>as for the above fish script, support different channels</li> 195 <li>make sure we don’t have conflicts — if the command already exists, then don’t create the 196 command</li> 197 </ul> 198 199 <p>The <code>nr</code> tool would have to be able to manage multiple <em>profile</em>, which really stands for 200 multiple file. The main reason is really about how I manage my configuration ; To make it 201 simple, depending on the computer my configurations are setup, I may not have <code>go</code>, thus I 202 don’t want any <code>go</code>-related aliases for a computer that doesn’t have <code>go</code> (using <code>go</code> here 203 but you can replace with anything).</p> 204 <div class="highlight"><pre class="chroma"><code class="language-fish" data-lang="fish"><span class="nv">$ </span>nr default 205 <span class="o">></span> nr generate default 206 <span class="o">></span> virtmanager already exists 207 <span class="nv">$ </span>nr git 208 <span class="o">></span> nr generate git</code></pre></div> 209 <p><code>nr</code> generates a bash script that does the <code>nr run …</code> and mark it as executable. <code>nr</code> 210 needs to be able to clean files it has generated (in case we removed it from 211 aliases). Thus, I went for a really naive comment in the script. When generating a new set 212 of commands, <code>nr</code> will first remove previously generated script for this profile, and for 213 that, it uses the comment. Let’s look at what a generated script looks like, for the 214 default profile.</p> 215 <div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash"><span class="cp">#!/usr/bin/env bash 216 </span><span class="cp"></span><span class="c1"># Generated by nr default</span> 217 nix run nixpkgs.nix-prefetch-scripts -c nix-prefetch-git <span class="nv">$@</span></code></pre></div> 218 <p>The format used in <code>nr</code> is <code>json</code>. I’m not a <em>huge fan</em> of <code>json</code> but it really was the 219 best format to use for this tool. The reason to use <code>json</code> are simple :</p> 220 221 <ul> 222 <li><p>Go has <code>encoding/json</code> built-in, so it’s really easy to <code>Marshall</code> and <code>Unmarshall</code> 223 structure.</p> 224 <div class="highlight"><pre class="chroma"><code class="language-go" data-lang="go"><span class="kd">type</span> <span class="nx">alias</span> <span class="kd">struct</span> <span class="p">{</span> 225 <span class="nx">Command</span> <span class="kt">string</span> <span class="s">`json:"cmd"`</span> 226 <span class="nx">Package</span> <span class="kt">string</span> <span class="s">`json:"pkg"`</span> 227 <span class="nx">Channel</span> <span class="kt">string</span> <span class="s">`json:"chan"`</span> 228 <span class="p">}</span></code></pre></div></li> 229 230 <li><p>Nix also has built-in support for <code>json</code> : <code>builtins.toJSON</code> will marshall a <em>struct</em> 231 into a json file.</p></li> 232 </ul> 233 234 <p>Finally, to avoid conflicts at <em>build time</em> (<code>home-manager switch</code>) I couldn’t use/define 235 a nix package, but to execute command(s) at the end of the build. One way to achieve it is 236 to use <code>file.?.onChange</code> script, which is executed after <a href="https://github.com/rycee/home-manager"><code>home-manager</code></a> has updated the 237 environment, <strong>if</strong> the file has changed. That means it’s possible to check for executable 238 files in <code>~/.nix-profile/bin/</code> for defined aliases and create those that are not there, 239 with <code>nr</code>. My configuration then looks like the following.</p> 240 <div class="highlight"><pre class="chroma"><code class="language-nix" data-lang="nix"><span class="n">xdg</span><span class="o">.</span><span class="n">configFile</span><span class="o">.</span><span class="s2">"nr/default"</span> <span class="err">=</span> <span class="p">{</span> 241 <span class="n">text</span> <span class="o">=</span> <span class="nb">builtins</span><span class="o">.</span><span class="n">toJSON</span> <span class="p">[</span> 242 <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"ncdu"</span><span class="p">;}</span> <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"sshfs"</span><span class="p">;}</span> <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"gotop"</span><span class="p">;}</span> <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"pandoc"</span><span class="p">;}</span> 243 <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"wakeonlan"</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">"python36Packages.wakeonlan"</span><span class="p">;}</span> 244 <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"beet"</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">"beets"</span><span class="p">;}</span> 245 <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"virt-manager"</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">"virtmanager"</span><span class="p">;}</span> 246 <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"nix-prefetch-git"</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">"nix-prefetch-scripts"</span><span class="p">;}</span> 247 <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">"nix-prefetch-hg"</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">"nix-prefetch-scripts"</span><span class="p">;}</span> 248 <span class="p">];</span> 249 <span class="n">onChange</span> <span class="o">=</span> <span class="s2">"</span><span class="si">${</span><span class="n">pkgs</span><span class="o">.</span><span class="n">nur</span><span class="o">.</span><span class="n">repos</span><span class="o">.</span><span class="n">vdemeester</span><span class="o">.</span><span class="n">nr</span><span class="si">}</span><span class="s2">/bin/nr default"</span><span class="p">;</span> 250 <span class="p">};</span></code></pre></div> 251 <p>And there you are, now, each time I update my environment (<code>home-manager switch</code>), <code>nr</code> 252 will regenerate my <code>nix run</code> aliases.</p> 253 254 255 </article> 256 <hr /> 257 <div class="prev-next"> 258 259 <a class="paging-link prev" href="/posts/2019-01-27-link/" title="Self-care tips for new developers">← Previous post</a> 260 261 262 263 <a class="paging-link next" href="/posts/2019-01-20-2018-year-review/" title="2018 year review">Next post →</a> 264 265 </div> 266 267 </div> 268 </div> 269 270 <footer> 271 <nav> 272 273 <a href="/">home</a> 274 <span class="text-muted"> | </span> 275 276 <a href="/about">about</a> 277 <span class="text-muted"> | </span> 278 279 <a href="/archive">archive</a> 280 <span class="text-muted"> | </span> 281 282 <a href="/categories">categories</a> 283 <span class="text-muted"> | </span> 284 285 <a href="/tags">tags</a> 286 <span class="text-muted"> | </span> 287 288 <a href="https://twitter.com/vdemeest">twitter</a> 289 <span class="text-muted"> | </span> 290 291 <a href="https://github.com/vdemeester">github</a> 292 <span class="text-muted"> | </span> 293 294 <a href="https://vincent.demeester.fr/index.xml">rss</a> 295 </nav> 296 <br/> 297 <address> 298 <span class="copyright"> 299 Content and design by Vincent Demeester 300 (<a rel="licence" href="http://creativecommons.org/licenses/by-nc-sa/3.0/">Some rights reserved</a>) 301 </span><br /> 302 <span class="engine"> 303 Powered by <a href="https://gohugo.io/">Hugo</a> and <a href="https://github.com/kaushalmodi/ox-hugo/">ox-hugo</a> 304 </span> 305 </address> 306 </footer> 307 </body> 308