From ec9f9b1acc0c4c2d04938b99a86a6b9000261d71 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 13 Sep 2026 12:16:22 +0200 Subject: [PATCH 1/3] fix: overrideSubSpec to handle case where input override take source arg --- tests.nix | 15 +++++++++++++++ with-inputs.nix | 6 ++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tests.nix b/tests.nix index dc85ecb..cb053df 100644 --- a/tests.nix +++ b/tests.nix @@ -369,6 +369,21 @@ in expected = npins.nixpkgs.outPath; }; + introspection.test-follow-sub-npins-with-inputs-input-with-source-arg = { + # s: my-lib.inputs.nixpkgs.follows = "with-inputs-dep/nixpkgs" → traverse native inputs + expr = + (with-inputs + { + my-lib = mkSrc ./fixtures/fake-flake; + with-inputs-dep = mkSrc ./fixtures/with-inputs-flake; + } + { + my-lib = s: { inputs.nixpkgs.follows = "with-inputs-dep/nixpkgs2"; }; + } + ).my-lib.inputs.nixpkgs.outPath; + expected = npins.nixpkgs.outPath; + }; + real-flakes.test-npins-nix-maid-nixosModules-output-is-readable = { expr = (with-inputs npins { } (inputs: { diff --git a/with-inputs.nix b/with-inputs.nix index dedc559..8d300ec 100644 --- a/with-inputs.nix +++ b/with-inputs.nix @@ -62,11 +62,9 @@ let hostName: subName: let entry = inputs.${hostName} or null; + getSubInput = e: if builtins.isAttrs e && e ? inputs then e.inputs.${subName} or null else null; in - if entry != null && builtins.isAttrs entry && entry ? inputs then - entry.inputs.${subName} or null - else - null; + getSubInput (if builtins.isFunction entry then (entry sources.${hostName}) else entry); # Resolve an inputs entry to an actual input value, or null if unresolvable. # Values with outPath but no _type go through mkInput so their flake.nix is loaded. From 7ef6c6035a44b4f0800ce267dd7949abce7f76d5 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Sun, 13 Sep 2026 17:12:12 +0200 Subject: [PATCH 2/3] fix: use default.nix to inject inputsOverrides if available using the flake api breaks when flakes use the following pattern: ```nix { outputs = args: let f = { self }: ...; in f args; } ``` (which is the case of nixpkgs-lib). --- README.md | 5 +- fixtures/with-inputs-flake/flake.nix | 1 - .../default.nix | 0 tests.nix | 6 +- with-inputs.nix | 83 +++++++++++-------- 5 files changed, 54 insertions(+), 41 deletions(-) delete mode 100644 fixtures/with-inputs-flake/flake.nix rename fixtures/{with-inputs-flake => with-inputs-project}/default.nix (100%) diff --git a/README.md b/README.md index 053387b..1764d47 100644 --- a/README.md +++ b/README.md @@ -106,15 +106,12 @@ let in with-inputs outputs ``` +Consumer projects that use `with-inputs` will automatically inject their own `inputs` as `inputsOverrides`. [^output-trick]: To use the experimental `nix` cli commands, create a `flake.nix` containing only ```nix { outputs = _: import ./.; } ``` -[^output-trick-2]: To additionally allow inputs overrides (eg, by a `with-inputs`-based consummer project): - ```nix - { outputs = inputsOverrides: import ./. { inherit inputsOverrides; } - ``` ### Flake backed by non-flake pins When `with-inputs` detect a flake dependency which does not declare any inputs, that flake `output` function is still called with the all available inputs, so they could be used as overrides. diff --git a/fixtures/with-inputs-flake/flake.nix b/fixtures/with-inputs-flake/flake.nix deleted file mode 100644 index 4a3f569..0000000 --- a/fixtures/with-inputs-flake/flake.nix +++ /dev/null @@ -1 +0,0 @@ -{ outputs = inputsOverrides: import ./. { inherit inputsOverrides; }; } diff --git a/fixtures/with-inputs-flake/default.nix b/fixtures/with-inputs-project/default.nix similarity index 100% rename from fixtures/with-inputs-flake/default.nix rename to fixtures/with-inputs-project/default.nix diff --git a/tests.nix b/tests.nix index cb053df..166fe54 100644 --- a/tests.nix +++ b/tests.nix @@ -360,7 +360,7 @@ in (with-inputs { my-lib = mkSrc ./fixtures/fake-flake; - with-inputs-dep = mkSrc ./fixtures/with-inputs-flake; + with-inputs-dep = mkSrc ./fixtures/with-inputs-project; } { my-lib.inputs.nixpkgs.follows = "with-inputs-dep/nixpkgs2"; @@ -375,7 +375,7 @@ in (with-inputs { my-lib = mkSrc ./fixtures/fake-flake; - with-inputs-dep = mkSrc ./fixtures/with-inputs-flake; + with-inputs-dep = mkSrc ./fixtures/with-inputs-project; } { my-lib = s: { inputs.nixpkgs.follows = "with-inputs-dep/nixpkgs2"; }; @@ -422,7 +422,7 @@ in (with-inputs { my-lib = mkFlake { nixpkgs = mkSrc "/nested-nixpkgs"; } { }; - with-inputs-dep = mkSrc ./fixtures/with-inputs-flake; + with-inputs-dep = mkSrc ./fixtures/with-inputs-project; } { with-inputs-dep.inputs.nixpkgs2.follows = "my-lib/nixpkgs"; diff --git a/with-inputs.nix b/with-inputs.nix index 8d300ec..eb65e71 100644 --- a/with-inputs.nix +++ b/with-inputs.nix @@ -106,15 +106,60 @@ let let hasPath = sourceInfo ? outPath; isFlake = hasPath && (sourceInfo.flake or true); + defaultPath = sourceInfo.outPath + "/default.nix"; + defaultNix = import defaultPath; + defaultArgs = builtins.functionArgs defaultNix; + hadDefaultValues = builtins.all (withDefault: withDefault) (builtins.attrValues defaultArgs); + defaultExists = isFlake && builtins.pathExists defaultPath; + defaultGood = builtins.tryEval ( + defaultExists && builtins.isFunction defaultNix && defaultArgs ? inputsOverrides && hadDefaultValues + ); flakePath = sourceInfo.outPath + "/flake.nix"; flakeExists = isFlake && builtins.pathExists flakePath; - allGood = builtins.tryEval flakeExists; + flakeGood = builtins.tryEval flakeExists; in - if allGood.success && allGood.value then + if defaultGood.success && defaultGood.value then + mkDefaultWithInputsInput name sourceInfo defaultNix + else if flakeGood.success && flakeGood.value then mkFlakeInput name sourceInfo (import flakePath) else sourceInfo // { inherit sourceInfo; }; + mkDefaultWithInputsInput = + name: sourceInfo: defaultNix: + let + inputsOverrides = + let + recFollows = + let + follows = + input: + isFollows inputs.${input} + && ( + let + followRoot = builtins.head (builtins.split "/" inputs.${input}.follows); + in + followRoot == name || follows followRoot + ); + in + [ name ] ++ builtins.filter follows (builtins.attrNames inputs); + in + removeAttrs allInputs recFollows + // (builtins.mapAttrs (sub: spec: resolveSubInput name sub spec) (inputs.${name}.inputs or { })); + outputs = builtins.trace defaultNix (defaultNix { + inherit inputsOverrides; + }); + self = + sourceInfo + // outputs + // { + _type = "flake"; + inputs = outputs.inputs or { inherit self; }; + inherit outputs sourceInfo; + }; + in + self; + mkFlakeInput = name: sourceInfo: flake: let @@ -127,42 +172,14 @@ let builtins.functionArgs flake.outputs ); nonEmptyInputs = direct != { } || indirect != { }; - inputs = - if nonEmptyInputs then - indirect // direct - else - # Assume inputs are not handled by flake, but output function - # may still accept inputs overrides: we give it allInputs minus - # those that would obviously trigger infinite recursion - # (inputs defined as follows of inputs of the flake we are importing) - # plus inputs overrides declared for this flake. - let - recFollows = - let - follows = - input: - isFollows topLevelInputs.${input} - && ( - let - followRoot = builtins.head (builtins.split "/" topLevelInputs.${input}.follows); - in - followRoot == name || follows followRoot - ); - in - [ name ] ++ builtins.filter follows (builtins.attrNames topLevelInputs); - in - removeAttrs allInputs recFollows - // (builtins.mapAttrs (sub: spec: resolveSubInput name sub spec) ( - topLevelInputs.${name}.inputs or { } - )); - outputs = flake.outputs (inputs // { inherit self; }); + inputs = indirect // direct // { inherit self; }; + outputs = flake.outputs inputs; self = sourceInfo // outputs // { _type = "flake"; - inputs = if nonEmptyInputs then inputs else outputs.inputs or { inherit self; }; - inherit outputs sourceInfo; + inherit outputs inputs sourceInfo; }; in self; From be6ee97f0c1839477eb61486d2ca423e6130fd4a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Giraudeau Date: Tue, 1 Sep 2026 19:26:09 +0200 Subject: [PATCH 3/3] Update all template to implement "inputsOverrides" pattern. and update all pins. Exclude generated template files from formatting. --- templates/flake/default.nix | 7 +- templates/flake/flake.lock | 32 +- templates/lon/default.nix | 7 +- templates/lon/lon.lock | 24 +- templates/niv/default.nix | 7 +- templates/niv/nix/sources.json | 24 +- templates/niv/with-inputs.nix | 2 +- templates/nixtamal/default.nix | 7 +- templates/nixtamal/nix/tamal/default.nix | 234 ++++++------ templates/nixtamal/nix/tamal/lock.json | 12 +- templates/nixtamal/nix/tamal/manifest.kdl | 2 +- templates/npins/default.nix | 7 +- templates/npins/npins/default.nix | 45 ++- templates/npins/npins/sources.json | 34 +- templates/tack/.tack/default.nix | 430 ++++++++++++++++++++++ templates/tack/.tack/pins.lock.json | 32 +- templates/tack/default.nix | 7 +- templates/unflake/default.nix | 7 +- templates/unflake/unflake.nix | 91 ++--- treefmt.toml | 10 +- 20 files changed, 699 insertions(+), 322 deletions(-) create mode 100644 templates/tack/.tack/default.nix diff --git a/templates/flake/default.nix b/templates/flake/default.nix index 6399c0b..e46ecf9 100644 --- a/templates/flake/default.nix +++ b/templates/flake/default.nix @@ -1,7 +1,4 @@ { - with-inputs ? import ./with-inputs.nix, - follows ? ./follows.nix, - outputs ? ./outputs.nix, - ... + inputsOverrides ? { }, }: -with-inputs follows outputs +import ./with-inputs.nix [ ./follows.nix inputsOverrides ] ./outputs.nix diff --git a/templates/flake/flake.lock b/templates/flake/flake.lock index 71e1c84..1b3c5b8 100644 --- a/templates/flake/flake.lock +++ b/templates/flake/flake.lock @@ -2,11 +2,11 @@ "nodes": { "den": { "locked": { - "lastModified": 1776710169, - "narHash": "sha256-q4WXIX2E3w9Ld3MZ1Pl8Lh5SgrEFdEuzvY1Lj/Wo2kY=", + "lastModified": 1788469400, + "narHash": "sha256-2NVetxl+ycFWX4NZTCRqEnx4F37QajVpJe/b+n1Xw9I=", "owner": "denful", "repo": "den", - "rev": "0af82e24be89b9fd400bd0b58b0fed5ea0f269ad", + "rev": "36c8ba5c07e2aa7d4816c6314ff2b6656293dee4", "type": "github" }, "original": { @@ -20,11 +20,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1777659959, - "narHash": "sha256-ax3229dUvNuwTQwo2o68kOQ24dvOlJ/BrVYY4miD1bI=", + "lastModified": 1788487777, + "narHash": "sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak=", "owner": "nix-community", "repo": "home-manager", - "rev": "5c1b74905c7261e8280dcda3623dbe677a1bc158", + "rev": "693e8ce0fb240a73c116a03cfd7b19269c87af88", "type": "github" }, "original": { @@ -35,27 +35,27 @@ }, "nixpkgs": { "locked": { - "lastModified": 1775423009, - "narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=", + "lastModified": 1787209939, + "narHash": "sha256-WvvHR4kSQLAbtouMC/ruZ5UpLwlUcY3K4FAllMN+yGk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9", + "rev": "391b592eb44808b3bd0cb80bb71b63a5a118b8bb", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixpkgs-unstable", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1777548390, - "narHash": "sha256-WacE23EbHTsBKvr8cu+1DFNbP6Rh1brHUH5SDUI0NQI=", + "lastModified": 1788372231, + "narHash": "sha256-7aqErvrAEz/5OcPA5p3M+tVOqeYc4oJXkNIPXfCqxAw=", "owner": "nixos", "repo": "nixpkgs", - "rev": "7aaa00e7cc9be6c316cb5f6617bd740dd435c59d", + "rev": "9387b3fcc0c23c86661636da63faabad4235a0a6", "type": "github" }, "original": { @@ -76,11 +76,11 @@ "with-inputs": { "flake": false, "locked": { - "lastModified": 1775843270, - "narHash": "sha256-GgKZ4LyKDS9vd946UeArhYqBKw63LdH4JqfbfFf7qNw=", + "lastModified": 1788467216, + "narHash": "sha256-yFXytpx2/EzFCji5gbn819t9AcIOe5LnlFTnrIkoZ/Q=", "owner": "denful", "repo": "with-inputs", - "rev": "b4cbe858b381c0ee0fe617498549f0562090ad04", + "rev": "f0a6bc2464d744e6e3089f89ea6efeb9427c4acd", "type": "github" }, "original": { diff --git a/templates/lon/default.nix b/templates/lon/default.nix index 6399c0b..e46ecf9 100644 --- a/templates/lon/default.nix +++ b/templates/lon/default.nix @@ -1,7 +1,4 @@ { - with-inputs ? import ./with-inputs.nix, - follows ? ./follows.nix, - outputs ? ./outputs.nix, - ... + inputsOverrides ? { }, }: -with-inputs follows outputs +import ./with-inputs.nix [ ./follows.nix inputsOverrides ] ./outputs.nix diff --git a/templates/lon/lon.lock b/templates/lon/lon.lock index 163ab67..3fe141c 100644 --- a/templates/lon/lon.lock +++ b/templates/lon/lon.lock @@ -7,9 +7,9 @@ "owner": "denful", "repo": "den", "branch": "main", - "revision": "4f15fb43dd17a5ace3680cde297e31bc7e017c91", - "url": "https://github.com/denful/den/archive/4f15fb43dd17a5ace3680cde297e31bc7e017c91.tar.gz", - "hash": "sha256-zWghnHLAo5rrOxZZXA2E5rY+aEKx/MwVQzL5eCbexJc=" + "revision": "36c8ba5c07e2aa7d4816c6314ff2b6656293dee4", + "url": "https://github.com/denful/den/archive/36c8ba5c07e2aa7d4816c6314ff2b6656293dee4.tar.gz", + "hash": "sha256-2NVetxl+ycFWX4NZTCRqEnx4F37QajVpJe/b+n1Xw9I=" }, "home-manager": { "type": "GitHub", @@ -17,9 +17,9 @@ "owner": "nix-community", "repo": "home-manager", "branch": "master", - "revision": "1dc2d1f720ab17fc7981e087346bf54b26d284b1", - "url": "https://github.com/nix-community/home-manager/archive/1dc2d1f720ab17fc7981e087346bf54b26d284b1.tar.gz", - "hash": "sha256-G0F2rFORVcFkEvVdE/qWQTnYekIc77YP5/vRF9nZlxU=" + "revision": "693e8ce0fb240a73c116a03cfd7b19269c87af88", + "url": "https://github.com/nix-community/home-manager/archive/693e8ce0fb240a73c116a03cfd7b19269c87af88.tar.gz", + "hash": "sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak=" }, "nixpkgs": { "type": "GitHub", @@ -27,9 +27,9 @@ "owner": "nixos", "repo": "nixpkgs", "branch": "master", - "revision": "35adf7a938f3189febd497330e2f1fe09b632a69", - "url": "https://github.com/nixos/nixpkgs/archive/35adf7a938f3189febd497330e2f1fe09b632a69.tar.gz", - "hash": "sha256-cZPb6rWoDLkSFBFVXT9iznDfWQwSRdP+YYukEVGPPFY=" + "revision": "217ee364d167bf7ff819c7e2d1a1b8140f4afc0d", + "url": "https://github.com/nixos/nixpkgs/archive/217ee364d167bf7ff819c7e2d1a1b8140f4afc0d.tar.gz", + "hash": "sha256-5jcl+RckGopvvB0Zz1ORU2AKT39r2ncchMuXAntAxAw=" }, "with-inputs": { "type": "GitHub", @@ -37,9 +37,9 @@ "owner": "denful", "repo": "with-inputs", "branch": "main", - "revision": "dae6b8126fe613bfe12185d94d593db426a34daa", - "url": "https://github.com/denful/with-inputs/archive/dae6b8126fe613bfe12185d94d593db426a34daa.tar.gz", - "hash": "sha256-ZPGmgcJX+xBAbA0edQC59ewoH56LhAqkH1Y/5KjV9J0=" + "revision": "f0a6bc2464d744e6e3089f89ea6efeb9427c4acd", + "url": "https://github.com/denful/with-inputs/archive/f0a6bc2464d744e6e3089f89ea6efeb9427c4acd.tar.gz", + "hash": "sha256-yFXytpx2/EzFCji5gbn819t9AcIOe5LnlFTnrIkoZ/Q=" } } } diff --git a/templates/niv/default.nix b/templates/niv/default.nix index 6399c0b..e46ecf9 100644 --- a/templates/niv/default.nix +++ b/templates/niv/default.nix @@ -1,7 +1,4 @@ { - with-inputs ? import ./with-inputs.nix, - follows ? ./follows.nix, - outputs ? ./outputs.nix, - ... + inputsOverrides ? { }, }: -with-inputs follows outputs +import ./with-inputs.nix [ ./follows.nix inputsOverrides ] ./outputs.nix diff --git a/templates/niv/nix/sources.json b/templates/niv/nix/sources.json index c3fa5fc..e6fd0f8 100644 --- a/templates/niv/nix/sources.json +++ b/templates/niv/nix/sources.json @@ -5,10 +5,10 @@ "homepage": "https://den.oeiuwq.com/", "owner": "denful", "repo": "den", - "rev": "0af82e24be89b9fd400bd0b58b0fed5ea0f269ad", - "sha256": "0insm3sqyjwdpnrlnx05n61547ifgkwx86bkfx5hzpw4glhrg1db", + "rev": "36c8ba5c07e2aa7d4816c6314ff2b6656293dee4", + "sha256": "1ln3axyzmnzg4mlkasnhgqbphz0jd8j4qnc3bxbc3jby36vmxmfq", "type": "tarball", - "url": "https://github.com/denful/den/archive/0af82e24be89b9fd400bd0b58b0fed5ea0f269ad.tar.gz", + "url": "https://github.com/denful/den/archive/36c8ba5c07e2aa7d4816c6314ff2b6656293dee4.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "home-manager": { @@ -17,10 +17,10 @@ "homepage": "https://nix-community.github.io/home-manager/", "owner": "nix-community", "repo": "home-manager", - "rev": "5c1b74905c7261e8280dcda3623dbe677a1bc158", - "sha256": "1cnmhdlf462nmp0rz56fvghkdr4hpj7dla0c9nqdpg2lszdzc7bb", + "rev": "693e8ce0fb240a73c116a03cfd7b19269c87af88", + "sha256": "02fhgv58zicwlr287rfcs7pwmww1a9gv0zjid3bwyiqrvvadx3s6", "type": "tarball", - "url": "https://github.com/nix-community/home-manager/archive/5c1b74905c7261e8280dcda3623dbe677a1bc158.tar.gz", + "url": "https://github.com/nix-community/home-manager/archive/693e8ce0fb240a73c116a03cfd7b19269c87af88.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "nixpkgs": { @@ -29,10 +29,10 @@ "homepage": "", "owner": "nixos", "repo": "nixpkgs", - "rev": "7aaa00e7cc9be6c316cb5f6617bd740dd435c59d", - "sha256": "00im6i10slkya33vmmb1lhzmnlqcnppp5z7s580kn78vf7dh99sr", + "rev": "9387b3fcc0c23c86661636da63faabad4235a0a6", + "sha256": "0364mbq5s3yjj1bq5qhwwsllxmgsrjfydh6377wky4y0zap89apd", "type": "tarball", - "url": "https://github.com/nixos/nixpkgs/archive/7aaa00e7cc9be6c316cb5f6617bd740dd435c59d.tar.gz", + "url": "https://github.com/nixos/nixpkgs/archive/9387b3fcc0c23c86661636da63faabad4235a0a6.tar.gz", "url_template": "https://github.com///archive/.tar.gz" }, "with-inputs": { @@ -41,10 +41,10 @@ "homepage": "https://dendritic.oeiuwq.com/ecosystem/with-inputs/", "owner": "denful", "repo": "with-inputs", - "rev": "b4cbe858b381c0ee0fe617498549f0562090ad04", - "sha256": "1p58zdbprnx74vwd2bdp1qmq32l55gh52fnyfxpjy3capkh9j0hs", + "rev": "f0a6bc2464d744e6e3089f89ea6efeb9427c4acd", + "sha256": "1x37524srrsljkkr4yqfq80pvnypzjwq3f9q1b2lrz3nkjvg4mf8", "type": "tarball", - "url": "https://github.com/denful/with-inputs/archive/b4cbe858b381c0ee0fe617498549f0562090ad04.tar.gz", + "url": "https://github.com/denful/with-inputs/archive/f0a6bc2464d744e6e3089f89ea6efeb9427c4acd.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } diff --git a/templates/niv/with-inputs.nix b/templates/niv/with-inputs.nix index e13d6ef..3b8c780 100644 --- a/templates/niv/with-inputs.nix +++ b/templates/niv/with-inputs.nix @@ -1 +1 @@ -(import (import ./nix/sources.nix).with-inputs).from.npins ./. +(import (import ./nix/sources.nix).with-inputs).from.niv ./. diff --git a/templates/nixtamal/default.nix b/templates/nixtamal/default.nix index 6399c0b..e46ecf9 100644 --- a/templates/nixtamal/default.nix +++ b/templates/nixtamal/default.nix @@ -1,7 +1,4 @@ { - with-inputs ? import ./with-inputs.nix, - follows ? ./follows.nix, - outputs ? ./outputs.nix, - ... + inputsOverrides ? { }, }: -with-inputs follows outputs +import ./with-inputs.nix [ ./follows.nix inputsOverrides ] ./outputs.nix diff --git a/templates/nixtamal/nix/tamal/default.nix b/templates/nixtamal/nix/tamal/default.nix index 062146d..8a1fa4f 100644 --- a/templates/nixtamal/nix/tamal/default.nix +++ b/templates/nixtamal/nix/tamal/default.nix @@ -1,143 +1,129 @@ /* - SPDX-FileCopyrightText: 2025–2026 toastal - SPDX-FileCopyrightText: 2026 Nixtamal contributors - SPDX-License-Identifier: ISC +SPDX-FileCopyrightText: 2025–2026 toastal +SPDX-FileCopyrightText: 2026 Nixtamal contributors +SPDX-License-Identifier: ISC - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice & this permission notice appear in all copies. +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice & this permission notice appear in all copies. - THE SOFTWARE IS PROVIDED “AS IS” & ISC DISCLAIMS ALL WARRANTIES WITH REGARD - TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY & - FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, - OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF - USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - OF THIS SOFTWARE. +THE SOFTWARE IS PROVIDED “AS IS” & ISC DISCLAIMS ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY & +FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, +OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +OF THIS SOFTWARE. - ────────────────────────────────────────────────────────────────────────────── - ┏┓╻+╻ ╱┏┳┓┏┓┏┳┓┏┓╻ - ┃┃┃┃┗━┓╹┃╹┣┫┃┃┃┣┫┃ This file was generated by Nixtamal. - ╹┗┛╹╱ ╹ ╹ ╹╹╹ ╹╹╹┗┛ Do not edit as it will be overwritten. - ────────────────────────────────────────────────────────────────────────────── +────────────────────────────────────────────────────────────────────────────── +┏┓╻+╻ ╱┏┳┓┏┓┏┳┓┏┓╻ +┃┃┃┃┗━┓╹┃╹┣┫┃┃┃┣┫┃ This file was generated by Nixtamal. +╹┗┛╹╱ ╹ ╹ ╹╹╹ ╹╹╹┗┛ Do not edit as it will be overwritten. +────────────────────────────────────────────────────────────────────────────── */ { - system ? builtins.currentSystem, - bootstrap-nixpkgs ? null, - bootstrap-nixpkgs-lock-name ? null, + system ? builtins.currentSystem, + bootstrap-nixpkgs ? null, + bootstrap-nixpkgs-lock-name ? null, + bootstrap-pkgs ? null, }: +let lock = builtins.fromJSON (builtins.readFile ./lock.json); in +assert (lock.v == "1.3.0"); let - lock = builtins.fromJSON (builtins.readFile ./lock.json); -in -assert (lock.v == "1.0.0"); -let - hash-token = { - "0" = "sha256"; - "1" = "sha512"; - "2" = "blake3"; - }; + hash-token = { + "0" = "sha256"; + "1" = "sha512"; + "2" = "blake3"; + }; - try-fetch = - name: fetcher: - let - try-fetch' = - failed-urls: url: urls: - let - result = builtins.tryEval (fetcher url); - in - if result.success then - result.value - else - let - failed-urls' = [ url ] ++ failed-urls; - in - if builtins.length urls <= 0 then - let - fus = builtins.concatStringsSep " " failed-urls'; - in - throw "Input 「${name}」fetchable @ [ ${fus} ]" - else - try-fetch' failed-urls' (builtins.head urls) (builtins.tail urls); - in - try-fetch' [ ]; + try-fetch = input-name: fetcher: + let + try-fetch' = failed-urls: url: urls: + let result = builtins.tryEval (fetcher url); in + if result.success then + result.value + else + let failed-urls' = [ url ] ++ failed-urls; in + if builtins.length urls <= 0 then + let fus = builtins.concatStringsSep " " failed-urls'; in + throw "Input 「${input-name}」fetchable @ [ ${fus} ]" + else + try-fetch' failed-urls' (builtins.head urls) (builtins.tail urls); + in + try-fetch' [ ]; - builtin-fetch-tarball = - { - name, - kind, - hash, - }: - try-fetch name ( - url: - builtins.fetchTarball { - inherit url; - ${hash-token.${builtins.toString hash.al}} = hash.vl; - } - ) kind.ur kind.ms; + builtin-fetch-tarball = {input-name, name, kind, hash}: + try-fetch input-name (url: + builtins.fetchTarball ({ + inherit url; + ${hash-token.${builtins.toString hash.al}} = hash.vl; + } + // (if name != null then {inherit name;} else {})) + ) kind.ur kind.ms; - builtin-to-input = - name: input: - let - k = builtins.head input.kd; - in - if k == 1 then - builtin-fetch-tarball { - inherit name; - kind = builtins.elemAt input.kd 1; - hash = input.ha; - } - else - throw "Unsupported input kind “${builtins.toString k}”."; + builtin-to-input = input-name: input: + let + name = input.sn; + hash = input.ha; + k = builtins.head input.kd; + in + if k == 1 then + builtin-fetch-tarball { + inherit name; + input-name = input-name; + kind = builtins.elemAt input.kd 1; + hash = input.ha; + } + else + throw "Unsupported input kind “${builtins.toString k}”."; - nixpkgs' = - if builtins.isNull bootstrap-nixpkgs then - builtin-to-input "nixpkgs-for-nixtamal" ( - if builtins.isString bootstrap-nixpkgs-lock-name then - lock.i.${bootstrap-nixpkgs-lock-name} - else - lock.i.nixpkgs-nixtamal or lock.i.nixpkgs - ) - else - bootstrap-nixpkgs; + nixpkgs' = + if builtins.isNull bootstrap-nixpkgs then + builtin-to-input "nixpkgs-for-nixtamal" ( + if builtins.isString bootstrap-nixpkgs-lock-name then + lock.i.${bootstrap-nixpkgs-lock-name} + else + lock.i.nixpkgs-nixtamal or lock.i.nixpkgs + ) + else + bootstrap-nixpkgs; - pkgs = import nixpkgs' { inherit system; }; + pkgs = + if builtins.isAttrs bootstrap-pkgs then + bootstrap-pkgs + else + import nixpkgs' {inherit system;}; - inherit (pkgs) lib; + inherit (pkgs) lib; - fetch-zip = - { - name, - kind, - hash, - }: - pkgs.fetchzip { - inherit name; - url = kind.ur; - hash = hash.vl; - } - // lib.optionalAttrs (builtins.length kind.ms > 0) { urls = kind.ms; }; + fetch-zip = {input-name, name, kind, hash}: pkgs.fetchzip ({ + url = kind.ur; + hash = hash.vl; + } + // lib.optionalAttrs (name != null) {inherit name;} + // lib.optionalAttrs (builtins.length kind.ms > 0) {urls = kind.ms;}); - to-input = - name: input: - let - k = builtins.head input.kd; - raw-input = - if k == 1 then - let - kind = builtins.elemAt input.kd 1; - fetch_time = kind.ft; - hash = input.ha; - in - if fetch_time == 0 then - fetch-zip { inherit name kind hash; } - else if fetch_time == 1 then - builtin-fetch-tarball { inherit name kind hash; } - else - throw "Unsupported fetch time ${fetch_time}." - else - throw "Unsupported input kind “${builtins.toString}”."; - in - raw-input; + to-input = input-name: input: + let + name = input.sn; + hash = input.ha; + k = builtins.head input.kd; + raw-input = + if k == 1 then + let + kind = builtins.elemAt input.kd 1; + fetch_time = kind.ft; + in + if fetch_time == 0 then + fetch-zip {inherit input-name name kind hash;} + else if fetch_time == 1 then + builtin-fetch-tarball {inherit input-name name kind hash;} + else + throw "Unsupported fetch time ${fetch_time}." + else + throw "Unsupported input kind “${builtins.toString k}”."; + in + raw-input; in builtins.mapAttrs to-input lock.i diff --git a/templates/nixtamal/nix/tamal/lock.json b/templates/nixtamal/nix/tamal/lock.json index c9177f4..92d67ac 100644 --- a/templates/nixtamal/nix/tamal/lock.json +++ b/templates/nixtamal/nix/tamal/lock.json @@ -1,9 +1,9 @@ -{"v":"1.0.0" +{"v":"1.3.0" ,"i":{ -"nixpkgs":{"kd":[1,{"ft":0,"ur":"https://github.com/NixOS/nixpkgs/archive/1c3fe55ad329cbcb28471bb30f05c9827f724c76.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE="},"fv":"1c3fe55ad329cbcb28471bb30f05c9827f724c76","ps":[]} -,"den":{"kd":[1,{"ft":0,"ur":"https://github.com/denful/den/archive/0af82e24be89b9fd400bd0b58b0fed5ea0f269ad.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-q4WXIX2E3w9Ld3MZ1Pl8Lh5SgrEFdEuzvY1Lj/Wo2kY="},"fv":"0af82e24be89b9fd400bd0b58b0fed5ea0f269ad","ps":[]} -,"home-manager":{"kd":[1,{"ft":0,"ur":"https://github.com/nix-community/home-manager/archive/9cb587ade2aa1b4a7257f0238d41072690b0ca4f.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-egYNbRrkn+6SwTHinhdb6WUfzzdC3nXfCRqS321VylY="},"fv":"9cb587ade2aa1b4a7257f0238d41072690b0ca4f","ps":[]} -,"with-inputs":{"kd":[1,{"ft":0,"ur":"https://github.com/denful/with-inputs/archive/b4cbe858b381c0ee0fe617498549f0562090ad04.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-GgKZ4LyKDS9vd946UeArhYqBKw63LdH4JqfbfFf7qNw="},"fv":"b4cbe858b381c0ee0fe617498549f0562090ad04","ps":[]} +"nixpkgs":{"sn":"nixpkgs-src","kd":[1,{"ft":0,"ur":"https://github.com/NixOS/nixpkgs/archive/3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-bc7rSpXIdn9QWGNqfWcPZWOhEVF8NoeAZkWq0XWnf/k="},"fv":"3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2","ps":[]} +,"den":{"sn":"den-src","kd":[1,{"ft":0,"ur":"https://github.com/denful/den/archive/36c8ba5c07e2aa7d4816c6314ff2b6656293dee4.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-2NVetxl+ycFWX4NZTCRqEnx4F37QajVpJe/b+n1Xw9I="},"fv":"36c8ba5c07e2aa7d4816c6314ff2b6656293dee4","ps":[]} +,"home-manager":{"sn":"home-manager-src","kd":[1,{"ft":0,"ur":"https://github.com/nix-community/home-manager/archive/693e8ce0fb240a73c116a03cfd7b19269c87af88.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak="},"fv":"693e8ce0fb240a73c116a03cfd7b19269c87af88","ps":[]} +,"with-inputs":{"sn":"with-inputs-src","kd":[1,{"ft":0,"ur":"https://github.com/denful/with-inputs/archive/f0a6bc2464d744e6e3089f89ea6efeb9427c4acd.tar.gz","ms":[]}],"ha":{"al":0,"vl":"sha256-yFXytpx2/EzFCji5gbn819t9AcIOe5LnlFTnrIkoZ/Q="},"fv":"f0a6bc2464d744e6e3089f89ea6efeb9427c4acd","ps":[]} } ,"p":{} -} +} \ No newline at end of file diff --git a/templates/nixtamal/nix/tamal/manifest.kdl b/templates/nixtamal/nix/tamal/manifest.kdl index d9106fa..91c1f41 100644 --- a/templates/nixtamal/nix/tamal/manifest.kdl +++ b/templates/nixtamal/nix/tamal/manifest.kdl @@ -1,7 +1,7 @@ // ┏┓╻+╻ ╱┏┳┓┏┓┏┳┓┏┓╻ // ┃┃┃┃┗━┓╹┃╹┣┫┃┃┃┣┫┃ Read the manpage: // ╹┗┛╹╱ ╹ ╹ ╹╹╹ ╹╹╹┗┛ $ man nixtamal-manifest -version "1.0.0" +version "1.3.0" inputs { nixpkgs { archive { diff --git a/templates/npins/default.nix b/templates/npins/default.nix index 6399c0b..e46ecf9 100644 --- a/templates/npins/default.nix +++ b/templates/npins/default.nix @@ -1,7 +1,4 @@ { - with-inputs ? import ./with-inputs.nix, - follows ? ./follows.nix, - outputs ? ./outputs.nix, - ... + inputsOverrides ? { }, }: -with-inputs follows outputs +import ./with-inputs.nix [ ./follows.nix inputsOverrides ] ./outputs.nix diff --git a/templates/npins/npins/default.nix b/templates/npins/npins/default.nix index 884fc8c..8ec5eca 100644 --- a/templates/npins/npins/default.nix +++ b/templates/npins/npins/default.nix @@ -65,7 +65,9 @@ let if pkgs == null then { inherit (builtins) fetchTarball fetchurl; - # For some fucking reason, fetchGit has a different signature than the other builtin fetchers … + # Frustratingly, due to flakes and `fetchTree`, `fetchGit` + # has a different signature than the other builtin + # fetchers fetchGit = args: (builtins.fetchGit args).outPath; } else @@ -95,7 +97,6 @@ let }; }; - # Dispatch to the correct code path based on the type path = if spec.type == "Git" then mkGitSource fetchers spec @@ -105,8 +106,8 @@ let mkPyPiSource fetchers spec else if spec.type == "Channel" then mkChannelSource fetchers spec - else if spec.type == "Tarball" then - mkTarballSource fetchers spec + else if spec.type == "Url" || spec.type == "MutableUrl" then + mkUrlSource fetchers spec else if spec.type == "Container" then mkContainerSource pkgs spec else @@ -192,16 +193,20 @@ let sha256 = hash; }; - mkTarballSource = - { fetchTarball, ... }: + mkUrlSource = + { + fetchTarball, + fetchurl, + ... + }: { url, - locked_url ? url, hash, + unpack, ... }: - fetchTarball { - url = locked_url; + (if unpack then fetchTarball else fetchurl) { + inherit url; sha256 = hash; }; @@ -211,16 +216,22 @@ let image_name, image_tag, image_digest, + hash, ... - }: + }@args: if pkgs == null then builtins.throw "container sources require passing in a Nixpkgs value: https://github.com/andir/npins/blob/master/README.md#using-the-nixpkgs-fetchers" else - pkgs.dockerTools.pullImage { - imageName = image_name; - imageDigest = image_digest; - finalImageTag = image_tag; - }; + pkgs.dockerTools.pullImage ( + { + imageName = image_name; + imageDigest = image_digest; + finalImageTag = image_tag; + hash = hash; + } + // (if args.arch or null != null then { arch = args.arch; } else { }) + ); + in mkFunctor ( { @@ -231,7 +242,7 @@ mkFunctor ( if builtins.isPath input then # while `readFile` will throw an error anyways if the path doesn't exist, # we still need to check beforehand because *our* error can be caught but not the one from the builtin - # *piegames sighs* + # See: if builtins.pathExists input then builtins.fromJSON (builtins.readFile input) else @@ -242,7 +253,7 @@ mkFunctor ( throw "Unsupported input type ${builtins.typeOf input}, must be a path or an attrset"; version = data.version; in - if version == 7 then + if version == 8 then builtins.mapAttrs (name: spec: mkFunctor (mkSource name spec)) data.pins else throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`" diff --git a/templates/npins/npins/sources.json b/templates/npins/npins/sources.json index 9930065..3265c04 100644 --- a/templates/npins/npins/sources.json +++ b/templates/npins/npins/sources.json @@ -11,10 +11,10 @@ "version_upper_bound": null, "release_prefix": null, "submodules": false, - "version": "v0.16.0", - "revision": "927f4d8e2be40d05c976a91bbec66238c622bbf5", - "url": "https://api.github.com/repos/denful/den/tarball/refs/tags/v0.16.0", - "hash": "sha256-RNbDvS6voiq2GalVHRt6w2EpdlEmmCjUAb6fvPO9PnE=" + "version": "v0.18.0", + "revision": "5df0987658d6e44268abba953406480e9f066928", + "url": "https://api.github.com/repos/denful/den/tarball/refs/tags/v0.18.0", + "hash": "sha256-8Mntz200pNVIF2l8twpdc4wvEFqzrlkICz2tvkfwz0s=" }, "home-manager": { "type": "Git", @@ -25,32 +25,30 @@ }, "branch": "master", "submodules": false, - "revision": "5c1b74905c7261e8280dcda3623dbe677a1bc158", - "url": "https://github.com/nix-community/home-manager/archive/5c1b74905c7261e8280dcda3623dbe677a1bc158.tar.gz", - "hash": "sha256-ax3229dUvNuwTQwo2o68kOQ24dvOlJ/BrVYY4miD1bI=" + "revision": "693e8ce0fb240a73c116a03cfd7b19269c87af88", + "url": "https://github.com/nix-community/home-manager/archive/693e8ce0fb240a73c116a03cfd7b19269c87af88.tar.gz", + "hash": "sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak=" }, "nixpkgs": { "type": "Channel", "name": "nixpkgs-unstable", - "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre989763.7aaa00e7cc9b/nixexprs.tar.xz", - "hash": "sha256-d98DFUUUUqRBs5etNBc06hg2BluQ8aIukRMBJop7nUE=" + "artifact": "nixexprs.tar.xz", + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.11pre1066425.9387b3fcc0c2/nixexprs.tar.xz", + "hash": "sha256-4ua7nluyuvF5SVIa2nBTCTW9ZiuzYuSBZQk4EdwOWhI=" }, "with-inputs": { - "type": "GitRelease", + "type": "Git", "repository": { "type": "GitHub", "owner": "denful", "repo": "with-inputs" }, - "pre_releases": false, - "version_upper_bound": null, - "release_prefix": null, + "branch": "main", "submodules": false, - "version": "v0.3.0", - "revision": "920b35279fb800c147017c8332e04a3daa994af4", - "url": "https://api.github.com/repos/denful/with-inputs/tarball/refs/tags/v0.3.0", - "hash": "sha256-3rNFfdLJ/nivZkgBoVi+M8tJQQpupuojaHyNig1Qzrs=" + "revision": "f0a6bc2464d744e6e3089f89ea6efeb9427c4acd", + "url": "https://github.com/denful/with-inputs/archive/f0a6bc2464d744e6e3089f89ea6efeb9427c4acd.tar.gz", + "hash": "sha256-yFXytpx2/EzFCji5gbn819t9AcIOe5LnlFTnrIkoZ/Q=" } }, - "version": 7 + "version": 8 } diff --git a/templates/tack/.tack/default.nix b/templates/tack/.tack/default.nix new file mode 100644 index 0000000..1593547 --- /dev/null +++ b/templates/tack/.tack/default.nix @@ -0,0 +1,430 @@ +# SPDX-License-Identifier: EUPL-1.2 +# tack-managed resolver. delete this line to take ownership; tack will leave it alone afterwards. + +let + inherit (builtins) + attrNames + attrValues + concatMap + elem + elemAt + filter + foldl' + fromJSON + head + intersectAttrs + isList + isString + listToAttrs + mapAttrs + match + pathExists + readFile + substring + tail + trace + ; + + call = + { + overrides ? { }, + }: + let + pins = fromTOML (readFile ./pins.toml); + lock = fromJSON (readFile ./pins.lock.json); + all_follow_raw = pins.all_follow or { }; + + # flatten `target = [aliases]` rows alongside `alias = "target"` rows + all_follow = foldl' ( + acc: key: + let + val = all_follow_raw.${key}; + in + if isList val then + acc + // { + ${key} = key; + } + // listToAttrs ( + map (a: { + name = a; + value = key; + }) val + ) + else if isString val then + acc // { ${key} = val; } + else + acc + ) { } (attrNames all_follow_raw); + + knownTypes = [ + "github" + "gitlab" + "git" + "tarball" + "path" + "indirect" + ]; + + # path nodes are convenience pins, so return the live local path directly + # because fetchTree rejects unlocked paths in pure eval + fetchPin = + name: + if !(lock ? ${name}) then + throw "tack: pin '${name}' has no lock entry; run tack update" + else + let + node = lock.${name}; + in + if (node.type or "") == "path" then + { + outPath = if substring 0 1 node.path == "/" then node.path else ./. + ("/" + node.path); + lastModified = node.lastModified or 0; + } + // (if node ? narHash then { inherit (node) narHash; } else { }) + else if !(elem (node.type or "") knownTypes) then + throw "tack: unknown lock type '${node.type or "?"}' for pin '${name}'" + else + fetchTree node; + + fetchFixed = + { name, entry }: + let + raw = derivation { + inherit name; + inherit (entry) url; + builder = "builtin:fetchurl"; + system = "builtin"; + outputHash = entry.sha256; + outputHashAlgo = "sha256"; + outputHashMode = "flat"; + }; + unpacked = derivation { + inherit name; + builder = "builtin:unpack-channel"; + system = "builtin"; + src = raw; + channelName = name; + }; + in + if (entry.unpack or "file") == "tarball" then unpacked.outPath + "/" + name else raw.outPath; + + resolveSpec = + { upLock, spec }: + if isList spec then + walkPath { + inherit upLock; + nodeName = upLock.root; + path = spec; + } + else + spec; + + walkPath = + { + upLock, + nodeName, + path, + }: + if path == [ ] then + nodeName + else if !(upLock.nodes ? ${nodeName}) then + throw "tack: follows path dead-end: no node '${nodeName}' in flake.lock" + else + let + key = head path; + inputs = upLock.nodes.${nodeName}.inputs or { }; + in + if !(inputs ? ${key}) then + throw "tack: follows path dead-end: node '${nodeName}' has no input '${key}'" + else + walkPath { + inherit upLock; + nodeName = resolveSpec { + inherit upLock; + spec = inputs.${key}; + }; + path = tail path; + }; + + followsFor = + pin: + let + rules = removeAttrs all_follow (pin.exclude_follow or [ ]); + in + { + level = rules // (pin.follows or { }); + deep = rules; + }; + + resolveFollows = mapAttrs ( + _: target: self.${target} or (throw "tack: follows target '${target}' is not a pin") + ); + + # follows key is `flake:name`, `tack:name`, or bare `name` + # project onto one side, rekeyed to bare names + followsForSide = + { side, follows }: + listToAttrs ( + concatMap ( + key: + let + m = match "(flake|tack):(.*)" key; + in + if m == null then + [ + { + name = key; + value = follows.${key}; + } + ] + else if head m == side then + [ + { + name = elemAt m 1; + value = follows.${key}; + } + ] + else + [ ] + ) (attrNames follows) + ); + + mkCallerInputs = + { + upLock, + nodeName, + rawInputs, + levelFollows, + deepFollows, + }: + let + resolved = resolveFollows levelFollows; + in + mapAttrs ( + n: _decl: + resolved.${n} or ( + if upLock != null then + let + ref = + (upLock.nodes.${nodeName}.inputs or { }).${n} + or (throw "tack: input '${n}' declared but not in flake.lock node '${nodeName}'"); + childName = resolveSpec { + inherit upLock; + spec = ref; + }; + childNode = upLock.nodes.${childName}; + childSrc = fetchTree childNode.locked; + in + if childNode.flake or true then + evalTransitive { + inherit upLock; + nodeName = childName; + sourceInfo = childSrc; + follows = deepFollows; + } + else + childSrc + else + throw "tack: no flake.lock; cannot resolve input '${n}'" + ) + ) rawInputs; + + mkFlakeResult = + { + sourceInfo, + flakeDir, + callerInputs, + outputs, + }: + outputs + // sourceInfo + // { + outPath = flakeDir; + inputs = callerInputs; + inherit outputs sourceInfo; + _type = "flake"; + }; + + evalFlake = + { + sourceInfo, + flakeDir, + upLock, + nodeName, + levelFollows, + deepFollows, + }: + let + raw = import (flakeDir + "/flake.nix"); + + tackPinsPath = flakeDir + "/.tack/pins.toml"; + hasTack = pathExists tackPinsPath; + upPins = if hasTack then fromTOML (readFile tackPinsPath) else { }; + + # project follows onto each side, keep only names that side has + # bare follow reaches both; `flake:`/`tack:` reaches just one + tackOverrides = resolveFollows ( + intersectAttrs (upPins.inputs or { }) (followsForSide { + side = "tack"; + follows = levelFollows; + }) + ); + flakeLevel = intersectAttrs (raw.inputs or { }) (followsForSide { + side = "flake"; + follows = levelFollows; + }); + + # deep follows pass down raw, so each descendant re-projects per side + callerInputs = mkCallerInputs { + inherit upLock nodeName deepFollows; + rawInputs = raw.inputs or { }; + levelFollows = flakeLevel; + }; + + # upstream declares its outputs forward tackOverrides; a closed `{ self }:` + # would throw on the extra kwarg, so forward only when declared + supportsOverrides = (upPins.tack or { }).recomposable or false; + + extraArgs = if supportsOverrides && tackOverrides != { } then { inherit tackOverrides; } else { }; + + outputs = raw.outputs (callerInputs // extraArgs // { self = result; }); + + result = + let + base = mkFlakeResult { + inherit + sourceInfo + flakeDir + callerInputs + outputs + ; + }; + in + if hasTack && tackOverrides != { } && !supportsOverrides then + trace "tack: ${flakeDir}: not marked recomposable (set [tack] recomposable = true); overrides will not reach upstream" base + else + base; + in + result; + + evalTransitive = + { + upLock, + nodeName, + sourceInfo, + follows, + }: + evalFlake { + inherit upLock nodeName sourceInfo; + flakeDir = sourceInfo.outPath; + levelFollows = follows; + deepFollows = follows; + }; + + evalTopFlake = + { sourceInfo, pin }: + let + flakeDir = sourceInfo.outPath + (if pin ? dir then "/" + pin.dir else ""); + upLockPath = flakeDir + "/flake.lock"; + upLock = if pathExists upLockPath then fromJSON (readFile upLockPath) else null; + rootNode = if upLock != null then upLock.root else null; + f = followsFor pin; + in + evalFlake { + inherit sourceInfo flakeDir upLock; + nodeName = rootNode; + levelFollows = f.level; + deepFollows = f.deep; + }; + + evalFetch = + { + sourceInfo, + pin, + subdir, + }: + let + path = sourceInfo.outPath + subdir; + tackPinsPath = path + "/.tack/pins.toml"; + hasTack = pathExists tackPinsPath; + upPins = if hasTack then fromTOML (readFile tackPinsPath) else { }; + f = followsFor pin; + # a fetch drill-in is tack-only + tackOverrides = resolveFollows ( + intersectAttrs (upPins.inputs or { }) (followsForSide { + side = "tack"; + follows = f.level; + }) + ); + in + # only override tack files within a `fetch`, since there's no flake.lock + if hasTack && tackOverrides != { } then + let + upstream = import (path + "/.tack"); + in + # old resolvers return a plain attrset, not a callable functor + if upstream ? __functor then + (upstream { overrides = tackOverrides; }) // { outPath = path; } + else + trace "tack: ${path}: upstream .tack predates override support; overrides will not reach it" path + else + path; + + loadPin = + { name, pin }: + let + pinType = pin.type or (if pin.flake or true then "flake" else "fetch"); + subdir = if pin ? dir then "/" + pin.dir else ""; + in + if pinType == "fixed" then + fetchFixed { + inherit name; + entry = lock.${name}; + } + else + let + sourceInfo = fetchPin name; + in + if pinType == "flake" then + evalTopFlake { inherit sourceInfo pin; } + else + evalFetch { inherit sourceInfo pin subdir; }; + + declared = pins.inputs or { }; + + # undeclared lock entries are synthesised into toplevels by auto-dedup + # only when referenced as [all_follow] targets + autoTargets = listToAttrs ( + map (target: { + name = target; + value = true; + }) (attrValues all_follow) + ); + autoNames = filter (n: !(declared ? ${n}) && autoTargets ? ${n}) (attrNames lock); + autoPin = + name: + let + sourceInfo = fetchPin name; + in + if pathExists (sourceInfo.outPath + "/flake.nix") then + evalTopFlake { + inherit sourceInfo; + pin = { }; + } + else + sourceInfo; + + self = + (mapAttrs (name: pin: loadPin { inherit name pin; }) declared) + // listToAttrs ( + map (name: { + inherit name; + value = autoPin name; + }) autoNames + ) + // overrides; + in + self // { __functor = _: call; }; +in +call { } diff --git a/templates/tack/.tack/pins.lock.json b/templates/tack/.tack/pins.lock.json index 780ee47..24fc191 100644 --- a/templates/tack/.tack/pins.lock.json +++ b/templates/tack/.tack/pins.lock.json @@ -1,34 +1,34 @@ { "den": { - "lastModified": 1781197004, - "narHash": "sha256-lSaq5IFmRVySGEazhez4+IQN+KtWxNXBUukNNJqW+VU=", + "type": "github", "owner": "denful", "repo": "den", - "rev": "fe63b4bff3358e51687b6f88fa8746d5b3dc1bd5", - "type": "github" + "rev": "36c8ba5c07e2aa7d4816c6314ff2b6656293dee4", + "narHash": "sha256-2NVetxl+ycFWX4NZTCRqEnx4F37QajVpJe/b+n1Xw9I=", + "lastModified": 1788469400 }, "home-manager": { - "lastModified": 1781189114, - "narHash": "sha256-5inaamLgUMWy+MOBE9ChF9QAF1o/74LFuHkI0W/9rqc=", + "type": "github", "owner": "nix-community", "repo": "home-manager", - "rev": "486595d2cf49cfcd649b58a284fa11ac0e34da22", - "type": "github" + "rev": "693e8ce0fb240a73c116a03cfd7b19269c87af88", + "narHash": "sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak=", + "lastModified": 1788487777 }, "nixpkgs": { - "lastModified": 1780749050, - "narHash": "sha256-3av0pIjlOWQ6rDbNOmpUSvbNnJkGORQKKjb4LtCZsIY=", + "type": "github", "owner": "NixOS", "repo": "nixpkgs", - "rev": "a799d3e3886da994fa307f817a6bc705ae538eeb", - "type": "github" + "rev": "3ed67ec0a4d3c7ab4ae1f04f8ee8df07bfa506a2", + "narHash": "sha256-bc7rSpXIdn9QWGNqfWcPZWOhEVF8NoeAZkWq0XWnf/k=", + "lastModified": 1788316716 }, "with-inputs": { - "lastModified": 1777938927, - "narHash": "sha256-efneEYyUSQ7HOg/peGvfotb/P1N1+WWp10P1kMQSpe8=", + "type": "github", "owner": "denful", "repo": "with-inputs", - "rev": "e8729bdb4afb57fc72c2a7084a77a7ee09c7e38f", - "type": "github" + "rev": "f0a6bc2464d744e6e3089f89ea6efeb9427c4acd", + "narHash": "sha256-yFXytpx2/EzFCji5gbn819t9AcIOe5LnlFTnrIkoZ/Q=", + "lastModified": 1788467216 } } diff --git a/templates/tack/default.nix b/templates/tack/default.nix index 6399c0b..e46ecf9 100644 --- a/templates/tack/default.nix +++ b/templates/tack/default.nix @@ -1,7 +1,4 @@ { - with-inputs ? import ./with-inputs.nix, - follows ? ./follows.nix, - outputs ? ./outputs.nix, - ... + inputsOverrides ? { }, }: -with-inputs follows outputs +import ./with-inputs.nix [ ./follows.nix inputsOverrides ] ./outputs.nix diff --git a/templates/unflake/default.nix b/templates/unflake/default.nix index 6399c0b..e46ecf9 100644 --- a/templates/unflake/default.nix +++ b/templates/unflake/default.nix @@ -1,7 +1,4 @@ { - with-inputs ? import ./with-inputs.nix, - follows ? ./follows.nix, - outputs ? ./outputs.nix, - ... + inputsOverrides ? { }, }: -with-inputs follows outputs +import ./with-inputs.nix [ ./follows.nix inputsOverrides ] ./outputs.nix diff --git a/templates/unflake/unflake.nix b/templates/unflake/unflake.nix index ef70c8f..dd625c1 100644 --- a/templates/unflake/unflake.nix +++ b/templates/unflake/unflake.nix @@ -7,41 +7,33 @@ let type = "github"; owner = "denful"; repo = "den"; - rev = "0af82e24be89b9fd400bd0b58b0fed5ea0f269ad"; - lastModified = 1776710169; - narHash = "sha256-q4WXIX2E3w9Ld3MZ1Pl8Lh5SgrEFdEuzvY1Lj/Wo2kY="; + rev = "36c8ba5c07e2aa7d4816c6314ff2b6656293dee4"; + lastModified = 1788469400; + narHash = "sha256-2NVetxl+ycFWX4NZTCRqEnx4F37QajVpJe/b+n1Xw9I="; }; unflake_github_denful_with-inputs = { type = "github"; owner = "denful"; repo = "with-inputs"; - rev = "b4cbe858b381c0ee0fe617498549f0562090ad04"; - lastModified = 1775843270; - narHash = "sha256-GgKZ4LyKDS9vd946UeArhYqBKw63LdH4JqfbfFf7qNw="; + rev = "f0a6bc2464d744e6e3089f89ea6efeb9427c4acd"; + lastModified = 1788467216; + narHash = "sha256-yFXytpx2/EzFCji5gbn819t9AcIOe5LnlFTnrIkoZ/Q="; }; unflake_github_nix-community_home-manager = { type = "github"; owner = "nix-community"; repo = "home-manager"; - rev = "5c1b74905c7261e8280dcda3623dbe677a1bc158"; - lastModified = 1777659959; - narHash = "sha256-ax3229dUvNuwTQwo2o68kOQ24dvOlJ/BrVYY4miD1bI="; - }; - unflake_github_nixos_nixpkgs_ref_nixos-unstable = { - type = "github"; - owner = "nixos"; - repo = "nixpkgs"; - rev = "1c3fe55ad329cbcb28471bb30f05c9827f724c76"; - lastModified = 1777268161; - narHash = "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE="; + rev = "693e8ce0fb240a73c116a03cfd7b19269c87af88"; + lastModified = 1788487777; + narHash = "sha256-Ro/e1N4ZR8/XaFF+sF9SgfPK79HM5YNEppzFj8p+0Ak="; }; unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable = { type = "github"; owner = "nixos"; repo = "nixpkgs"; - rev = "7aaa00e7cc9be6c316cb5f6617bd740dd435c59d"; - lastModified = 1777548390; - narHash = "sha256-WacE23EbHTsBKvr8cu+1DFNbP6Rh1brHUH5SDUI0NQI="; + rev = "9387b3fcc0c23c86661636da63faabad4235a0a6"; + lastModified = 1788372231; + narHash = "sha256-7aqErvrAEz/5OcPA5p3M+tVOqeYc4oJXkNIPXfCqxAw="; }; unflake_github_denful_with-inputs_flake_false = unflake_github_denful_with-inputs; }; @@ -50,15 +42,12 @@ let unflake_github_denful_den = { }; unflake_github_nix-community_home-manager = { - nixpkgs = "unflake_github_nixos_nixpkgs_ref_nixos-unstable"; - }; - unflake_github_nixos_nixpkgs_ref_nixos-unstable = { + nixpkgs = "unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable"; }; unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable = { }; }; - inject = - name: flake_path: subdir: + inject = name: flake_path: subdir: let inputs = builtins.mapAttrs (_: dep: universe.${dep}) injections.${name} // { inherit self; @@ -66,33 +55,17 @@ let sourceInfo = deps.${name}; outPath = "${sourceInfo.outPath}${subdir}"; outputs = (import "${sourceInfo.outPath}/${flake_path}").outputs inputs; - self = - outputs - // sourceInfo - // { - inherit - inputs - outputs - outPath - sourceInfo - ; - _type = "flake"; - _flake = true; - }; - in - self; + self = outputs // sourceInfo // { + inherit inputs outputs outPath sourceInfo; + _type = "flake"; + _flake = true; + }; + in self; universe = rec { unflake_github_denful_den = inject "unflake_github_denful_den" "flake.nix" ""; unflake_github_denful_with-inputs_flake_false = deps.unflake_github_denful_with-inputs_flake_false; - unflake_github_nix-community_home-manager = - inject "unflake_github_nix-community_home-manager" "flake.nix" - ""; - unflake_github_nixos_nixpkgs_ref_nixos-unstable = - inject "unflake_github_nixos_nixpkgs_ref_nixos-unstable" "flake.nix" - ""; - unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable = - inject "unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable" "flake.nix" - ""; + unflake_github_nix-community_home-manager = inject "unflake_github_nix-community_home-manager" "flake.nix" ""; + unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable = inject "unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable" "flake.nix" ""; }; inputs = { den = universe.unflake_github_denful_den; @@ -100,20 +73,12 @@ let nixpkgs = universe.unflake_github_nixos_nixpkgs_ref_nixpkgs-unstable; with-inputs = universe.unflake_github_denful_with-inputs_flake_false; }; -in -inputs -// { - withInputs = - fn: - let - outputs = fn (inputs // { inherit self; }); - self = outputs // { - inherit inputs outputs; - _type = "flake"; - outPath = builtins.toString ./.; - }; - in - self; +in inputs // { + withInputs = fn: let outputs = fn (inputs // { inherit self; }); self = outputs // { + inherit inputs outputs; + _type = "flake"; + outPath = builtins.toString ./.; + }; in self; __functor = self: self.withInputs; self = throw "to use inputs.self, write `import ./unflake.nix (inputs: ...)`"; _unflake = { inherit specs deps injections; }; diff --git a/treefmt.toml b/treefmt.toml index d50e8eb..436c2d5 100644 --- a/treefmt.toml +++ b/treefmt.toml @@ -1,3 +1,11 @@ [formatter.nixfmt] command = "nixfmt" -includes = [ "*.nix" ] \ No newline at end of file +includes = [ "*.nix" ] +excludes = [ + "templates/lon/lon.nix", + "templates/niv/nix/sources.nix", + "templates/nixtamal/nix/tamal/default.nix", + "templates/npins/npins/default.nix", + "templates/tack/.tack/default.nix", + "templates/unflake/unflake.nix", +]