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> >= <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 <FLAGS>... <INSTALLABLES>... 30 31 Summary: run a shell in which the specified packages are available. 32 33 Flags: 34 --arg <NAME> <EXPR> argument to be passed to Nix functions 35 --argstr <NAME> <STRING> string-valued argument to be passed to Nix functions 36 -c, --command <COMMAND> <ARGS> command and arguments to be executed; defaults to 'bash' 37 -f, --file <FILE> evaluate FILE rather than the default 38 -i, --ignore-environment clear the entire environment (except those specified with --keep) 39 -I, --include <PATH> add a path to the list of locations used to look up <...> file names 40 -k, --keep <NAME> keep specified environment variable 41 -u, --unset <NAME> 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’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’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’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’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’m 165 not always on my shell — like, I tend to spend more and more time in <code>eshell</code>. This also 166 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 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’t have conflicts — if the command already exists, then don’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’t want any <code>go</code>-related aliases for a computer that doesn’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 > nr generate default 196 > virtmanager already exists 197 $ nr git 198 > 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’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’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’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’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’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>