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

bootstrap command for traditional flakes. (#82)

authored by oeiuwq.com and committed by

GitHub bfe0475f f18f9bad

+106 -152
+19 -4
.github/workflows/flake-check.yaml
··· 16 16 templates=$(find templates -mindepth 2 -maxdepth 2 -name flake.nix -print0 | xargs -0 dirname | xargs -n 1 basename | jq -R | jq -sc) 17 17 echo "$templates" 18 18 echo "templates=$templates" >> $GITHUB_OUTPUT 19 - bootstrap: 20 - name: Bootstrap 19 + bootstrap-command: 20 + name: Bootstrap command 21 + runs-on: ubuntu-latest 22 + env: 23 + NIX_PATH: "nixpkgs=https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz" 24 + steps: 25 + - uses: wimpysworld/nothing-but-nix@main 26 + - uses: cachix/install-nix-action@v31 27 + - uses: DeterminateSystems/magic-nix-cache-action@main 28 + - uses: actions/checkout@v4 29 + - run: echo '{ }' > flake.nix 30 + - run: nix-shell "https://github.com/$GITHUB_REPOSITORY/archive/$GITHUB_SHA.zip" -A flake-file.sh --run bootstrap 31 + - run: cat flake.nix flake-file.nix 32 + - run: grep inputs.flake-file.flakeModules.flake flake.nix 33 + - run: grep github:vic/flake-file flake.nix 34 + bootstrap-tests: 35 + name: Bootstrap apps 21 36 runs-on: ubuntu-latest 22 37 env: 23 - NIX_PATH: "nixpkgs=https://github.com/NixOS/nixpkgs/archive/nixos-unstable.tar.gz" 38 + NIX_PATH: "nixpkgs=https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz" 24 39 steps: 25 40 - uses: wimpysworld/nothing-but-nix@main 26 41 - uses: cachix/install-nix-action@v31 ··· 106 121 sed -i 's/# flake-file = import/flake-file = import/' default.nix 107 122 nix-shell . -A flake-file.sh --run write-nixlock 108 123 dev: 109 - needs: [bootstrap, nix-fmt] 124 + needs: [bootstrap-command, bootstrap-tests, nix-fmt] 110 125 name: Check flake dev 111 126 runs-on: ubuntu-latest 112 127 steps:
+7
README.md
··· 50 50 </td></tr></table> 51 51 52 52 ## Learn more: [Documentation](https://flake-file.oeiuwq.com) 53 + 54 + ## Try it Now! 55 + 56 + ```shell 57 + mv flake.nix flake-file.nix 58 + nix-shell https://github.com/vic/flake-file/archive/main.zip -A flake-file.sh --run bootstrap 59 + ```
+2 -4
docs/src/content/docs/index.mdx
··· 43 43 **A**: It's a subset of Nix that doesn't allow computation in the flake metadata attributes. 44 44 </Aside> 45 45 46 - ### [Single Step Adoption](https://flake-file.oeiuwq.com/tutorials/migrate-traditional-flake/) 46 + #### [Try It Now!](https://flake-file.oeiuwq.com/tutorials/migrate-traditional-flake/) 47 47 48 48 ```shell 49 49 mv flake.nix flake-file.nix 50 - nix-shell https://github.com/vic/flake-file/archive/main.tar.gz \ 51 - -A flake-file.sh --run write-flake \ 52 - --arg modules ./flake-file.nix --argstr outputs flake-file 50 + nix-shell https://github.com/vic/flake-file/archive/main.tar.gz -A flake-file.sh --run bootstrap 53 51 ``` 54 52 55 53 ### Features
+31 -124
docs/src/content/docs/tutorials/migrate-traditional-flake.mdx
··· 5 5 6 6 import { Aside } from '@astrojs/starlight/components'; 7 7 8 - 9 - Suppose you have a flake that looks like: 10 - 11 - ```nix 12 - # flake.nix 13 - { 14 - inputs = { # So much inputs 15 - nixpkgs.url = "github:nixos/nixpkgs/unstable"; 16 - foo.url = "github:coolguy/foo"; 17 - bar.url = "github:coolguy/bar"; 18 - }; 19 - outputs = inputs: { ... }; # wow! very outputs 20 - } 21 - ``` 22 - 23 - ## Boostraping flake-file 24 - 25 - First, move your `flake.nix` file as `flake-file.nix`. 8 + ## The bootstrap magic step. 26 9 27 10 ```shell 28 11 mv flake.nix flake-file.nix 12 + nix-shell https://github.com/vic/flake-file/archive/main.tar.gz -A flake-file.sh --run bootstrap 29 13 ``` 30 14 31 - And now, the bootstrapping magic step: 15 + ## The new `flake.nix` (static Nix) 32 16 33 - ```shell 34 - nix-shell https://github.com/vic/flake-file/archive/main.tar.gz \ 35 - -A flake-file.sh --run write-flake \ 36 - --arg modules ./flake-file.nix --argstr outputs flake-file 37 - ``` 17 + You will notice your **new** `flake.nix` has your same meta-data: `inputs`, `description`, `nixConfig`. Only the `outputs` function was changed. 38 18 39 - > <small>See also: all [bootstrap command args](/reference/bootstrap)</small> 19 + <img src="https://i.kym-cdn.com/photos/images/newsfeed/001/449/977/aba.gif" /> 40 20 21 + And the `outputs` function treats your `flake-file.nix` - your original flake.nix file - 22 + as an ordinary Nix module. No changes required on your existing Nix logic. 41 23 42 - ## The new `flake.nix` (static Nix) 24 + ## Your `flake-file.nix` (non-static **real** Nix) 43 25 44 - You will notice your new `flake.nix` looks like this: 26 + Your inputs and all other flake meta-data are now module options (`flake-file.inputs`, etc). 45 27 46 - ```diff lang="nix" "(A)" "(B)" "(C)" "(D)" 47 - # flake.nix 48 - # DO-NOT-EDIT. This file was auto-generated using github:vic/flake-file. (A) 49 - # Use `nix run .#write-flake` to regenerate it. (D) 50 - { 51 - outputs = inputs: (import ./flake-file.nix).outputs inputs; # (B) 52 - inputs = { 53 - # (C) all your inputs (static version) 54 - }; 55 - } 56 - ``` 57 - <strong>(A)</strong> The `--arg modules ./flake-file.nix` option indicates 58 - the module entry-point. Your old flake is **already** a module for flake-file. 28 + `flake.nix` is a generated static asset intended for code distribution. 59 29 60 - <strong>(B)</strong> The `--argstr outputs flake-file` generated 61 - an `outputs` function that just **delegates** to the one in `flake-file.nix`. 30 + ## Output Schemas 62 31 63 - <strong>(C)</strong> Your `inputs` are now the **static** version (Nix subset) 64 - of the **real Nix** inputs at your `flake-file.nix` file. 32 + Your outputs are now freeform types but can also be validated by **output schemas**. 33 + 34 + And they work with **any** Nix implementation: Lix, CppNix, DetSys, etc. 65 35 66 - ## Edit `flake-file.nix` (real Nix) 36 + Add the following `flake-file.outputs-schema` to your `flake-file.nix` and then 37 + run `nix flake check`, then try replacing `mundo` by `world`. 67 38 68 - Now that `flake-file.nix` is real Nix code, you can refactor your inputs code 69 - to your heart's content. 39 + No extra check-derivation was needed, no custom nix-implementation, 40 + the checks are by the Nix module system as normal. 70 41 71 42 ```nix 72 43 # flake-file.nix 73 - # Now with more and **real** Nix 74 - let 75 - # for example purposes 76 - gh = user: repo: branch: "github:${user}/${repo}/${branch}"; 44 + { 45 + outputs = inputs: { hello = "mundo"; }; 77 46 78 - coolguy = repo: gh "coolguy" repo "main"; 79 - channel = "unstable"; 80 - in { 81 - inputs = { 82 - nixpkgs.url = gh "nixos" "nixpkgs" channel; 83 - foo.url = coolguy "foo"; 84 - bar.url = coolguy "bar"; 47 + flake-file.outputs-schema = { lib, ... }: { 48 + options.hello = lib.mkOption { 49 + type = lib.types.enum [ "mundo" ]; 50 + }; 85 51 }; 86 - 87 - outputs = inputs: { ... }; # keeps the same 88 52 } 89 53 ``` 90 54 91 - ## Update static from dynamic Nix. 55 + ## Update `flake.nix` 92 56 93 - Run the **same** command again, your input changes will be reflected into 57 + You can run the **bootstrap** command whenever you want. 58 + 59 + When your input changes, run it again to propagate your input changes into 94 60 the **static** `flake.nix`. 95 61 96 62 ```shell 97 - nix-shell https://github.com/vic/flake-file/archive/main.tar.gz \ 98 - -A flake-file.sh --run write-flake \ 99 - --arg modules ./flake-file.nix --argstr outputs flake-file 63 + nix-shell https://github.com/vic/flake-file/archive/main.zip -A flake-file.sh --run bootstrap 100 64 ``` 101 65 102 66 <Aside title="You are done!"> 103 67 You can stop here and enjoy your dynamic inputs at `flake-file.nix`. 104 68 105 69 Simply run the same bootstrap command whenever inputs change. 106 - </Aside> 107 70 108 - ## Moving into a module-based flake. 109 - 110 - <strong>(D)</strong> You might have noticed the comment about the `.#write-flake` app. 111 - To include this app as part of your flake and not having use the bootstrap command 112 - and specify all the same options each time, you need: 113 - 114 - Use `flake-module` outputs instead of `flake-file` on the bootstrap command: 115 - 116 - ```shell ins="flake-module" 117 - nix-shell https://github.com/vic/flake-file/archive/main.tar.gz \ 118 - -A flake-file.sh --run write-flake \ 119 - --arg modules ./flake-file.nix --argstr outputs flake-module 120 - ``` 121 - 122 - Add options to your `flake-file.nix` **module**. I told you it was a module already. 123 - 124 - ```diff lang="nix" "resolved flake inputs" " inputs =" "flake-file.inputs option" "inputs," " outputs =" "type-checked schema" ins="flake-module" 125 - # flake-file.nix 126 - + { inputs, lib, ... }: # resolved flake inputs as specialArgs 127 - let 128 - # same helper code as before 129 - in 130 - { 131 - # same inputs code as before 132 - inputs = { # Alias option for flake-file.inputs type-checked schema! 133 - 134 - # make sure you add flake-file dependency. 135 - + flake-file.url = lib.mkDefault "github:vic/flake-file"; 136 - }; 137 - 138 - # same outputs code as before 139 - outputs = inputs: ...; # Now with **extensible** type-checked schema! 140 - 141 - # since this IS a module it can import other modules 142 - imports = [ 143 - # enable inside-flake and say goodbye to bootstrap 144 - + inputs.flake-file.flakeModule 145 - 146 - # start splitting from huge monolithic code to modular nix. 147 - # each input can be defined on their relevant module files. 148 - # maybe move outputs function to ./outputs.nix 149 - ]; 150 - 151 - # generate the same output function we used at bootstrap 152 - + flake-file.outputs = "flake-module"; 153 - } 154 - ``` 155 - 156 - 157 - ## Your flake is flake-file enabled 158 - 159 - Commands for your daily life: 160 - 161 - ```shell 162 - nix run .#write-flake # whenever you need to regen flake.nix 163 - 164 - nix flake check # will make sure your flake.nix is up-to-date 165 - ``` 71 + See Reference for integrating it as a runnable package from your flake. 72 + </Aside>
+34 -15
modules/bootstrap/default.nix
··· 2 2 pkgs ? import <nixpkgs> { }, 3 3 modules ? [ ], 4 4 outdir ? ".", 5 - bootstrap ? [ ], 5 + inputs ? null, 6 6 outputs ? null, 7 + do-not-edit ? null, 7 8 import-tree ? ( 8 9 pkgs.fetchFromGitHub { 9 10 owner = "vic"; ··· 24 25 type = lib.types.submodule { freeformType = lib.types.lazyAttrsOf lib.types.unspecified; }; 25 26 }; 26 27 27 - bootstrapInputs = 28 - let 29 - ins = import ./inputs.nix { inherit lib; }; 30 - take = name: { flake-file.inputs.${name} = ins.flake-file.inputs.${name}; }; 31 - names = 32 - if bootstrap == true then lib.attrNames ins.flake-file.inputs else lib.flatten [ bootstrap ]; 33 - in 34 - map take names; 35 - 36 28 module = { 37 29 imports = [ 38 30 tree ··· 45 37 ./../write-flake.nix 46 38 ./../write-lock.nix 47 39 ./../flake-options.nix 48 - { imports = bootstrapInputs; } 49 - (if outputs == null then { } else { flake-file.outputs = outputs; }) 40 + (lib.optionalAttrs (inputs != null) { flake-file.inputs = inputs; }) 41 + (lib.optionalAttrs (outputs != null) { flake-file.outputs = strOrFile outputs; }) 42 + (lib.optionalAttrs (do-not-edit != null) { flake-file.do-not-edit = strOrFile do-not-edit; }) 43 + 44 + bootstrap-app 50 45 ]; 51 46 config.flake-file.intoPath = outdir; 52 47 options = { ··· 56 51 }; 57 52 }; 58 53 59 - evaled = lib.evalModules { 60 - modules = [ module ]; 61 - specialArgs.inputs.self.outPath = ""; 54 + strOrFile = str: if builtins.isPath str then builtins.readFile str else str; 55 + 56 + bootstrap-app = { 57 + flake-file.apps.bootstrap = 58 + pkgs: 59 + let 60 + inputs = ''{ flake-file.url = "github:vic/flake-file"; }''; 61 + notice = pkgs.writeText "notice" '' 62 + # DO-NOT-EDIT. Generated by github:vic/flake-file from flake-file.nix. Regen with: 63 + # nix-shell https://github.com/vic/flake-file/archive/main.zip -A flake-file.sh --run bootstrap 64 + ''; 65 + in 66 + pkgs.writeShellApplication { 67 + name = "bootstrap"; 68 + text = '' 69 + if ! test -f flake-file.nix; then 70 + cp flake.nix flake-file.nix 71 + fi 72 + nix-shell ${../..} -A flake-file.sh --run write-flake \ 73 + --arg modules ./flake-file.nix \ 74 + --arg do-not-edit ${notice} \ 75 + --argstr outputs flake-module \ 76 + --arg inputs '${inputs}' 77 + ''; 78 + }; 62 79 }; 80 + 81 + evaled = lib.evalModules { modules = [ module ]; }; 63 82 64 83 in 65 84 evaled.config
+1 -1
modules/bootstrap/inputs.nix
··· 4 4 import-tree.url = lib.mkDefault "github:vic/import-tree"; 5 5 flake-file.url = lib.mkDefault "github:vic/flake-file"; 6 6 flake-parts.url = lib.mkDefault "github:hercules-ci/flake-parts"; 7 - flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 7 + flake-parts.inputs.nixpkgs-lib.follows = lib.mkDefault "nixpkgs"; 8 8 nixpkgs.url = lib.mkDefault "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; 9 9 }; 10 10 }
+9 -1
modules/default.nix
··· 1 1 let 2 2 flakeModules = { 3 3 inherit 4 - default 4 + flake # for non-flake-parts flakes 5 + default # for flake-parts flakes (keep as default for compatibility) 5 6 allfollow 6 7 nix-auto-follow 7 8 dendritic ··· 12 13 flake-options 13 14 ; 14 15 }; 16 + 17 + # A flake without flake-parts. (traditional flake) 18 + flake.imports = [ 19 + base 20 + flake-options 21 + ./write-flake.nix 22 + ]; 15 23 16 24 flake-options = ./flake-options.nix; 17 25
+1 -1
modules/flake-options.nix
··· 1 1 { lib, config, ... }: 2 2 let 3 - outputsOption = lib.mkOption { type = lib.type.functionTo outputsType; }; 3 + outputsOption = lib.mkOption { type = lib.types.functionTo outputsType; }; 4 4 5 5 outputsType = lib.types.submoduleWith { 6 6 modules = [
+1 -1
modules/options/outputs.nix
··· 13 13 inputs: 14 14 (inputs.nixpkgs.lib.evalModules { 15 15 specialArgs = { inherit inputs; inherit (inputs) self; }; 16 - modules = [ ./flake-file.nix ]; 16 + modules = [ inputs.flake-file.flakeModules.flake ./flake-file.nix ]; 17 17 }).config.outputs inputs 18 18 ''; 19 19
+1 -1
modules/write-flake.nix
··· 87 87 name = "write-flake"; 88 88 text = '' 89 89 cd ${config.flake-file.intoPath} 90 - cp --no-preserve=mode ${formatted pkgs} flake.nix 90 + cat ${formatted pkgs} > flake.nix 91 91 ${hooks} 92 92 ''; 93 93 };