i have some bash scripts i am converting to nix packages. in those i like to use a library script, so i would like to have it as its own nix package for reuse between scripts. now i only lack a way of elegantly sourcing/linking the library in the scripts. i know that packages in other languages have a way of getting dependencies from nix packages, but how could one accomplish the same for bash?
what i have so far and how i would like it to work
i have already turned the library into its own nix package. the script using the library is packaged using writeShellApplication and i’m reading the script from a separate sh file. i know one can get the path to a package in the nix store when writing the contents of the script into the text property as a string directly (something like text = "source ${pkgs.my-lib}/lib/my-lib.sh"), but i want to keep the script itself separate from the package file.
to have a similar way of getting to the path of the library, i have so far added a script that just outputs the path to itself/the package in the nix store, that i can then use in scripts. that however feels a bit hacky aswell and not really like the nix-way, if you understand what i mean.
maybe it will always have to be a bit inelegant like this (at least for my taste), but any help and advice on this niche problem is appreciated.
thanks in advance :)
Here are two simple-ish ways to think about accomplishing this without much change beyond what you’ve got right now:
The source builtin will search PATH, so I imagine if you moved the library script to /bin/my-lib.sh and use source my-lib.sh in the script, it might be able to pick it up if you add it as one of the runtimeInputs in writeShellApplication.
If you’re okay with using an environment variable in the source of your sourcing script (i.e., source MY_LIB), you could use writeShellApplication’s runtimeEnv attr to specify the path to the library.
One caveat is that there’s just one PATH–your library package can’t also have its own distinct runtime dependencies that come from the PATH without things starting to get cumbersome. Either each script that uses the library would have to include the library’s runtime dependencies, or the absolute path of each reference to those dependencies needs to get built into the library.
If you need to handle that, there are again a few approaches:
If the library’s shell source is in the .nix file, you can just inline all of the references.
You can do this semi-manually with the various substitute* functions in nixpkgs, but you’ll either need to be careful about overmatching or partially rewrite the library source with @thingToReplace@ placeholders to mitigate that risk.
It’s a little more complex (has its own levers to learn), but resholve is basically designed around satisfying this use-case without having to inline scripts or make their source unusable outside of a Nix package.
It’s a little contrived, but resholve’s Nix API ~demo exercises the ability to integrate shell libraries with their own dependencies:
ah, is see, i didn’t know that source searches in the PATH aswell. for now this is the simpest way to solve my problem, although i would have liked to put the library in /lib/ of it’s package for elegance. only thing to add is: when sourcing the library, a shellcheck directive needs to be added, so that the build won’t fail. so that would something like this:
# shellcheck source=/dev/null
source my-lib.sh
i’m not quite sure this is how it is supposed to be done, but it works like this for me. there is more info about this on the shellcheck wiki
the other solutions sound good aswell, but for my purposes and for now at least, they are overcomplicating my simple scripts.
sorry for the late reply, it took me a bit of time to get back to working on this.
thank you very much for the help :)
For whatever it’s worth, I made my peace with the semantics being suboptimal and put my own shell libraries (i.e., for things I’ve written or packaged) in $out/bin. Even though I made sure resholve is able to handle the lib/ approach, PATH-setting wrappers are such a common idiom in the Nix community that I think it’s ~neighborly to ensure anything someone else might want to use is readily compatible with that approach.
That works. You can also disable the check (# shellcheck disable=SC1091).
My only caution would be that, if you start extracting libraries/scripts into another repo for others to use, you might want to keep shellcheck out of the build path. A nixpkgs bump that include a shellcheck update can cause surprise build blocks. It’s easy enough to just fix whatever it complains about if it’s all in your dotfiles, but if not I generally shellcheck from a separate derivation that I put in passthru.tests and flake checks.