www

My personal website(s)
Log | Files | Refs

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> &gt;= <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 &lt;FLAGS&gt;... &lt;INSTALLABLES&gt;...
     79 
     80 Summary: run a shell in which the specified packages are available.
     81 
     82 Flags:
     83       --arg &lt;NAME&gt; &lt;EXPR&gt;         argument to be passed to Nix functions
     84       --argstr &lt;NAME&gt; &lt;STRING&gt;    string-valued argument to be passed to Nix functions
     85   -c, --command &lt;COMMAND&gt; &lt;ARGS&gt;  command and arguments to be executed; defaults to &#39;bash&#39;
     86   -f, --file &lt;FILE&gt;               evaluate FILE rather than the default
     87   -i, --ignore-environment        clear the entire environment (except those specified with --keep)
     88   -I, --include &lt;PATH&gt;            add a path to the list of locations used to look up &lt;...&gt; file names
     89   -k, --keep &lt;NAME&gt;               keep specified environment variable
     90   -u, --unset &lt;NAME&gt;              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 &#39;nixpkgs&#39; channel:
     98   $ nix run nixpkgs.youtube-dl
     99 
    100   To run GNU Hello:
    101   $ nix run nixpkgs.hello -c hello --greeting &#39;Hi everybody!&#39;
    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&rsquo;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&rsquo;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&rsquo;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">&#34;:&#34;</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">&#34;</span><span class="nv">$channelsfile</span><span class="s2">&#34;</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&rsquo;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&rsquo;m
    183 not always on my shell — like, I tend to spend more and more time in <code>eshell</code>. This also
    184 doesn&rsquo;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&rsquo;t have conflicts — if the command already exists, then don&rsquo;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&rsquo;t want any <code>go</code>-related aliases for a computer that doesn&rsquo;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">&gt;</span> nr generate default
    206 <span class="o">&gt;</span> virtmanager already exists
    207 <span class="nv">$ </span>nr git
    208 <span class="o">&gt;</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&rsquo;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&rsquo;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&rsquo;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:&#34;cmd&#34;`</span>
    226     <span class="nx">Package</span> <span class="kt">string</span> <span class="s">`json:&#34;pkg&#34;`</span>
    227     <span class="nx">Channel</span> <span class="kt">string</span> <span class="s">`json:&#34;chan&#34;`</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&rsquo;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&rsquo;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">&#34;nr/default&#34;</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">&#34;ncdu&#34;</span><span class="p">;}</span> <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;sshfs&#34;</span><span class="p">;}</span> <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;gotop&#34;</span><span class="p">;}</span> <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;pandoc&#34;</span><span class="p">;}</span>
    243     <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;wakeonlan&#34;</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">&#34;python36Packages.wakeonlan&#34;</span><span class="p">;}</span>
    244     <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;beet&#34;</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">&#34;beets&#34;</span><span class="p">;}</span>
    245     <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;virt-manager&#34;</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">&#34;virtmanager&#34;</span><span class="p">;}</span>
    246     <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;nix-prefetch-git&#34;</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">&#34;nix-prefetch-scripts&#34;</span><span class="p">;}</span>
    247     <span class="p">{</span><span class="n">cmd</span> <span class="o">=</span> <span class="s2">&#34;nix-prefetch-hg&#34;</span><span class="p">;</span> <span class="n">pkg</span> <span class="o">=</span> <span class="s2">&#34;nix-prefetch-scripts&#34;</span><span class="p">;}</span>
    248   <span class="p">];</span>
    249   <span class="n">onChange</span> <span class="o">=</span> <span class="s2">&#34;</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&#34;</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