Hello all!
I am trying to nixify a modular monorepo using multiple flakes.
This seems like the ideal approach because it enables nix run
to work with each sub-directory, among other things. Read Nix sub-flakes design - HackMD to see the reasons this design is important to support.
The directory structure looks like:
root
└ .git
└ package1
└ derivation.nix
└ flake.nix
└ ...
└ package2
└ derivation.nix
└ flake.nix
└ ...
└ flake.nix
└ ...
I found a good GitHub issue thread on this subject, with working syntax in a comment by @aviallon at Allow flakes to refer to other flakes by relative path · Issue #3978 · NixOS/nix · GitHub
So I add child flakes to the inputs of the parent flake like:
<...>
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
flake-utils.url = "github:numtide/flake-utils";
subflake = {
url = "git+file:.?dir=subflake";
inputs.nixpkgs.follows="nixpkgs";
inputs.flake-utils.follows="flake-utils";
};
};
<...>
This works, for the most part.
I can use pkgs.mkShell { inputsFrom }
to combine a child’s shell into the parent shell.
Parent flake.nix
:
<...>
devShells.default = pkgs.mkShell {
packages = [
pkgs.podman
pkgs.gzip
pkgs.skopeo
];
inputsFrom = [
subflake.devShells.${system}.default
];
};
<...>
So nix develop
works.
However, the child flakes seem to not resolve nixpkgs correctly outside of this use case.
Both pkgs.callPackage
and pkgs.writeShellApplication
fail to resolve.
Steps To Reproduce
Copy the following code into a file, ./mk-subflake-test.sh
:
#!/bin/sh
# make the project directories
mkdir -p subflake-test/subflake
# enter the project directory
cd subflake-test
# track the repository by git
git init
# create the parent flake
cat > flake.nix << EOF
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05";
flake-utils.url = "github:numtide/flake-utils";
subflake = {
url = "git+file:.?dir=subflake";
inputs.nixpkgs.follows="nixpkgs";
inputs.flake-utils.follows="flake-utils";
};
};
outputs = { self, nixpkgs, flake-utils, subflake, ... }:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs { inherit system; };
in rec {
packages.default = pkgs.writeShellApplciation {
name = "subflake-test";
runtimeInputs = [ subflake ];
text = ''
\${pkgs.lib.getExe subflake.packages.\${system}.default}
'';
};
apps.default = flake-utils.lib.mkApp { drv = packages.default; };
}
);
}
EOF
# create the child flake (subflake)
cat > subflake/flake.nix << EOF
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system: let
pkgs = import nixpkgs { inherit system; };
in rec {
packages.default = pkgs.callPackage ./test.nix {};
apps.default = flake-utils.lib.mkApp { drv = packages.default; };
}
);
}
EOF
# create the nix derivation
cat > subflake/test.nix << EOF
{ pkgs }: pkgs.writeShellApplication {
name = "subflake-test";
text = ''
#/bin/sh
echo "Hello, World!"
'';
}
EOF
# track the project files using git
git add .
Then execute the file using:
chmod +x ./mk-subflake-test.sh && ./mk-subflake-test.sh
Now enter the project directory using cd subflake-test
and then use nix run
;
Observe the error returned.
Now, nix run
in subflake-test
returns:
> 16:52 subflake-test git:(main) X nix run
warning: Git tree '/home/admin/code/subflake-test' is dirty
warning: Git tree '.' is dirty
warning: creating lock file '/home/admin/code/subflake-test/flake.lock'
warning: Git tree '/home/admin/code/subflake-test' is dirty
error:
… while evaluating the attribute 'default'
at /nix/store/hzwacgdhqd752zyg2hyv9rrk1b28hb48-source/flake.nix:16:7:
15| in rec {
16| packages.default = pkgs.writeShellApplciation {
| ^
17| name = "subflake-test";
error: attribute 'writeShellApplciation' missing
at /nix/store/hzwacgdhqd752zyg2hyv9rrk1b28hb48-source/flake.nix:16:26:
15| in rec {
16| packages.default = pkgs.writeShellApplciation {
| ^
17| name = "subflake-test";
Did you mean writeShellApplication?
> 16:52 subflake-test git:(main) X
Yet nix run
in subflake-test/subflake
returns:
> 16:52 subflake-test/subflake git:(main) X nix run
warning: Git tree '/home/admin/code/subflake-test' is dirty
warning: creating lock file '/home/admin/code/subflake-test/subflake/flake.lock'
warning: Git tree '/home/admin/code/subflake-test' is dirty
Hello, World!
> 16:52 subflake-test/subflake git:(main) X
Why is this not working?
How should I be using sub-flakes instead?
If it’s impossible to use sub-flakes, how should I organize my code with multiple submodules?
Thank you for your time.
Solution
There 3 options.
- Call
nix flake lock --update-input subflake
for each subflake whenever it’s modified (source: from a GitHub Issue). - Store each package inside their own separate git repositories.
- Don’t use sub-flakes.
I decided to stop trying to use sub-flakes. I outlined the approach I used instead in this post.