Skip to content

Instantly share code, notes, and snippets.

@nat-418
Last active March 21, 2024 17:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nat-418/3a105a958969182695e40214b6ac813a to your computer and use it in GitHub Desktop.
Save nat-418/3a105a958969182695e40214b6ac813a to your computer and use it in GitHub Desktop.
Packaging scripts for Nix

Packaging scripts for Nix

In a previous article, I described how to build a Nix package. In this article, I will discuss how to package scripts: specifically, how to handle their runtime dependencies.

Let's begin with the example of a simple script:

#!/usr/bin/env xonsh
echo "2 + 2" | bc

This is a xonsh script, which calls echo and bc. This program should print the number 4.

Let's look at a basic package definition:

{ stdenv, lib, xonsh, toybox, bc, makeWrapper }:

stdenv.mkDerivation {
  pname = "example";
  version = "0.0.1";
  src = ./.;
  nativeBuildInputs = [ makeWrapper ];
  installPhase = ''
    mkdir -p $out/bin
    install -m 755 example.xonsh $out/bin/example
    wrapProgram $out/bin/example --set PATH ${lib.makeBinPath [
      bc
      toybox
      xonsh
    ]}
  '';
}

Let's explore this line-by-line:

{ stdenv, lib, xonsh, toybox, bc, makeWrapper }:

For most scripts we will need stdenv, lib, and makeWrapper. makeWrapper provides wrapProgram, which we will use below. This particular program needs xonsh, toybox, and bc. xonsh does not provide echo, so we get it from toybox.

stdenv.mkDerivation {
  pname = "example";
  version = "0.0.1";
  src = ./.;

Here we are getting basic information about our program. Normally, src = would be followed by some fetcher like fetchFromGithub.

  nativeBuildInputs = [ makeWrapper ];

Here we specify what we need to build the program. Since it is a script, we just need makeWrapper.

  installPhase = ''
    mkdir -p $out/bin
    install -m 755 example.xonsh $out/bin/example

Here we explain how to actually extract the relevant files from the source. install -m 755 moves the file and marks it as executable.

    wrapProgram $out/bin/example --set PATH ${lib.makeBinPath [
      bc
      toybox
      xonsh
    ]}
  '';
}

This is where we specify our runtime dependencies. The --set PATH argument is important. You may find other documents saying to use --prefix or --suffix, but I recommend --set because it is the easiest to reason about: only what you specify in the following list will be available to the script.

Hopefully this helps show how easy and convienient it is to use Nix to package scripts and make them distributable, for example, in a custom channel.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment