Skip to content

Allow a flake output to re-evaluate the flake with sourceInfo preserved #12388

Open
@plietar

Description

I have a flake where one output embeds self.sourceInfo (writeJSON behaves in a surprising way if outPath is present, hence the removeAttrs call):

packages.x86_64-linux.sourceInfo = pkgs.writers.writeJSON "sourceInfo.json" (builtins.removeAttrs self.sourceInfo [ "outPath" ]);

If I build and print this output, I get all the metadata I expected

$ nix build .#sourceInfo
$ cat result
{
  "dirtyRev": "c8bec8697f5dda3eb27a8703ea1004ba6bac1c8a-dirty",
  "dirtyShortRev": "c8bec86-dirty",
  "lastModified": 1738331265,
  "lastModifiedDate": "20250131134745",
  "narHash": "sha256-MIrEQ686AL6QdvsU+guE3Zh5UnL9V7mu0MZAu413R38=",
  "submodules": false
}

I now want to have a second output of my flake that is a script which when run, evaluates and builds the first attribute.
I can get a basic version of this by doing nix build .#sourceInfo

packages.x86_64-linux.print-sourceinfo = pkgs.writeShellScriptBin "print-sourceinfo" ''
  cat $(nix build --no-link --print-out-paths .#sourceInfo)
'';

The above works, but only if the current working directory is the actual flake that I want to run. I would like to be able to run this script from any source, using something as nix run github:org/repo#print-sourceinfo.

I can get pretty close to this by using self as the flake ref in the script:

outputs = { self, nixpkgs }:
  let pkgs = nixpkgs.legacyPackages.x86_64-linux; in {
    packages.x86_64-linux.sourceInfo =
      pkgs.writers.writeJSON "sourceInfo.json" (builtins.removeAttrs self.sourceInfo [ "outPath" ]);
    packages.x86_64-linux.print-sourceinfo = pkgs.writeShellScriptBin "print-metadata" ''
      cat $(nix build --no-link --print-out-paths ${self}#sourceInfo)
    '';
  };

Unfortunately, because ${self} is just a store path, this setup loses any of the Git metadata that is associated with the flake.

$ nix run .#print-sourceinfo
{
  "lastModified": 0,
  "lastModifiedDate": "19700101000000",
  "narHash": "sha256-Ze0b5N+a3RklxbLX7bEFVCOv+PSo2rr2Zq83Yd1bbhM="
}

I'd like a way to replicate this setup, but while preserving the sourceInfo metadata.

Proposed solution

Effectively I need to be able to get a reference to the current flake as a string that can be embedded into a script.
There should be a function that can get take a sourceInfo and produce a flake ref that is locked to that source, including all the associated metadata.

The CLI has the nix flake metadata command that prints a "locked URL". If this URL was exposed inside the flake, then my script could embed it and be certain to be re-evaluating itself with the same source and metadata by using nix build ${self.lockedUrl}. It's worth noting that even in the CLI, the "Locked URL" is not printed if the local Git tree is dirty.

Alternative solutions

I can actually get some of the way there by using path:${self}?rev=${self.sourceInfo.rev} as the flake reference in my script. Somehow the path flake scheme allows a ref to be specified. It does not however allow a dirtyRev to be used, which means that I cannot use this solution in the case where the Git tree has local modifications.

Additional context

My use case of having a flake output evaluate the flake itself might seemed a bit contrived. In pratice this script is used to deploy a NixOS configuration from the flake to a machine. It accepts a hostname as an argument, and when run evalutes ${self}#nixosConfigurations.$hostname and deploys it to the machine.

The overall workflow should be to run nix run github:org/repo#deploy <hostname>, which would use the deploy script and NixOS configuration found in the flake.

I do not want to embed the built system configuration into the deploy script, since we have many machines and they should be all be evaluated and built lazily when needed by the script, and not eagerly when the script is being built.


Add 👍 to issues you find important.

Metadata

Assignees

No one assigned

    Labels

    featureFeature request or proposal

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions