Distributing shell scripts via a flake - failed to produce output path

I’m trying to package a shell script from a github repo as a flake just by copying it to the bin directory in the output:

{
	inputs  = {
		nixpkgs.url = "github:nixos/nixpkgs/release-24.05";
	};

	outputs = { nixpkgs, ... }@inputs: 
	{
		packages."x86_64-linux".default = derivation {
			name = "test-script";
			builder = "${inputs.nixpkgs.legacyPackages."x86_64-linux".bash}/bin/bash";
			src = ./.;
			system = "x86_64-linux";
			buildCommand = ''
				mkdir $out/bin
				mv script.sh $out/bin
			'';
		};
	};
}

flake.nix and script.sh are in the same directory and I want to copy script.sh to a package so it can be updated on the system by updating the flake. However, when I run nix build in the flake directory I get

error: builder for '/nix/store/fa1jvk3hqr7p721z4h4h02cxr2nxcrzi-test-script.drv' failed to produce output path for output 'out' at '/nix/store/fa1jvk3hqr7p721z4h4h02cxr2nxcrzi-test-script.drv.chroot/root/nix/store/wxmb38b27jfd13rh758r619ki245kabx-test-script'

What am I missing here? I also tried using installPhase instead of buildCommand but it had the same error

I also couldn’t figure out how to get stdenv.mkDerevation to work in the flake but that’s a different problem I think!

Try this:

  outputs =
    { nixpkgs, ... }@inputs:
    let
      pkgs = import nixpkgs {
        system = "x86_64-linux";
      };
    in
    {
      packages."x86_64-linux".default = pkgs.stdenv.mkDerivation {
        pname = "test-script";
        version = "0.1.0";
        src = ./.;
        installPhase = ''
          	mkdir -p $out/bin
          	mv script.sh $out/bin
        '';
      };
    };

This seems to work for me:

$ nix build
$ ls -l ./result/bin/script.sh
-r-xr-xr-x 3 root root 86 Dec 31  1969 ./result/bin/script.sh*
$ ./result/bin/script.sh
Hello world!

aha, thank you for the quick reply! Would have take me a lot longer to figure out

And that’s okay :slight_smile: It’s a steep learning curve. In general, my advice is to try to use mkDerivation instead of derivation. It’s much more ergonomic.

Sorry for replying after making this solved but it’s not quite having the result I expected.

If I use this code:

{
	inputs  = {
		nixpkgs.url = "github:nixos/nixpkgs/release-24.05";
	};

	outputs = { nixpkgs, ... }@inputs: 
	let
      pkgs = import nixpkgs {
        system = "x86_64-linux";
    	};
    in {
		packages."x86_64-linux".default = pkgs.stdenv.mkDerivation {
			name = "my-package";
			src = ./.;
			buildPhase = ''
				echo 1 > test1
			'';
			installPhase = ''
				mkdir -p $out
			  echo 2 > $out/test2
			'';
		};
	};
}

What is the correct way to reference this in a target system? I’m doing this:

{
  inputs = {
    my-package.url = "path:/path-containing-previous-flake";
  };

  outputs = { nixpkgs, my-package, ... }@inputs: {
    nixosConfigurations.system = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./config.nix
        {
          _module.args = { inherit inputs; };
        }
      ];
    };
  };
}

Then if I do something like this to reference a file

environment.etc."test".path = "${inputs.my-package}/test2";

The file doesn’t exist and ${inputs.my-package}/ references a directory suffixed -source which contains all the files from the src directory in the flake, but without any changes from the build or install phases.

nix build in the directory with the my-package flake creates the expected result.

I’m assuming I’m using it wrong, but what do I need to do in place of ${inputs.my-package} to reference the result directory, rather than the source directory?

Can I prevent the source directory being placed in the nix store entirely?

To answer my own question I need to use "${inputs.my-package.packages.x86_64-linux.default}/file which is kind of verbose and slightly annoying that it has to include the architecture given the package contains interpreted code rather than compiled code

You could use an overlay in the flake distributing the script, and apply that overlay in your NixOS configuration. This way you don’t have to reference the target architecture when including the package in your configuration.

Script’s flake.nix:

{
  inputs  = {
    nixpkgs.url = "github:nixos/nixpkgs/release-24.05";
  };

  outputs = { nixpkgs, ... }@inputs: {
    overlays.default = final: prev: {
      my-package = final.stdenv.mkDerivation {
        name = "my-package";
        src = ./.;
        buildPhase = ''
          echo 1 > test1
        '';
        installPhase = ''
          mkdir -p $out
          echo 2 > $out/test2
        '';
      };
    };
  };
}

NixOS configuration flake.nix:

{
  inputs = {
    my-package.url = "path:/path-containing-previous-flake";
  };

  outputs = { nixpkgs, my-package, ... }@inputs: {
    nixosConfigurations.system = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./config.nix
        { pkgs, ... }: {
          nixpkgs.overlays = [inputs.my-package.overlays.default];
          environment.etc."test".path = "${pkgs.my-package}/test2";
        }
      ];
    };
  };
}
2 Likes