Generate flake.nix from module options. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ dendrix.oeiuwq.com/Dendritic.html
dendritic nix inputs

unflake (#60)

authored by oeiuwq.com and committed by

GitHub 5312958b 1515b77c

+285 -51
+16
.github/workflows/flake-check.yaml
··· 23 23 strategy: 24 24 matrix: 25 25 template: ${{ fromJSON(needs.find-templates.outputs.templates) }} 26 + exclude: 27 + - template: "unflake" 26 28 steps: 27 29 - uses: wimpysworld/nothing-but-nix@main 28 30 - uses: cachix/install-nix-action@v31 ··· 44 46 nix flake metadata 45 47 nix run .#write-flake -L --show-trace 46 48 nix flake check -L --show-trace 49 + unflake: 50 + name: Check unflake 51 + runs-on: ubuntu-latest 52 + steps: 53 + - uses: actions/checkout@v4 54 + - uses: wimpysworld/nothing-but-nix@main 55 + - uses: cachix/install-nix-action@v31 56 + - uses: DeterminateSystems/magic-nix-cache-action@main 57 + - run: | 58 + set -e -o pipefail 59 + cd templates/unflake 60 + sed -i 's/# flake-file = import/flake-file = import/' default.nix 61 + echo "{ inputs, ... }: { unflake.pkgs = import inputs.nixpkgs {}; }" | tee modules/pkgs.nix 62 + nix-shell . -A unflake.env --run 'write-unflake --verbose' 47 63 dev: 48 64 name: Check flake dev 49 65 runs-on: ubuntu-latest
+20 -8
README.md
··· 9 9 <a href="LICENSE"> <img src="https://img.shields.io/github/license/vic/flake-file" alt="License"/> </a> 10 10 </p> 11 11 12 - # flake-file — Generate flake.nix from flake-parts modules. 12 + # flake-file — Generate flake.nix or unflake.nix from inputs defined as module options. 13 13 14 14 > `flake-file` and [vic](https://bsky.app/profile/oeiuwq.bsky.social)'s [dendritic libs](https://vic.github.io/dendrix/Dendritic-Ecosystem.html#vics-dendritic-libraries) made for you with Love++ and AI--. If you like my work, consider [sponsoring](https://github.com/sponsors/vic) 15 15 16 - **flake-file** lets you generate a clean, maintainable `flake.nix` from modular options, using [flake-parts](https://flake.parts/). 16 + **flake-file** lets you generate a clean, maintainable `flake.nix` from modular options. It works on both flakes and non-flakes environments. 17 17 18 18 It makes your flake configuration modular and based on the Nix module system. This means you can use 19 19 `lib.mkDefault` or anything you normally do with Nix modules, and have them reflected in flake schema values. 20 + 21 + > Despite the original flake-oriented name, it NOW also works on _stable Nix_, [_non flakes_](templates/unflake) environments. 20 22 21 23 <table><tr><td> 22 24 23 25 ## Features 24 26 25 - - Flake definition aggregated from all flake-parts modules. 27 + - Flake definition aggregated from Nix modules. 26 28 - Schema as [options](https://github.com/vic/flake-file/blob/main/modules/options/default.nix). 27 29 - Syntax for nixConfig and follows is the same as in flakes. 28 30 - `flake check` ensures files are up to date. 29 - - App for generator: `nix run .#write-flake` 31 + - App for `flake.nix` generator: `nix run .#write-flake` 30 32 - Custom do-not-edit header. 31 33 - Automatic flake.lock [flattening](#automatic-flakelock-flattening). 32 34 - Incrementally add [flake-parts-builder](#parts_templates) templates. 33 35 - Pick flakeModules for different feature sets. 34 36 - [Dendritic](https://vic.github.io/dendrix/Dendritic.html) flake template. 37 + - Works on stable Nix, [unflake](templates/unflake) environments. 35 38 36 39 </td><td> 37 40 ··· 60 63 ## Who is this for? 61 64 62 65 - Nix users who want to keep their `flake.nix` modular and maintainable 63 - - Anyone using [flake-parts](https://flake.parts/) and looking to automate or simplify flake input management 66 + - Anyone using Nix modules and looking to automate or simplify flake input management 64 67 - Teams or individuals who want to share and reuse flake modules across projects 65 68 66 69 --- ··· 148 151 149 152 > Previously, this module included `flake-aspects` and `den` as dependencies. It now provides a pure flake-parts Dendritic setup. If you need the complete [den](https://github.com/vic/den) functionality, use den's `flakeModules.dendritic` instead. 150 153 154 + #### [`flakeModules.unflake`](https://github.com/vic/flake-file/tree/main/modules/unflake.nix) 155 + 156 + - Defines `flake-file` options. 157 + - Exposes `write-unflake` to generate `unflake.nix` or `npins`. See [templates/unflake](templates/unflake) for usage. 158 + 151 159 ### Flake Templates 152 160 153 - #### `default` template 161 + #### [`default`](templates/default) template 154 162 155 163 A more basic, explicit setup. 156 164 ··· 178 186 > [!TIP] 179 187 > You can use the `write-flake` app as part of a devshell or git hook. 180 188 181 - #### `dendritic` template 189 + #### [`dendritic`](templates/dendritic) template 182 190 183 191 A template for dendritic setups; includes `flakeModules.dendritic`. 184 192 185 - #### `parts` template 193 + #### [`parts`](templates/parts) template 186 194 187 195 A template that uses `lib.flakeModules.flake-parts-builder`. 196 + 197 + #### [`unflake`](templates/unflake) template 198 + 199 + Uses [goldstein/unflake](https://codeberg.org/goldstein/unflake) to pin and fetch inputs that were defined as options for non-flakes stable Nix environments. 188 200 189 201 --- 190 202
+42 -1
dev/modules/_lib/default.nix
··· 75 75 } 76 76 ); 77 77 78 + nixAttr = 79 + name: value: 80 + let 81 + childIsAttr = builtins.isAttrs value; 82 + childIsOne = builtins.length (builtins.attrNames value) == 1; 83 + nested = lib.head (lib.mapAttrsToList nixAttr value); 84 + in 85 + if childIsAttr && childIsOne then 86 + { 87 + name = "${name}.${nested.name}"; 88 + value = nested.value; 89 + } 90 + else 91 + { 92 + inherit name; 93 + value = value; 94 + }; 95 + 96 + # expr to code 97 + nixCode = 98 + x: 99 + if lib.isStringLike x then 100 + lib.strings.escapeNixString x 101 + else if lib.isAttrs x then 102 + lib.pipe x [ 103 + (lib.mapAttrsToList nixAttr) 104 + (map ({ name, value }: "${name} = ${nixCode value}; ")) 105 + (values: "{ ${lib.concatStringsSep " " values} }") 106 + ] 107 + else if lib.isList x then 108 + lib.pipe x [ 109 + (lib.map nixCode) 110 + (values: "[ ${lib.concatStringsSep " " values} ]") 111 + ] 112 + else if x == true then 113 + "true" 114 + else if x == false then 115 + "false" 116 + else 117 + toString x; 118 + 78 119 in 79 120 { 80 - inherit inputsExpr isNonEmptyString; 121 + inherit inputsExpr isNonEmptyString nixCode; 81 122 }
+2
dev/modules/formatter.nix
··· 31 31 ".envrc" 32 32 ".direnv/*" 33 33 "*/.gitignore" 34 + "**/unflake.nix" # generated by: nix-shell . -A unflake.env --run write-unflake 35 + "**/inputs.nix" # generated by: nix-shell . -A unflake.env --run write-inputs 34 36 ]; 35 37 }; 36 38 };
+11
modules/default.nix
··· 6 6 nix-auto-follow 7 7 dendritic 8 8 import-tree 9 + unflake 9 10 ; 10 11 }; 12 + 13 + unflake.imports = [ 14 + ./options 15 + ./unflake.nix 16 + ]; 11 17 12 18 default.imports = [ 13 19 ./options ··· 34 40 templates.default = { 35 41 description = "default template"; 36 42 path = ./../templates/default; 43 + }; 44 + 45 + templates.unflake = { 46 + description = "unflake template"; 47 + path = ./../templates/unflake; 37 48 }; 38 49 39 50 templates.dendritic = {
+64
modules/unflake.nix
··· 1 + { lib, config, ... }: 2 + let 3 + inherit (config.unflake) pkgs; 4 + inherit (import ./../dev/modules/_lib lib) inputsExpr nixCode; 5 + 6 + inputsString = '' 7 + # DO-NOT-EDIT: Generated by github:vic/flake-file for unflake. 8 + # To re-generate use: nix-shell . -A unflake.env --run write-inputs 9 + '' 10 + + (nixCode (inputsExpr config.flake-file.inputs)); 11 + 12 + inputsFile = pkgs.stdenvNoCC.mkDerivation { 13 + name = "inputs"; 14 + passAsFile = [ "inputsString" ]; 15 + inherit inputsString; 16 + phases = [ "copy" ]; 17 + copy = '' 18 + cp $inputsStringPath $out 19 + ''; 20 + }; 21 + 22 + write-inputs = pkgs.writeShellApplication { 23 + name = "write-inputs"; 24 + text = '' 25 + cp ${inputsFile} "''${1:-inputs.nix}" 26 + ''; 27 + }; 28 + 29 + write-unflake = pkgs.writeShellApplication { 30 + name = "write-unflake"; 31 + text = '' 32 + nix-shell "${config.unflake.url}" -A unflake-shell --run "unflake -i ${inputsFile} $*" 33 + ''; 34 + }; 35 + 36 + env = pkgs.mkShell { 37 + buildInputs = [ 38 + write-inputs 39 + write-unflake 40 + ]; 41 + }; 42 + in 43 + { 44 + options.unflake = { 45 + url = lib.mkOption { 46 + type = lib.types.str; 47 + description = "unflake archive url"; 48 + default = "https://codeberg.org/goldstein/unflake/archive/main.tar.gz"; 49 + }; 50 + 51 + pkgs = lib.mkOption { 52 + type = lib.types.raw; 53 + description = "nixpkgs instance for unflake generator"; 54 + default = import <nixpkgs> { }; 55 + }; 56 + 57 + env = lib.mkOption { 58 + type = lib.types.raw; 59 + readOnly = true; 60 + internal = true; 61 + default = env; 62 + }; 63 + }; 64 + }
+1 -42
modules/write-flake.nix
··· 6 6 ... 7 7 }: 8 8 let 9 - inherit (import ./../dev/modules/_lib lib) inputsExpr isNonEmptyString; 9 + inherit (import ./../dev/modules/_lib lib) inputsExpr isNonEmptyString nixCode; 10 10 11 11 flake-file = config.flake-file; 12 12 ··· 26 26 (lib.replaceString "<inputs>" flakeInputs) 27 27 addHeader 28 28 ]; 29 - 30 - nixAttr = 31 - name: value: 32 - let 33 - childIsAttr = builtins.isAttrs value; 34 - childIsOne = builtins.length (builtins.attrNames value) == 1; 35 - nested = lib.head (lib.mapAttrsToList nixAttr value); 36 - in 37 - if childIsAttr && childIsOne then 38 - { 39 - name = "${name}.${nested.name}"; 40 - value = nested.value; 41 - } 42 - else 43 - { 44 - inherit name; 45 - value = value; 46 - }; 47 - 48 - # expr to code 49 - nixCode = 50 - x: 51 - if lib.isStringLike x then 52 - lib.strings.escapeNixString x 53 - else if lib.isAttrs x then 54 - lib.pipe x [ 55 - (lib.mapAttrsToList nixAttr) 56 - (map ({ name, value }: "${name} = ${nixCode value}; ")) 57 - (values: "{ ${lib.concatStringsSep " " values} }") 58 - ] 59 - else if lib.isList x then 60 - lib.pipe x [ 61 - (lib.map nixCode) 62 - (values: "[ ${lib.concatStringsSep " " values} ]") 63 - ] 64 - else if x == true then 65 - "true" 66 - else if x == false then 67 - "false" 68 - else 69 - toString x; 70 29 71 30 description = 72 31 if isNonEmptyString flake-file.description then
+40
templates/unflake/README.md
··· 1 + # Unflake 2 + 3 + This template is an example of using `flake-file.inputs` in a non-flakes project. 4 + 5 + It uses [unflake](https://codeberg.org/goldstein/unflake) to pin and fetch inputs defined as options inside `./modules`. 6 + 7 + ## Generate `unflake.nix` 8 + 9 + The following command is a convenience for generating `unflake.nix` by 10 + first producing a temporary `inputs.nix` from your config and then 11 + running unflake on it. 12 + 13 + ```shell 14 + nix-shell . -A unflake.env --run write-unflake 15 + ``` 16 + 17 + You can also pass any unflake option: 18 + 19 + ```shell 20 + nix-shell . -A unflake.env --run 'write-unflake --verbose --backend nix' 21 + ``` 22 + 23 + If you need to see the file that is being passed as `--inputs inputs.nix` 24 + to the unflake command, you can generate it with: 25 + 26 + ```shell 27 + # (only recommended for debugging) 28 + nix-shell . -A unflake.env --run write-inputs 29 + 30 + # then, you can run unflake yourself: 31 + nix-shell https://ln-s.sh/unflake -A unflake-shell --run unflake 32 + ``` 33 + 34 + ## Using with [npins](https://github.com/andir/npins) 35 + 36 + Unflake has an npins backend to use it run: 37 + 38 + ```shell 39 + nix-shell . -A unflake.env --run 'write-unflake --backend npins' 40 + ```
+21
templates/unflake/default.nix
··· 1 + let 2 + 3 + outputs = 4 + inputs: 5 + (inputs.nixpkgs.lib.evalModules { 6 + modules = [ (inputs.import-tree ./modules) ]; 7 + specialArgs = { inherit inputs; }; 8 + }).config; 9 + 10 + withInputs = 11 + inputs: outputs: 12 + outputs ( 13 + inputs 14 + // { 15 + # uncomment to use local checkout on CI 16 + # flake-file = import ./../../modules; 17 + } 18 + ); 19 + 20 + in 21 + (import ./unflake.nix withInputs) outputs
+10
templates/unflake/modules/default.nix
··· 1 + { inputs, ... }: 2 + { 3 + imports = [ inputs.flake-file.flakeModules.unflake ]; 4 + 5 + flake-file.inputs = { 6 + flake-file.url = "github:vic/flake-file"; 7 + import-tree.url = "github:vic/import-tree"; 8 + nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; 9 + }; 10 + }
+58
templates/unflake/unflake.nix
··· 1 + # @generated by https://codeberg.org/goldstein/unflake 2 + let 3 + deps = rec { 4 + unflake_github_vic_flake-file = builtins.fetchTree { 5 + type = "github"; 6 + owner = "vic"; 7 + repo = "flake-file"; 8 + rev = "1515b77c3c1dd08ab394e840603fc152e059d13e"; 9 + lastModified = 1770893152; 10 + narHash = "sha256-DuZHf9EaQQ2iiUloYj4374VCsfDQ7kGuWC7037BDSpE="; 11 + }; 12 + unflake_github_vic_import-tree = builtins.fetchTree { 13 + type = "github"; 14 + owner = "vic"; 15 + repo = "import-tree"; 16 + rev = "3c23749d8013ec6daa1d7255057590e9ca726646"; 17 + lastModified = 1763762820; 18 + narHash = "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0="; 19 + }; 20 + unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz = builtins.fetchTree { 21 + type = "tarball"; 22 + url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; 23 + lastModified = 1770843696; 24 + narHash = "sha256-9SFCZkVcpDOV6unH5hVEy4+dB0rxMuUoBnDAO6vshac="; 25 + }; 26 + }; 27 + universe = rec { 28 + unflake_github_vic_flake-file = ((import "${deps.unflake_github_vic_flake-file.outPath}/flake.nix").outputs { 29 + self = unflake_github_vic_flake-file; 30 + }) // deps.unflake_github_vic_flake-file // { 31 + _flake = true; 32 + outPath = "${deps.unflake_github_vic_flake-file.outPath}"; 33 + sourceInfo = deps.unflake_github_vic_flake-file; 34 + }; 35 + unflake_github_vic_import-tree = ((import "${deps.unflake_github_vic_import-tree.outPath}/flake.nix").outputs { 36 + self = unflake_github_vic_import-tree; 37 + }) // deps.unflake_github_vic_import-tree // { 38 + _flake = true; 39 + outPath = "${deps.unflake_github_vic_import-tree.outPath}"; 40 + sourceInfo = deps.unflake_github_vic_import-tree; 41 + }; 42 + unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz = ((import "${deps.unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz.outPath}/flake.nix").outputs { 43 + self = unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz; 44 + }) // deps.unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz // { 45 + _flake = true; 46 + outPath = "${deps.unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz.outPath}"; 47 + sourceInfo = deps.unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz; 48 + }; 49 + }; 50 + inputs = { 51 + flake-file = universe.unflake_github_vic_flake-file; 52 + import-tree = universe.unflake_github_vic_import-tree; 53 + nixpkgs = universe.unflake_tarball_https---channels-nixos-org-nixpkgs-unstable-nixexprs-tar-xz; 54 + self = throw "to use inputs.self, write `import ./unflake.nix (inputs: ...)`"; 55 + withInputs = outputs: let self = outputs (inputs // { inherit self; }); in self; 56 + __functor = self: self.withInputs; 57 + }; 58 + in inputs