www

My personal website(s)
Log | Files | Refs

2019-01-26-nix-run-alias.html (12329B)


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