nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at 22.05 294 lines 11 kB view raw view rendered
1# Using resholve's Nix API 2resholve replaces bare references (subject to a PATH search at runtime) to external commands and scripts with absolute paths. 3 4This small super-power helps ensure script dependencies are declared, present, and don't unexpectedly shift when the PATH changes. 5 6resholve is developed to enable the Nix package manager to package and integrate Shell projects, but its features are not Nix-specific and inevitably have other applications. 7 8<!-- generated from resholve's repo; best to suggest edits there (or at least notify me) --> 9 10This will hopefully make its way into the Nixpkgs manual soon, but 11until then I'll outline how to use the functions: 12- `resholve.mkDerivation` (formerly `resholvePackage`) 13- `resholve.writeScript` (formerly `resholveScript`) 14- `resholve.writeScriptBin` (formerly `resholveScriptBin`) 15- `resholve.phraseSolution` (new in resholve 0.8.0) 16 17> Fair warning: resholve does *not* aspire to resolving all valid Shell 18> scripts. It depends on the OSH/Oil parser, which aims to support most (but 19> not all) Bash. resholve aims to be a ~90% sort of solution. 20 21## API Concepts 22 23The main difference between `resholve.mkDerivation` and other builder functions 24is the `solutions` attrset, which describes which scripts to resolve and how. 25Each "solution" (k=v pair) in this attrset describes one resholve invocation. 26 27> NOTE: For most shell packages, one invocation will probably be enough: 28> - Packages with a single script will only need one solution. 29> - Packages with multiple scripts can still use one solution if the scripts 30> don't require conflicting directives. 31> - Packages with scripts that require conflicting directives can use multiple 32> solutions to resolve the scripts separately, but produce a single package. 33 34`resholve.writeScript` and `resholve.writeScriptBin` support a _single_ 35`solution` attrset. This is basically the same as any single solution in `resholve.mkDerivation`, except that it doesn't need a `scripts` attr (it is automatically added). `resholve.phraseSolution` also only accepts a single solution--but it _does_ still require the `scripts` attr. 36 37## Basic `resholve.mkDerivation` Example 38 39Here's a simple example of how `resholve.mkDerivation` is already used in nixpkgs: 40 41<!-- TODO: figure out how to pull this externally? --> 42 43```nix 44{ lib 45, fetchFromGitHub 46, resholve 47, substituteAll 48, bash 49, coreutils 50, goss 51, which 52}: 53 54resholve.mkDerivation rec { 55 pname = "dgoss"; 56 version = "0.3.16"; 57 58 src = fetchFromGitHub { 59 owner = "aelsabbahy"; 60 repo = "goss"; 61 rev = "v${version}"; 62 sha256 = "1m5w5vwmc9knvaihk61848rlq7qgdyylzpcwi64z84rkw8qdnj6p"; 63 }; 64 65 dontConfigure = true; 66 dontBuild = true; 67 68 installPhase = '' 69 sed -i '2i GOSS_PATH=${goss}/bin/goss' extras/dgoss/dgoss 70 install -D extras/dgoss/dgoss $out/bin/dgoss 71 ''; 72 73 solutions = { 74 default = { 75 scripts = [ "bin/dgoss" ]; 76 interpreter = "${bash}/bin/bash"; 77 inputs = [ coreutils which ]; 78 fake = { 79 external = [ "docker" ]; 80 }; 81 }; 82 }; 83 84 meta = with lib; { 85 homepage = "https://github.com/aelsabbahy/goss/blob/v${version}/extras/dgoss/README.md"; 86 description = "Convenience wrapper around goss that aims to bring the simplicity of goss to docker containers"; 87 license = licenses.asl20; 88 platforms = platforms.linux; 89 maintainers = with maintainers; [ hyzual ]; 90 }; 91} 92``` 93 94 95## Basic `resholve.writeScript` and `resholve.writeScriptBin` examples 96 97Both of these functions have the same basic API. This example is a little 98trivial for now. If you have a real usage that you find helpful, please PR it. 99 100```nix 101resholvedScript = resholve.writeScript "name" { 102 inputs = [ file ]; 103 interpreter = "${bash}/bin/bash"; 104 } '' 105 echo "Hello" 106 file . 107 ''; 108resholvedScriptBin = resholve.writeScriptBin "name" { 109 inputs = [ file ]; 110 interpreter = "${bash}/bin/bash"; 111 } '' 112 echo "Hello" 113 file . 114 ''; 115``` 116 117 118## Basic `resholve.phraseSolution` example 119 120This function has a similar API to `writeScript` and `writeScriptBin`, except it does require a `scripts` attr. It is intended to make resholve a little easier to mix into more types of build. This example is a little 121trivial for now. If you have a real usage that you find helpful, please PR it. 122 123```nix 124{ stdenv, resholve, module1 }: 125 126stdenv.mkDerivation { 127 # pname = "testmod3"; 128 # version = "unreleased"; 129 # src = ...; 130 131 installPhase = '' 132 mkdir -p $out/bin 133 install conjure.sh $out/bin/conjure.sh 134 ${resholve.phraseSolution "conjure" { 135 scripts = [ "bin/conjure.sh" ]; 136 interpreter = "${bash}/bin/bash"; 137 inputs = [ module1 ]; 138 fake = { 139 external = [ "jq" "openssl" ]; 140 }; 141 }} 142 ''; 143} 144``` 145 146 147## Options 148 149`resholve.mkDerivation` maps Nix types/idioms into the flags and environment variables 150that the `resholve` CLI expects. Here's an overview: 151 152| Option | Type | Containing | 153|--------|------|------------| 154| scripts | `<list>` | scripts to resolve (`$out`-relative paths) | 155| interpreter | `"none"` `<path>` | The absolute interpreter `<path>` for the script's shebang. The special value `none` ensures there is no shebang. | 156| inputs | `<packages>` | Packages to resolve external dependencies from. | 157| fake | `<directives>` | pretend some commands exist | 158| fix | `<directives>` | fix things we can't auto-fix/ignore | 159| keep | `<directives>` | keep things we can't auto-fix/ignore | 160| lore | `<directory>` | control nested resolution | 161| execer | `<statements>` | modify nested resolution | 162| wrapper | `<statements>` | modify nested resolution | 163| prologue | `<file>` | insert file before resolved script | 164| epilogue | `<file>` | insert file after resolved script | 165 166<!-- TODO: section below is largely custom for nixpkgs, but I would LIKE to wurst it. --> 167 168## Controlling resolution with directives 169 170In order to resolve a script, resholve will make you disambiguate how it should 171handle any potential problems it encounters with directives. There are currently 1723 types: 1731. `fake` directives tell resholve to pretend it knows about an identifier 174 such as a function, builtin, external command, etc. if there's a good reason 175 it doesn't already know about it. Common examples: 176 - builtins for a non-bash shell 177 - loadable builtins 178 - platform-specific external commands in cross-platform conditionals 1792. `fix` directives give resholve permission to fix something that it can't 180 safely fix automatically. Common examples: 181 - resolving commands in aliases (this is appropriate for standalone scripts 182 that use aliases non-interactively--but it would prevent profile/rc 183 scripts from using the latest current-system symlinks.) 184 - resolve commands in a variable definition 185 - resolve an absolute command path from inputs as if it were a bare reference 1863. `keep` directives tell resholve not to raise an error (i.e., ignore) 187 something it would usually object to. Common examples: 188 - variables used as/within the first word of a command 189 - pre-existing absolute or user-relative (~) command paths 190 - dynamic (variable) arguments to commands known to accept/run other commands 191 192> NOTE: resholve has a (growing) number of directives detailed in `man resholve` 193> via `nixpkgs.resholve`. 194 195Each of these 3 types is represented by its own attrset, where you can think 196of the key as a scope. The value should be: 197- `true` for any directives that the resholve CLI accepts as a single word 198- a list of strings for all other options 199<!-- 200TODO: these should be fully-documented here, but I'm already maintaining 201more copies of their specification/behavior than I like, and continuing to 202add more at this early date will only ensure that I spend more time updating 203docs and less time filling in feature gaps. 204 205Full documentation may be greatly accellerated if someone can help me sort out 206single-sourcing. See: https://github.com/abathur/resholve/issues/19 207--> 208 209This will hopefully make more sense when you see it. Here are CLI examples 210from the manpage, and the Nix equivalents: 211 212```nix 213# --fake 'f:setUp;tearDown builtin:setopt source:/etc/bashrc' 214fake = { 215 # fake accepts the initial of valid identifier types as a CLI convenience. 216 # Use full names in the Nix API. 217 function = [ "setUp" "tearDown" ]; 218 builtin = [ "setopt" ]; 219 source = [ "/etc/bashrc" ]; 220}; 221 222# --fix 'aliases $GIT:gix /bin/bash' 223fix = { 224 # all single-word directives use `true` as value 225 aliases = true; 226 "$GIT" = [ "gix" ]; 227 "/bin/bash"; 228}; 229 230# --keep 'source:$HOME /etc/bashrc ~/.bashrc' 231keep = { 232 source = [ "$HOME" ]; 233 "/etc/bashrc" = true; 234 "~/.bashrc" = true; 235}; 236``` 237 238 239> **Note:** For now, at least, you'll need to reference the manpage to completely understand these examples. 240 241## Controlling nested resolution with lore 242 243Initially, resolution of commands in the arguments to command-executing 244commands was limited to one level for a hard-coded list of builtins and 245external commands. resholve can now resolve these recursively. 246 247This feature combines information (_lore_) that the resholve Nix API 248obtains via binlore ([nixpkgs](../../tools/analysis/binlore), [repo](https://github.com/abathur/resholve)), 249with some rules (internal to resholve) for locating sub-executions in 250some of the more common commands. 251 252- "execer" lore identifies whether an executable can, cannot, 253 or might execute its arguments. Every "can" or "might" verdict requires 254 either built-in rules for finding the executable, or human triage. 255- "wrapper" lore maps shell exec wrappers to the programs they exec so 256 that resholve can substitute an executable's verdict for its wrapper's. 257 258> **Caution:** At least when it comes to common utilities, it's best to treat 259> overrides as a stopgap until they can be properly handled in resholve and/or 260> binlore. Please report things you have to override and, if possible, help 261> get them sorted. 262 263There will be more mechanisms for controlling this process in the future 264(and your reports/experiences will play a role in shaping them...) For now, 265the main lever is the ability to substitute your own lore. This is how you'd 266do it piecemeal: 267 268```nix 269# --execer 'cannot:${openssl.bin}/bin/openssl can:${openssl.bin}/bin/c_rehash' 270execer = [ 271 /* 272 This is the same verdict binlore will 273 come up with. It's a no-op just to demo 274 how to fiddle lore via the Nix API. 275 */ 276 "cannot:${openssl.bin}/bin/openssl" 277 # different verdict, but not used 278 "can:${openssl.bin}/bin/c_rehash" 279]; 280 281# --wrapper '${gnugrep}/bin/egrep:${gnugrep}/bin/grep' 282execer = [ 283 /* 284 This is the same verdict binlore will 285 come up with. It's a no-op just to demo 286 how to fiddle lore via the Nix API. 287 */ 288 "${gnugrep}/bin/egrep:${gnugrep}/bin/grep" 289]; 290``` 291 292 293The format is fairly simple to generate--you can script your own generator if 294you need to modify the lore.