Good example of embedding a custom derivation within a nix flake

Hi all! I am experimenting with nix flakes and adopted the following flake structure:

flake.nix:

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

  outputs = { self, nixpkgs }@inputs:
    let
      inherit (self) outputs;
      forAllSystems = nixpkgs.lib.genAttrs [
        "aarch64-linux"
        "i686-linux"
        "x86_64-linux"
        "aarch64-darwin"
        "x86_64-darwin"
      ];
    in rec {
      # Devshell for bootstrapping
      # Acessible through 'nix develop' or 'nix-shell' (legacy)
      devShells = forAllSystems (system:
        let
          pkgs = nixpkgs.legacyPackages.${system};
        in
          import ./shell.nix { inherit pkgs; }
      );
  };
}

shell.nix

# Shell for bootstrapping flake-enabled nix and home-manager
# You can enter it through 'nix develop' or (legacy) 'nix-shell'

{ pkgs }: {

  default = pkgs.mkShell {
    nativeBuildInputs = [
      pkgs.terraform
    ];
  };
}

Say that I have a custom derivation like this:

./terraform-graph-beautifier/default.nix

{ stdenv
, lib
, go
, buildGoModule
, fetchFromGitHub
, makeWrapper
, coreutils
, runCommand
, runtimeShell
, writeText
, installShellFiles
}:
{ version, hash, vendorHash ? null, ... }:
  buildGoModule rec {
    pname = "terraform-graph-beautifier";
    version = "0.3.3";
    vendorHash = lib.fakeSha256;

    src = fetchFromGitHub {
      owner = "pcasteran";
      repo = "terraform-graph-beautifier";
      rev = "v${version}";
      hash = lib.fakeSha256;
    };

    meta = with lib; {
      description =
        "Command line tool allowing to convert the barely usable output of the terraform graph command to something more meaningful and explanatory.";
      homepage = "https://github.com/pcasteran/terraform-graph-beautifier";
      license = licenses.apache2;
      changelog = "https://github.com/pcasteran/terraform-graph-beautifier/releases/v${version}/";
      ...
    };
}

Note: this dirivation is copied from docs, and it might also contain errors, as I have not been able to test it yet.

How do I use this as a package within shell.nix?

I think I am just missing some fundamental part of how flakes work and can’t seem to find a good example.

I tried the following things:

  • naively adding this to the nativeBuildInputs in shell.nix
(pkgs.callPackage ./pkgs/terraform-graph-beautifier { })
  • attempted to add an custom package like this in flake.nix
packages = forAllSystems (system:
  let pkgs = nixpkgs.legacyPackages.${system};
    in import ./pkgs { inherit pkgs; }
);

If somebody could point me in the right direction, I would be very grateful!

I would be happy to contribute a working example back to the documentation, just point me where to add it.

I would expect this one to work. If it doesn’t, please provide a more detailed error description

1 Like

Thanks for the encouragement. I felt like I was very close, and it seems I was. For other travellers finding this post. I’ll add an working example below.

flake.nix

{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixos-23.05";
    nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs, nixpkgs-unstable }@inputs:
    let
      inherit (self) outputs;
      forAllSystems = nixpkgs.lib.genAttrs [
        "aarch64-linux"
        "i686-linux"
        "x86_64-linux"
        "aarch64-darwin"
        "x86_64-darwin"
      ];

    in rec {
      # Your custom packages
      # Acessible through 'nix build', 'nix shell', etc
      packages = forAllSystems (system:
        let pkgs = nixpkgs.legacyPackages.${system};
        in import ./pkgs { inherit pkgs; }
      );

      # Devshell for bootstrapping
      # Acessible through 'nix develop' or 'nix-shell' (legacy)
      devShells = forAllSystems (system:
        let
          pkgs = nixpkgs.legacyPackages.${system};
        in
          import ./shell.nix { inherit pkgs; inherit packages; }
      );
  };
}

./pkgs/default.nix

# Custom packages, that can be defined similarly to ones from nixpkgs
# You can build them using 'nix build .#example' or (legacy) 'nix-build -A example'

{ pkgs }: {
  terraform-graph-beautifier = pkgs.callPackage ./terraform-graph-beautifier { };
}

./pkgs/terraform-graph-beautifier/default.nix

{
buildGoModule,
fetchFromGitHub,
lib,
}:
buildGoModule rec {
  pname = "terraform-graph-beautifier";
  version = "0.3.3";
  vendorHash = "sha256-Znas0FCA4QfIjrNXbc8sKEQhqXMeYZucOJSID0qjVLo=";

  src = fetchFromGitHub {
    owner = "pcasteran";
    repo = "terraform-graph-beautifier";
    rev = "v${version}";
    hash = "sha256-UmN0YsELaichtwINtHIKtA8uDhRqsuLjQ4wRAeJx0Ws=";
  };

  meta = {
    description =
      "Command line tool allowing to convert the barely usable output of the terraform graph command to something more meaningful and explanatory.";
    homepage = "https://github.com/pcasteran/terraform-graph-beautifier";
    changelog = "https://github.com/pcasteran/terraform-graph-beautifier/releases/v${version}/";
  };
}

./shell.nix

# Shell
{ pkgs, packages }: {

  default = pkgs.mkShell({
    nativeBuildInputs = [
      pkgs.terraform
      # if you have more packages, the cleaner way to do this is probably 
      # by using overlays. See this repo for a good example:  
      # https://github.com/Misterio77/nix-starter-configs
      packages.x86_64-darwin.terraform-graph-beautifier
    ];

    buildInputs = [
    ];
  });
}

Note that the main difference between my first version and earlier version, is the function header in the derivation, I think this cause the issue.

From what I understand now you can basically almost copy derivations from nixpkgs, and edit them, but be mindful of the fact that some of them like above are functions, while callPackage expects just an attrset (correct me if I am wrong).