Can I get a different outPath for a "modified" flake input (one reflecting the follows I gave)

I found a nice flake in the docs of nix shell (GitHub - tomberek/-) and now I want to add it to my local registry. But that flake has a flake input for nixpkgs so I thought “that should follow my nixpkgs” (so I will have less copies in the store and need not evaluate so many nixpkgs versions).

But my experiments show that the flake input gets written to the same store path no matter if I let its input follow something or not.

Here is a minimal flake that demonstrates the problem:

{
  description = "How can I link to a modified flake from my inputs?";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
    original.url = "github:tomberek/-";
    following.url = "github:tomberek/-";
    following.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = inputs: let
    pkgs = import inputs.nixpkgs {system = "x86_64-linux";};
    info = flake: {inherit (flake) lastModified narHash shortRev;};
    traceAndBuild = input: let
      flake = inputs.${input};
      clean = info flake // {nixpkgs = info flake.inputs.nixpkgs; name = input;};
    in
      pkgs.lib.debug.traceSeqN 2 clean
      ''
        cat <<EOF | ${pkgs.lib.getExe pkgs.jq} > $out/${input}.json
        ${builtins.toJSON clean}
        EOF
        ln -s ${flake} $out/${input}
      '';
  in {
    packages.x86_64-linux.default = pkgs.runCommand "debug" {} ''
      mkdir $out
      ${traceAndBuild "original"}
      ${traceAndBuild "following"}
    '';
  };
}

The result looks like this

$ ls -l result/
total 8
lrwxrwxrwx 1 root root  50 Jan  1  1970 following -> /nix/store/zizzgvr1i4rg3ygicmzs34c9a6fq0j8p-source
-r--r--r-- 1 root root 297 Jan  1  1970 following.json
lrwxrwxrwx 1 root root  50 Jan  1  1970 original -> /nix/store/zizzgvr1i4rg3ygicmzs34c9a6fq0j8p-source
-r--r--r-- 1 root root 296 Jan  1  1970 original.json
$ diff result/*.json
3c3
<   "name": "following",
---
>   "name": "original",
6,8c6,8
<     "lastModified": 1753694789,
<     "narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=",
<     "shortRev": "dc96378"
---
>     "lastModified": 1744232761,
>     "narHash": "sha256-gbl9hE39nQRpZaLjhWKmEu5ejtQsgI5TWYrIVVJn30U=",
>     "shortRev": "f675531"

Note that I had to remove some attributes from the flakes before dumping them to json, otherwise their outPath would be show or I would get evaluation errors from some derivations in nixpkgs if I seq’ed to deep.

My question is thus: Can I get a modified outPath for my flake input that follows my nixpkgs? How do I do that?

Yes, because you’re not changing the upstream flake, you’re changing your own flake’s lockfile. So the upstream flake’s narHash won’t change.

This sounds x-y but I don’t know what you’re trying to accomplish here. Using follows will not save space in the storage of the upstream flake, as it’s going to download the whole thing regardless. It just won’t fetch multiple nixpkgs inputs.

Saving disk space (and even more so evaluation time) by not having two different versions of nixpkgs is my aim.

I want to write this github:tomberek/- to my local nix registry. It is actually a utility flake with very little code (so this flake itself does not take much disk space) so I want to write nix shell p#python3With.requests instead of nix shell github:tomberek/-#python3With.requests. So I do this:

{ inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
  inputs.smart-dash.url = "github:tomberek/-";
  outputs = { nixpkgs, smart-dash, ... }: {
    nixosConfigurations.home = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = { nix.registry.p.flake = smart-dash; };
};};}

But this results in two versions of nixpkgs and if I use nix shell p#... later on these packages get pulled from the locked input of tomberek/- instead of my nixpkgs version.

You’re already doing that with follows*. Again, not sure what the rigmarole about store paths is.

*well, sort of. If you have the same nixpkgs input but they are configured differently or otherwise reimported then you’d still have multiple instances.

Use --override-input nixpkgs nixpkgs. (By default, NixOS creates a nixpkgs system registry entry tied to the nixpkgs that built your config.) That has nithing to do with follows though, as you’re now outside of your downstream flake context.

This does not help my final usage of tomberek’s flake.

By your suggestion I would have to type

nix shell --override-input nixpkgs nixpkgs p#...

but that is a long command and I am mainly introducing the registry entry in order to not write the long flake name. In this case I could also write

nix shell --override-input nixpkgs nixpkgs github:tomberek/-#...

but I would like to write the much shorter

nix shell p#...

and have it use the same nixpkgs as my system.

Maybe I can write a derivation that copies the tomberek flake and modifies the lock file, will try.

The flake registry can not reflect that an entry should use other inputs than specified than in that flakes lockfile.

If you want to use different inputs than toms lockfile specifies, you have to patch it.

Due to the fact that then you usually have to build stuff, and this means that you have to get buildtools, the saved space will probably net out to 0.

You can try. I’m not sure that will work due to the recent nix changes that ignore the system and user registries as flake inputs, combined with the fact that under various conditions nix just ignores the lockfile and does whatever it wants.

i.e. you may end up back to the solution I mentioned :slight_smile:

Correct. Make a shell alias / script if you like.

I was thinking about that initially but than I would loose shell completion (or would have to write some for my script).

I could of course make a shell alias for just alias nsn=nix shell --override-input nixpkgs nixpkgs but that feels strange and would spit out errors if used with a flake that does not have the nixpkgs input:

warning: input '' has an override for a non-existent input 'nixpkgs'
warning: the flag '--override-input nixpkgs flake:nixpkgs' does not match any input

I am now using this and it works as I expect it:

{
  description = "How can I link to a modified flake from my inputs?";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
    original.url = "github:tomberek/-";
    following.url = "github:tomberek/-";
    following.inputs.nixpkgs.follows = "nixpkgs";
  };

  outputs = inputs: let
    system = "x86_64-linux";
    pkgs = import inputs.nixpkgs {inherit system;};
    linkFlakeAndInput = input: ''
      ln -s ${inputs.${input}} $out/${input}
      ln -s ${inputs.${input}.inputs.nixpkgs} $out/${input}-nixpkgs
    '';
    lock = builtins.fromJSON (builtins.readFile "${inputs.following}/flake.lock");
    modifiedLock = lock // {
      nodes.nixpkgs.locked = {
        inherit (inputs.nixpkgs) lastModified narHash;
        type = "path";
        path = "${inputs.nixpkgs}";
      };
      nodes.nixpkgs.original = lock.nodes.nixpkgs.original;
      nodes.root = lock.nodes.root;
    };
    rewrittenFlake = pkgs.runCommand "rewritten" {} ''
      mkdir $out
      cp ${inputs.following}/flake.nix $out
      echo '${builtins.toJSON modifiedLock}' > $out/flake.lock
    '';
  in {
    packages.x86_64-linux.default = pkgs.runCommand "debug" {} ''
      mkdir $out
      ${linkFlakeAndInput "original"}
      ${linkFlakeAndInput "following"}
      ln -s ${rewrittenFlake} $out/rewritten
    '';
    nixosConfigurations.home = pkgs.lib.mkSystem {
      inherit system;
      modules = {
        nix.registry.original.flake = inputs.original;
        nix.registry.following.flake = inputs.following;
        nix.registry.rewritten.flake = rewrittenFlake;
      };
    };
  };
}

It is more verbose than I was hoping for though :confused: