/nuenv

A Nushell environment for Nix

Primary LanguageNushell

Nuenv: an experimental Nushell environment for Nix

GitHub Actions status badge

Warning: This project is a fun experiment—and perhaps a source of inspiration for others—but not something you should use for any serious purpose.

This repo houses an example project that uses Nushell as an alternative builder for Nix (whose standard environment uses Bash). For more information, check out Nuenv: an experimental Nushell environment for Nix on the Determinate Systems blog.

Running the scenario

First, make sure that you have Nix installed with flakes enabled. We recommend using our Determinate Nix Installer:

curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix \
  | sh -s -- install

With Nix installed, you can realise a Nix derivation:

nix build --print-build-logs

# Or without cloning this repo
nix build --print-build-logs "github:DeterminateSystems/nuenv"

You should see build output like this:

hello-nix-nushell> >>> INFO
hello-nix-nushell> > Realising the hello-nix-nushell derivation for aarch64-darwin
hello-nix-nushell> > Using Nushell 0.77.0
hello-nix-nushell> > Declared build outputs:
hello-nix-nushell> + out
hello-nix-nushell> >>> SETUP
hello-nix-nushell> > Adding 1 package to PATH:
hello-nix-nushell> + hello-2.12.1
hello-nix-nushell> > Setting PATH
hello-nix-nushell> > Setting 1 user-supplied environment variable:
hello-nix-nushell> + MESSAGE = "Hello from Nix + Bash"
hello-nix-nushell> > Copying sources
hello-nix-nushell> > Creating output directories
hello-nix-nushell> >>> REALISATION
hello-nix-nushell> > Running build phase
hello-nix-nushell> + Running hello version 2.12.1
hello-nix-nushell> + Creating output directory at /nix/store/n0dqy5gpshz21hp1qhgj6795nahqpdyc-hello-nix-nushell/share
hello-nix-nushell> + Writing hello message to /nix/store/n0dqy5gpshz21hp1qhgj6795nahqpdyc-hello-nix-nushell/share/hello.txt
hello-nix-nushell> + Substituting Bash for Nushell in /nix/store/n0dqy5gpshz21hp1qhgj6795nahqpdyc-hello-nix-nushell/share/hello.txt
hello-nix-nushell> >>> DONE!
hello-nix-nushell> > out output written to /nix/store/n0dqy5gpshz21hp1qhgj6795nahqpdyc-hello-nix-nushell

This derivation does something very straightforward: it provides a message to GNU's hello tool and saves the result in a text file called hello.txt under the share directory.

cat ./result/share/hello.txt

How it works

The key differentiator from regular Nix here is that realisation happens in Nushell scripts rather than in Bash. The project's flake outputs a function called mkNushellDerivation that wraps Nix's built-in derivation function but, in contrast to stdenv.mkDerivation, uses Nushell as the builder, which in turn runs a builder.nu script that provides the Nix environment. In addition to builder.nu, env.nu provides helper functions to your realisation scripts.

Try it out

You can use nuenv to realise your own derivations. Here's a straightforward example:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05";
    nuenv.url = "github:DeterminateSystems/nuenv";
  };

  outputs = { self, nixpkgs, nuenv }: let
    overlays = [ nuenv.overlays.default ];
    systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
    forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f {
      inherit system;
      pkgs = import nixpkgs { inherit overlays system; };
    });
  in {
    packages = forAllSystems ({ pkgs, system }: {
      default = pkgs.nuenv.mkDerivation {
        name = "hello";
        src = ./.;
        inherit system;
        # This script is Nushell, not Bash
        packages = with pkgs; [ hello ];
        build = ''
          hello --greeting $"($env.MESSAGE)" | save hello.txt
          let out = $"($env.out)/share"
          mkdir $out
          cp hello.txt $out
        '';
        MESSAGE = "My custom Nuenv derivation!";
      };
    });
  };
}

To build and view the result of this derivation:

nix build
cat ./result/share/hello.txt

Creating Nushell scripts

Nuenv has a writeScriptBin function that you can use to wrap Nushell scripts using Nix. Here's an example:

# This example is only for x86_64-linux; adjust for your own platform
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs";
    nuenv.url = "github:DeterminateSystems/nuenv";
  };

  outputs = { self, nixpkgs, nuenv }: let
    pkgs = import nixpkgs {
      system = "x86_64-linux";
      overlays = [ nuenv.overlays.nuenv ];
    };
  in {
    packages.x86_64-linux.run-me = pkgs.nuenv.writeScriptBin {
      name = "run-me";
      script = ''
        def blue [msg: string] { $"(ansi blue)($msg)(ansi reset)" }
        blue "Hello world"
      '';
    };
  };
}

writeScriptBin serves the same purpose as writeScriptBin in the Nixpkgs standard environment but for Nushell instead of Bash.