Modular, context-aware and aspect-oriented dendritic Nix configurations. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ den.oeiuwq.com
configurations den dendritic nix aspect oriented

Compare changes

Choose any two refs to compare.

+3230 -2437
+53 -1
.github/workflows/test.yml
··· 18 18 steps: 19 19 - uses: cachix/install-nix-action@v31 20 20 - run: nix flake check -L github:vic/checkmate --override-input target github:$GITHUB_REPOSITORY/$GITHUB_SHA 21 + noflake: 22 + needs: [non-draft] 23 + name: noflake 24 + runs-on: ubuntu-latest 25 + steps: 26 + - uses: wimpysworld/nothing-but-nix@main 27 + - uses: cachix/install-nix-action@v31 28 + - uses: DeterminateSystems/magic-nix-cache-action@v13 29 + - uses: actions/checkout@v4 30 + - run: sed -i 's@# den.outPath@den.outPath@' templates/noflake/with-inputs.nix 31 + - run: | 32 + cat <<-EOF > templates/noflake/modules/ci-runtime.nix 33 + { 34 + _module.args.CI = true; 35 + } 36 + EOF 37 + git add templates/noflake/modules/ci-runtime.nix 38 + - run: cd templates/noflake && nix-build -A nixosConfigurations.igloo.config.system.build.toplevel 39 + minimal: 40 + needs: [non-draft] 41 + name: minimal 42 + runs-on: ubuntu-latest 43 + steps: 44 + - uses: wimpysworld/nothing-but-nix@main 45 + - uses: cachix/install-nix-action@v31 46 + - uses: DeterminateSystems/magic-nix-cache-action@v13 47 + - uses: actions/checkout@v4 48 + - run: | 49 + cat <<-EOF > templates/minimal/modules/ci-runtime.nix 50 + { 51 + _module.args.CI = true; 52 + } 53 + EOF 54 + git add templates/minimal/modules/ci-runtime.nix 55 + - run: nix flake check ./templates/minimal --override-input den github:$GITHUB_REPOSITORY/$GITHUB_SHA 56 + bogus: 57 + needs: [non-draft] 58 + name: bogus 59 + runs-on: ubuntu-latest 60 + steps: 61 + - uses: wimpysworld/nothing-but-nix@main 62 + - uses: cachix/install-nix-action@v31 63 + - uses: DeterminateSystems/magic-nix-cache-action@v13 64 + - uses: actions/checkout@v4 65 + - run: | 66 + cat <<-EOF > templates/bogus/modules/ci-runtime.nix 67 + { 68 + _module.args.CI = true; 69 + } 70 + EOF 71 + git add templates/bogus/modules/ci-runtime.nix 72 + - run: nix flake check ./templates/bogus --override-input den github:$GITHUB_REPOSITORY/$GITHUB_SHA 21 73 template: 22 74 needs: [non-draft] 23 75 strategy: 24 76 matrix: 25 77 os: [ubuntu-latest, macos-latest] 26 - template: [default, bogus, examples] 78 + template: [default, ci, example] 27 79 name: Check template ${{matrix.template}} ${{matrix.os}} 28 80 runs-on: ${{matrix.os}} 29 81 steps:
+82 -20
README.md
··· 1 1 <p align="right"> 2 2 <a href="https://github.com/sponsors/vic"><img src="https://img.shields.io/badge/sponsor-vic-white?logo=githubsponsors&logoColor=white&labelColor=%23FF0000" alt="Sponsor Vic"/> 3 3 </a> 4 + <a href="https://deepwiki.com/vic/den"><img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki"></a> 4 5 <a href="https://github.com/vic/den/releases"><img src="https://img.shields.io/github/v/release/vic/den?style=plastic&logo=github&color=purple"/></a> 5 6 <a href="https://vic.github.io/dendrix/Dendritic-Ecosystem.html#vics-dendritic-libraries"> <img src="https://img.shields.io/badge/Dendritic-Nix-informational?logo=nixos&logoColor=white" alt="Dendritic Nix"/> </a> 6 7 <a href="LICENSE"> <img src="https://img.shields.io/github/license/vic/den" alt="License"/> </a> ··· 19 20 20 21 <img width="300" height="300" alt="den" src="https://github.com/user-attachments/assets/af9c9bca-ab8b-4682-8678-31a70d510bbb" /> 21 22 22 - - Dendritic: each module configures **same** concern over **different** Nix classes. 23 + - [Dendritic](https://github.com/mightyiam/dendritic): **same** concern across **different** Nix classes. 23 24 24 - - Create [DRY](modules/aspects/provides/unfree.nix) & [`class`-generic](modules/aspects/provides/primary-user.nix) modules. 25 + - [Flake optional](templates/noflake). Works with _stable_/_unstable_ Nix and with/without flake-parts. 26 + 27 + - Create [DRY](modules/aspects/provides/unfree/unfree.nix) & [`class`-generic](modules/aspects/provides/primary-user.nix) modules. 25 28 26 29 - [Parametric](modules/aspects/provides/define-user.nix) over `host`/`home`/`user`. 27 30 28 - - [Share](templates/default/modules/namespace.nix) aspects across systems & repos. 31 + - Context-aware [dependencies](modules/aspects/dependencies.nix): user\<->host bidirectional contributions. 29 32 30 - - Context-aware [dependencies](modules/aspects/dependencies.nix): user/host contributions. 33 + - [Share](templates/example/modules/namespace.nix) aspects across systems & repos. 31 34 32 - - [Routable](templates/default/modules/aspects/eg/routes.nix) configurations. 35 + - [Routable](templates/example/modules/aspects/eg/routes.nix) configurations. 33 36 34 - - Custom factories for any Nix `class`. 37 + - Custom [factories](https://github.com/vic/den/blob/f5c44098e4855e07bf5cbcec00509e75ddde4220/templates/ci/modules/homes.nix#L20) for any Nix `class`. 35 38 36 39 - Use `stable`/`unstable` channels per config. 37 40 38 - - Freeform `host`/`user`/`home` [schemas](modules/_types.nix) (no `specialArgs`). 41 + - Freeform `host`/`user`/`home` [schemas](modules/_types.nix) (no `specialArgs`) with [base](https://github.com/vic/den/pull/119) modules. 39 42 40 43 - Multi-platform, multi-tenant hosts. 41 44 ··· 43 46 44 47 - Opt-in [`<angle/brackets>`](https://vic.github.io/den/angle-brackets.html) aspect resolution. 45 48 46 - - Templates [tested](templates/default/modules/tests.nix) along [examples](templates/examples/modules/_example/ci). 49 + - Features [tested](templates/ci). 47 50 48 - - Concepts [documented](https://vic.github.io/den). 51 + - REPL [friendly](https://github.com/vic/den/blob/f5c44098e4855e07bf5cbcec00509e75ddde4220/templates/bogus/modules/bug.nix#L34) [debugging](https://den.oeiuwq.com/debugging.html). 49 52 50 53 Need more **batteries**? See [vic/denful](https://github.com/vic/denful). 51 54 ··· 74 77 **Available templates** 75 78 76 79 - [`default`](templates/default) batteries-included layout. 77 - - [`minimal`](templates/minimal) truly minimalistic start. 78 - - [`examples`](templates/examples) tests for all features. 80 + - [`minimal`](templates/minimal) minimalistic flake. 81 + - [`noflake`](templates/noflake) no flakes, no flake-parts, user nix-maid. 82 + - [`example`](templates/example) examples. 83 + - [`ci`](templates/ci) tests for all features. 79 84 - [`bogus`](templates/bogus) reproduce and report bugs. 80 85 81 86 </div> 82 87 </td> 83 88 <td> 84 89 85 - ๐Ÿ  Define [Hosts, Users](templates/examples/modules/_example/hosts.nix) & [Homes](templates/examples/modules/_example/homes.nix) concisely. 90 + ### Den fundamental idea 91 + 92 + > Configurations that can be applied to multiple host/user combinations. 93 + > The [`__functor`](https://den.oeiuwq.com/functor.html) pattern makes aspects parametric. 94 + 95 + <details> 86 96 87 - See schema in [`_types.nix`](modules/_types.nix). 97 + <summary> 98 + Den is about propagating context to produce configs. 99 + </summary> 100 + 101 + ```nix 102 + # context => aspect 103 + { host, user, ... }@context: { 104 + # any Nix configuration classes 105 + nixos = { }; 106 + darwin = { }; 107 + homeManager = { }; 108 + # supports conditional includes that inspect context 109 + # unlike nix-module imports 110 + includes = [ ]; 111 + } 112 + ``` 113 + 114 + You first define which [Hosts, Users](templates/ci/modules/hosts.nix) 115 + and [Homes](templates/ci/modules/homes.nix) exist 116 + using freeform-attributes or base-modules. 117 + 118 + ```nix 119 + { 120 + # isWarm or any other freeform attr 121 + den.hosts.x86_64-linux.igloo.isWarm = true; 122 + } 123 + ``` 124 + 125 + Then, you write functions from context `host`/`user` to configs. 126 + 127 + ```nix 128 + { 129 + den.aspects.heating = { host, user, ... }: { 130 + nixos = { ... }; # depends on host.isWarm, etc. 131 + homeManager = { ... }; 132 + }; 133 + 134 + # previous aspect can be included on any host 135 + # den.aspects.igloo.includes = [ den.aspects.heating ]; 136 + # or by default in all of them 137 + # den.default.includes = [ den.aspects.heating ]; 138 + } 139 + ``` 140 + 141 + This way, configurations are truly re-usable, 142 + as they are nothing more than functions of the 143 + particularities of the host or its users. 144 + 145 + </details> 146 + 147 + ### Code example 148 + 149 + Schema based hosts/users/homes entities (see [`_types.nix`](modules/_types.nix)). 88 150 89 151 ```nix 90 152 # modules/hosts.nix 91 153 { 92 - # same home-manager vic configuration 93 - # over laptop, macbook and standalone-hm 154 + # same vic home-manager aspect shared 155 + # on laptop, macbook and standalone-hm 94 156 den.hosts.x86_64-linux.lap.users.vic = {}; 95 157 den.hosts.aarch64-darwin.mac.users.vic = {}; 96 158 den.homes.aarch64-darwin.vic = {}; ··· 103 165 $ home-manager switch --flake .#vic 104 166 ``` 105 167 106 - ๐Ÿงฉ [Aspect-oriented](https://github.com/vic/flake-aspects) incremental features. ([example](templates/default/modules/den.nix)) 107 - 108 - Any module can contribute configurations to aspects. 168 + Any module can contribute configurations to [aspects](https://github.com/vic/flake-aspects). 109 169 110 170 ```nix 111 171 # modules/my-laptop.nix ··· 151 211 152 212 }; 153 213 } 214 + ``` 154 215 216 + ```nix 155 217 # modules/vic.nix 156 218 { den, ... }: { 157 219 den.aspects.vic = { ··· 162 224 }; 163 225 includes = [ 164 226 den.aspects.tiling-wm 165 - den._.primary-user 227 + den.provides.primary-user 166 228 ]; 167 229 }; 168 230 } ··· 174 236 175 237 You are done! You know everything to start creating configurations with `den`. 176 238 177 - Feel free to to **explore** the codebase, particularly our [included batteries](modules/aspects/provides) and [tests](templates/examples/modules/_example/ci). 239 + Feel free to to **explore** the codebase, particularly our [included batteries](modules/aspects/provides) and [tests](templates/ci). 178 240 179 241 ## Learn more at our [documentation website](https://vic.github.io/den) 180 242
+9
modules/aspects/provides/ci-noboot.nix
··· 1 + { config, lib, ... }: 2 + { 3 + den.default.includes = lib.optionals (config ? _module.args.CI) [ 4 + { 5 + nixos.fileSystems."/".device = lib.mkDefault "/dev/noroot"; 6 + nixos.boot.loader.grub.enable = lib.mkDefault false; 7 + } 8 + ]; 9 + }
+1 -2
modules/aspects/provides/home-manager/hm-dependencies.nix
··· 22 22 includes = [ 23 23 (owned den.default) 24 24 (statics den.default) 25 - (owned HM-OS-USER.HM) 26 - (statics HM-OS-USER.HM) 27 25 (parametric.fixedTo HM-OS-USER HM-OS-USER.OS) 26 + (parametric.fixedTo HM-OS-USER HM-OS-USER.HM) 28 27 ]; 29 28 }; 30 29
+11
modules/output.nix
··· 1 + { inputs, lib, ... }: 2 + if inputs ? flake-parts then 3 + { } 4 + else 5 + { 6 + # NOTE: Currently Den needs a top-level attribute where to place configurations, 7 + # by default it is the `flake` attribute, even if Den uses no flake-parts at all. 8 + options.flake = lib.mkOption { 9 + type = lib.types.submodule { freeformType = lib.types.anything; }; 10 + }; 11 + }
+5 -3
nix/default.nix
··· 8 8 modules.flake = flakeModules; 9 9 templates = { 10 10 default.path = ../templates/default; 11 - default.description = "Batteries included"; 11 + default.description = "Default template"; 12 12 minimal.path = ../templates/minimal; 13 13 minimal.description = "Minimalistic den"; 14 - examples.path = ../templates/examples; 15 - examples.description = "API examples and CI"; 14 + example.path = ../templates/example; 15 + example.description = "Example"; 16 + ci.path = ../templates/ci; 17 + ci.description = "Feature Tests"; 16 18 bogus.path = ../templates/bogus; 17 19 bogus.description = "For bug reproduction"; 18 20 };
+1 -1
nix/template-packages.nix
··· 5 5 url = "https://github.com/edolstra/flake-compat/archive/${rev}.tar.gz"; 6 6 sha256 = narHash; 7 7 }; 8 - flake = import compat { src = ../templates/default; }; 8 + flake = import compat { src = ../templates/example; }; 9 9 pkgs = flake.outputs.packages; 10 10 in 11 11 {
+2 -2
templates/bogus/.github/workflows/test.yml
··· 8 8 flake-check: 9 9 strategy: 10 10 matrix: 11 - os: [ubuntu-latest, macos-latest] 11 + os: [ubuntu-latest] 12 12 name: Nix flake check 13 13 runs-on: ${{matrix.os}} 14 14 steps: 15 15 - uses: wimpysworld/nothing-but-nix@main 16 16 if: matrix.os == 'ubuntu-latest' 17 17 - uses: cachix/install-nix-action@v31 18 - - uses: DeterminateSystems/magic-nix-cache-action@13 18 + - uses: DeterminateSystems/magic-nix-cache-action@v13 19 19 - uses: actions/checkout@v5 20 20 - run: nix flake metadata 21 21 - run: nix flake check
+13 -4
templates/bogus/README.md
··· 4 4 5 5 Create a **minimal** bug reproduction at [`modules/bug.nix`](modules/bug.nix) 6 6 7 + See also [Den debugging tips](https://den.oeiuwq.com/debugging.html) 8 + 7 9 Then run tests: 8 10 9 - ```console 11 + ```shell 10 12 nix flake check 11 13 ``` 12 14 13 - Format code with: 15 + Please share a link to your reproduction repo, showing the CI step and the 16 + error at CI build. 17 + 18 + ## Fixing Den 19 + 20 + If you are contributing a bug-fix PR, you can use the following command to 21 + use your local den checkout. 14 22 15 - ```console 16 - nix fmt 23 + ```shell 24 + cd <den-working-copy> 25 + nix flake check --override-input den . ./templates/bogus 17 26 ```
+45 -79
templates/bogus/flake.lock
··· 1 1 { 2 2 "nodes": { 3 - "darwin": { 4 - "inputs": { 5 - "nixpkgs": [ 6 - "nixpkgs" 7 - ] 8 - }, 9 - "locked": { 10 - "lastModified": 1763505477, 11 - "narHash": "sha256-nJRd4LY2kT3OELfHqdgWjvToNZ4w+zKCMzS2R6z4sXE=", 12 - "owner": "nix-darwin", 13 - "repo": "nix-darwin", 14 - "rev": "3bda9f6b14161becbd07b3c56411f1670e19b9b5", 15 - "type": "github" 16 - }, 17 - "original": { 18 - "owner": "nix-darwin", 19 - "repo": "nix-darwin", 20 - "type": "github" 21 - } 22 - }, 23 3 "den": { 24 4 "locked": { 25 - "lastModified": 1763707606, 26 - "narHash": "sha256-l9v3NNdKj3GJvV5LhzsWDs4Sl2bg0tuKNFFkMeFvUWo=", 5 + "lastModified": 1770400943, 6 + "narHash": "sha256-73eh5R6UQOP6B04dxot7Jdxmc/nd3n9fe1XX8vp8l3Q=", 27 7 "owner": "vic", 28 8 "repo": "den", 29 - "rev": "8164e0d89c59839d67757bc9a1fb61770dc6e8b7", 9 + "rev": "b07ef22d338ef765e4e296fb3223ba3cf4e9ae82", 30 10 "type": "github" 31 11 }, 32 12 "original": { 33 13 "owner": "vic", 34 - "ref": "main", 35 14 "repo": "den", 36 15 "type": "github" 37 16 } 38 17 }, 39 18 "flake-aspects": { 40 19 "locked": { 41 - "lastModified": 1769717274, 20 + "lastModified": 1769723924, 42 21 "narHash": "sha256-U15OaMr9AcJiB1wW2uCFzFO+DnQ3jJSvln+ZR/+Q0vE=", 43 22 "owner": "vic", 44 23 "repo": "flake-aspects", 45 - "rev": "a35ed5efc9a629694d659d606230ba18a76cefaa", 24 + "rev": "61524836788ef6991a82e7d34ebb0ccc05d374ed", 46 25 "type": "github" 47 26 }, 48 27 "original": { ··· 51 30 "type": "github" 52 31 } 53 32 }, 54 - "flake-file": { 55 - "locked": { 56 - "lastModified": 1763706734, 57 - "narHash": "sha256-kR1Rrh9evfiJaTb6ufWCSk6GMtrnPKFydqQUV0Bb4Eg=", 58 - "owner": "vic", 59 - "repo": "flake-file", 60 - "rev": "9af20d5e62c94b658b4d0671829393c1b8bb0b3e", 61 - "type": "github" 62 - }, 63 - "original": { 64 - "owner": "vic", 65 - "repo": "flake-file", 66 - "type": "github" 67 - } 68 - }, 69 33 "flake-parts": { 70 34 "inputs": { 71 35 "nixpkgs-lib": [ 72 - "nixpkgs-lib" 36 + "nixpkgs" 73 37 ] 74 38 }, 75 39 "locked": { 76 - "lastModified": 1762440070, 77 - "narHash": "sha256-xxdepIcb39UJ94+YydGP221rjnpkDZUlykKuF54PsqI=", 40 + "lastModified": 1769996383, 41 + "narHash": "sha256-AnYjnFWgS49RlqX7LrC4uA+sCCDBj0Ry/WOJ5XWAsa0=", 78 42 "owner": "hercules-ci", 79 43 "repo": "flake-parts", 80 - "rev": "26d05891e14c88eb4a5d5bee659c0db5afb609d8", 44 + "rev": "57928607ea566b5db3ad13af0e57e921e6b12381", 81 45 "type": "github" 82 46 }, 83 47 "original": { ··· 93 57 ] 94 58 }, 95 59 "locked": { 96 - "lastModified": 1762463325, 97 - "narHash": "sha256-33YUsWpPyeBZEWrKQ2a1gkRZ7i0XCC/2MYpU6BVeQSU=", 60 + "lastModified": 1770491427, 61 + "narHash": "sha256-8b+0vixdqGnIIcgsPhjdX7EGPdzcVQqYxF+ujjex654=", 98 62 "owner": "nix-community", 99 63 "repo": "home-manager", 100 - "rev": "0562fef070a1027325dd4ea10813d64d2c967b39", 64 + "rev": "cbd8a72e5fe6af19d40e2741dc440d9227836860", 101 65 "type": "github" 102 66 }, 103 67 "original": { ··· 108 72 }, 109 73 "import-tree": { 110 74 "locked": { 111 - "lastModified": 1762327901, 112 - "narHash": "sha256-AJ96FNj50DU0bTyIzAPkPOjCZTHqjURVjok8qoXvmqM=", 75 + "lastModified": 1763762820, 76 + "narHash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=", 113 77 "owner": "vic", 114 78 "repo": "import-tree", 115 - "rev": "90fa129798be99cde036b78658e89475710966a1", 79 + "rev": "3c23749d8013ec6daa1d7255057590e9ca726646", 116 80 "type": "github" 117 81 }, 118 82 "original": { ··· 121 85 "type": "github" 122 86 } 123 87 }, 88 + "nix-github-actions": { 89 + "inputs": { 90 + "nixpkgs": [ 91 + "nix-unit", 92 + "nixpkgs" 93 + ] 94 + }, 95 + "locked": { 96 + "lastModified": 1737420293, 97 + "narHash": "sha256-F1G5ifvqTpJq7fdkT34e/Jy9VCyzd5XfJ9TO8fHhJWE=", 98 + "owner": "nix-community", 99 + "repo": "nix-github-actions", 100 + "rev": "f4158fa080ef4503c8f4c820967d946c2af31ec9", 101 + "type": "github" 102 + }, 103 + "original": { 104 + "owner": "nix-community", 105 + "repo": "nix-github-actions", 106 + "type": "github" 107 + } 108 + }, 124 109 "nix-unit": { 125 110 "inputs": { 126 111 "flake-parts": [ 127 112 "flake-parts" 128 113 ], 114 + "nix-github-actions": "nix-github-actions", 129 115 "nixpkgs": [ 130 116 "nixpkgs" 131 117 ], 132 118 "treefmt-nix": "treefmt-nix" 133 119 }, 134 120 "locked": { 135 - "lastModified": 1762507096, 136 - "narHash": "sha256-dE3CbZR8KRDdb3b4fhMnpvhEl6XB+UnrLezuFekQ2ME=", 121 + "lastModified": 1762774186, 122 + "narHash": "sha256-hRADkHjNt41+JUHw2EiSkMaL4owL83g5ZppjYUdF/Dc=", 137 123 "owner": "nix-community", 138 124 "repo": "nix-unit", 139 - "rev": "0d7230bc54783b812a5817398885aec660a93051", 125 + "rev": "1c9ab50554eed0b768f9e5b6f646d63c9673f0f7", 140 126 "type": "github" 141 127 }, 142 128 "original": { ··· 147 133 }, 148 134 "nixpkgs": { 149 135 "locked": { 150 - "lastModified": 1762361079, 151 - "narHash": "sha256-lz718rr1BDpZBYk7+G8cE6wee3PiBUpn8aomG/vLLiY=", 136 + "lastModified": 1770380644, 137 + "narHash": "sha256-P7dWMHRUWG5m4G+06jDyThXO7kwSk46C1kgjEWcybkE=", 152 138 "owner": "nixos", 153 139 "repo": "nixpkgs", 154 - "rev": "ffcdcf99d65c61956d882df249a9be53e5902ea5", 140 + "rev": "ae67888ff7ef9dff69b3cf0cc0fbfbcd3a722abe", 155 141 "type": "github" 156 142 }, 157 143 "original": { ··· 163 149 }, 164 150 "root": { 165 151 "inputs": { 166 - "darwin": "darwin", 167 152 "den": "den", 168 153 "flake-aspects": "flake-aspects", 169 - "flake-file": "flake-file", 170 154 "flake-parts": "flake-parts", 171 155 "home-manager": "home-manager", 172 156 "import-tree": "import-tree", 173 157 "nix-unit": "nix-unit", 174 - "nixpkgs": "nixpkgs", 175 - "nixpkgs-lib": [ 176 - "nixpkgs" 177 - ], 178 - "systems": "systems" 179 - } 180 - }, 181 - "systems": { 182 - "locked": { 183 - "lastModified": 1681028828, 184 - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 185 - "owner": "nix-systems", 186 - "repo": "default", 187 - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 188 - "type": "github" 189 - }, 190 - "original": { 191 - "owner": "nix-systems", 192 - "repo": "default", 193 - "type": "github" 158 + "nixpkgs": "nixpkgs" 194 159 } 195 160 }, 196 161 "treefmt-nix": { 197 162 "inputs": { 198 163 "nixpkgs": [ 164 + "nix-unit", 199 165 "nixpkgs" 200 166 ] 201 167 },
+13 -23
templates/bogus/flake.nix
··· 1 - # DO-NOT-EDIT. This file was auto-generated using github:vic/flake-file. 2 - # Use `nix run .#write-flake` to regenerate it. 3 1 { 4 - 5 2 outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 6 3 7 4 inputs = { 8 - darwin = { 9 - inputs.nixpkgs.follows = "nixpkgs"; 10 - url = "github:nix-darwin/nix-darwin"; 11 - }; 12 - den.url = "github:vic/den/main"; 5 + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 + import-tree.url = "github:vic/import-tree"; 13 7 flake-aspects.url = "github:vic/flake-aspects"; 14 - flake-file.url = "github:vic/flake-file"; 8 + den.url = "github:vic/den"; 9 + 15 10 flake-parts = { 16 - inputs.nixpkgs-lib.follows = "nixpkgs-lib"; 17 11 url = "github:hercules-ci/flake-parts"; 18 - }; 19 - home-manager = { 20 - inputs.nixpkgs.follows = "nixpkgs"; 21 - url = "github:nix-community/home-manager"; 12 + inputs.nixpkgs-lib.follows = "nixpkgs"; 22 13 }; 23 - import-tree.url = "github:vic/import-tree"; 14 + 24 15 nix-unit = { 25 - inputs = { 26 - flake-parts.follows = "flake-parts"; 27 - nixpkgs.follows = "nixpkgs"; 28 - }; 29 16 url = "github:nix-community/nix-unit"; 17 + inputs.nixpkgs.follows = "nixpkgs"; 18 + inputs.flake-parts.follows = "flake-parts"; 30 19 }; 31 - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 32 - nixpkgs-lib.follows = "nixpkgs"; 33 - systems.url = "github:nix-systems/default"; 20 + 21 + home-manager = { 22 + url = "github:nix-community/home-manager"; 23 + inputs.nixpkgs.follows = "nixpkgs"; 24 + }; 34 25 }; 35 - 36 26 }
+36 -14
templates/bogus/modules/bug.nix
··· 1 - { inputs, lib, ... }: 1 + { 2 + inputs, 3 + den, 4 + lib, 5 + ... 6 + }: 2 7 { 3 8 den.hosts.x86_64-linux.igloo.users.tux = { }; 4 - den.hosts.aarch64-darwin.apple.users.tim = { }; 5 9 10 + den.aspects.igloo.includes = [ den.aspects.testing ]; 6 11 # Use aspects to create a **minimal** bug reproduction 7 - den.aspects.igloo.nixos = 8 - { pkgs, ... }: 9 - { 10 - users.users.tux.packages = [ pkgs.hello ]; 12 + den.aspects.testing = 13 + { user, ... }@ctx: 14 + builtins.trace ctx.host.hostName { 15 + homeManager.programs.vim.enable = user.userName == "tux"; 11 16 }; 12 17 13 - # rename "it works", evidently it has bugs 14 - flake.tests."test it works" = 18 + # `nix-unit --flake .#.tests.systems` 19 + # `nix eval .#.tests.testItWorks` 20 + flake.tests.testItWorks = 15 21 let 16 - tux = inputs.self.nixosConfigurations.igloo.config.users.users.tux; 17 - 18 - expr.len = lib.length tux.packages; 19 - expr.names = map lib.getName tux.packages; 22 + igloo = inputs.self.nixosConfigurations.igloo.config; 23 + tux = igloo.home-manager.users.tux; 20 24 21 - expected.len = 1; 22 - expected.names = [ "hello" ]; 25 + expr = tux.programs.vim.enable; 26 + expected = true; 23 27 in 24 28 { 25 29 inherit expr expected; 26 30 }; 31 + 32 + # See [Debugging Tips](https://den.oeiuwq.com/debugging.html) 33 + flake.den = den; 34 + # `nix eval .#.value` 35 + flake.value = 36 + let 37 + aspect = den.aspects.testing { 38 + user.userName = "tux"; 39 + host.hostName = "fake"; 40 + }; 41 + modules = [ 42 + (aspect.resolve { class = "homeManager"; }) 43 + { options.programs = lib.mkOption { }; } 44 + ]; 45 + evaled = lib.evalModules { inherit modules; }; 46 + in 47 + evaled.config; 48 + 27 49 }
-9
templates/bogus/modules/dendritic.nix
··· 1 - { inputs, lib, ... }: 2 - { 3 - flake-file.inputs.flake-file.url = lib.mkDefault "github:vic/flake-file"; 4 - flake-file.inputs.den.url = lib.mkDefault "github:vic/den"; 5 - imports = [ 6 - (inputs.flake-file.flakeModules.dendritic or { }) 7 - (inputs.den.flakeModules.dendritic or { }) 8 - ]; 9 - }
-26
templates/bogus/modules/flakes.nix
··· 1 - # DO-NOT-CHANGE. Keep your reproduction minimalistic! 2 - # 3 - # try not adding new inputs 4 - # but if you have no options (pun intended) 5 - # here's the place. 6 - # 7 - # IF you make any change to this file, use: 8 - # `nix run .#write-flake` 9 - # 10 - # We provide nix-darwin and home-manager for common usage. 11 - { 12 - # change "main" with a commit where bug is present 13 - flake-file.inputs.den.url = "github:vic/den/main"; 14 - 15 - # included so we can test HM integrations. 16 - flake-file.inputs.home-manager = { 17 - url = "github:nix-community/home-manager"; 18 - inputs.nixpkgs.follows = "nixpkgs"; 19 - }; 20 - 21 - # included for testing darwin hosts. 22 - flake-file.inputs.darwin = { 23 - url = "github:nix-darwin/nix-darwin"; 24 - inputs.nixpkgs.follows = "nixpkgs"; 25 - }; 26 - }
-19
templates/bogus/modules/nix-unit.nix
··· 1 - # DO-NOT-EDIT: nix-unit configuration. 2 - { lib, inputs, ... }: 3 - { 4 - 5 - flake-file.inputs.nix-unit = { 6 - url = "github:nix-community/nix-unit"; 7 - inputs.nixpkgs.follows = "nixpkgs"; 8 - inputs.flake-parts.follows = "flake-parts"; 9 - }; 10 - 11 - imports = [ 12 - inputs.nix-unit.modules.flake.default 13 - ]; 14 - 15 - perSystem.nix-unit = { 16 - allowNetwork = lib.mkDefault true; 17 - inputs = lib.mkDefault inputs; 18 - }; 19 - }
+25 -8
templates/bogus/modules/test-base.nix
··· 1 - { den, ... }: 1 + # DO-NOT-EDIT unless necessary. Keep your reproduction repo minimal. 2 + { 3 + den, 4 + lib, 5 + inputs, 6 + ... 7 + }: 2 8 { 3 - den.default.nixos.system.stateVersion = "25.11"; 4 - den.default.homeManager.home.stateVersion = "25.11"; 5 - den.default.darwin.system.stateVersion = 6; 9 + imports = [ 10 + inputs.den.flakeModule 11 + inputs.nix-unit.modules.flake.default 12 + ]; 13 + 14 + systems = lib.attrNames den.hosts; 15 + 16 + den.default.nixos.system.stateVersion = "26.05"; 17 + den.default.homeManager.home.stateVersion = "26.05"; 6 18 7 19 den.default.includes = [ 8 - den._.home-manager 9 - den._.define-user 20 + den.provides.home-manager 21 + den.provides.define-user 10 22 den.aspects.no-boot 11 23 ]; 12 24 13 25 den.aspects.no-boot.nixos = { 14 - boot.loader.grub.enable = false; 15 - fileSystems."/".device = "/dev/fake"; 26 + boot.loader.grub.enable = lib.mkForce false; 27 + fileSystems."/".device = lib.mkForce "/dev/fake"; 28 + }; 29 + 30 + perSystem.nix-unit = { 31 + allowNetwork = lib.mkDefault true; 32 + inputs = lib.mkDefault inputs; 16 33 }; 17 34 }
+304
templates/ci/flake.lock
··· 1 + { 2 + "nodes": { 3 + "darwin": { 4 + "inputs": { 5 + "nixpkgs": [ 6 + "nixpkgs" 7 + ] 8 + }, 9 + "locked": { 10 + "lastModified": 1762039661, 11 + "narHash": "sha256-oM5BwAGE78IBLZn+AqxwH/saqwq3e926rNq5HmOulkc=", 12 + "owner": "nix-darwin", 13 + "repo": "nix-darwin", 14 + "rev": "c3c8c9f2a5ed43175ac4dc030308756620e6e4e4", 15 + "type": "github" 16 + }, 17 + "original": { 18 + "owner": "nix-darwin", 19 + "repo": "nix-darwin", 20 + "type": "github" 21 + } 22 + }, 23 + "den": { 24 + "locked": { 25 + "lastModified": 1767645236, 26 + "narHash": "sha256-VMrWett3fWRb0hQh9K1JUC3zV2OlV4zOajFZHGr1GPw=", 27 + "owner": "vic", 28 + "repo": "den", 29 + "rev": "e7837daa0ea0f03fba62a2653ce16314a7d9d1c8", 30 + "type": "github" 31 + }, 32 + "original": { 33 + "owner": "vic", 34 + "repo": "den", 35 + "type": "github" 36 + } 37 + }, 38 + "flake-aspects": { 39 + "locked": { 40 + "lastModified": 1769717274, 41 + "narHash": "sha256-U15OaMr9AcJiB1wW2uCFzFO+DnQ3jJSvln+ZR/+Q0vE=", 42 + "owner": "vic", 43 + "repo": "flake-aspects", 44 + "rev": "a35ed5efc9a629694d659d606230ba18a76cefaa", 45 + "type": "github" 46 + }, 47 + "original": { 48 + "owner": "vic", 49 + "repo": "flake-aspects", 50 + "type": "github" 51 + } 52 + }, 53 + "flake-file": { 54 + "locked": { 55 + "lastModified": 1763706734, 56 + "narHash": "sha256-kR1Rrh9evfiJaTb6ufWCSk6GMtrnPKFydqQUV0Bb4Eg=", 57 + "owner": "vic", 58 + "repo": "flake-file", 59 + "rev": "9af20d5e62c94b658b4d0671829393c1b8bb0b3e", 60 + "type": "github" 61 + }, 62 + "original": { 63 + "owner": "vic", 64 + "repo": "flake-file", 65 + "type": "github" 66 + } 67 + }, 68 + "flake-parts": { 69 + "inputs": { 70 + "nixpkgs-lib": [ 71 + "nixpkgs-lib" 72 + ] 73 + }, 74 + "locked": { 75 + "lastModified": 1762040540, 76 + "narHash": "sha256-z5PlZ47j50VNF3R+IMS9LmzI5fYRGY/Z5O5tol1c9I4=", 77 + "owner": "hercules-ci", 78 + "repo": "flake-parts", 79 + "rev": "0010412d62a25d959151790968765a70c436598b", 80 + "type": "github" 81 + }, 82 + "original": { 83 + "owner": "hercules-ci", 84 + "repo": "flake-parts", 85 + "type": "github" 86 + } 87 + }, 88 + "home-manager": { 89 + "inputs": { 90 + "nixpkgs": [ 91 + "nixpkgs" 92 + ] 93 + }, 94 + "locked": { 95 + "lastModified": 1762087455, 96 + "narHash": "sha256-hpbPma1eUKwLAmiVRoMgIHbHiIKFkcACobJLbDt6ABw=", 97 + "owner": "nix-community", 98 + "repo": "home-manager", 99 + "rev": "43e205606aeb253bfcee15fd8a4a01d8ce8384ca", 100 + "type": "github" 101 + }, 102 + "original": { 103 + "owner": "nix-community", 104 + "repo": "home-manager", 105 + "type": "github" 106 + } 107 + }, 108 + "home-manager-stable": { 109 + "inputs": { 110 + "nixpkgs": [ 111 + "nixpkgs-stable" 112 + ] 113 + }, 114 + "locked": { 115 + "lastModified": 1758463745, 116 + "narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=", 117 + "owner": "nix-community", 118 + "repo": "home-manager", 119 + "rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3", 120 + "type": "github" 121 + }, 122 + "original": { 123 + "owner": "nix-community", 124 + "ref": "release-25.05", 125 + "repo": "home-manager", 126 + "type": "github" 127 + } 128 + }, 129 + "import-tree": { 130 + "locked": { 131 + "lastModified": 1761120675, 132 + "narHash": "sha256-TEbh9zISiQcU82VwVoEbmXHnSGlUxTwvjJA9g9ErSDA=", 133 + "owner": "vic", 134 + "repo": "import-tree", 135 + "rev": "a037ed2a58fc0ebed9e93b9ef79b0646e648f719", 136 + "type": "github" 137 + }, 138 + "original": { 139 + "owner": "vic", 140 + "repo": "import-tree", 141 + "type": "github" 142 + } 143 + }, 144 + "neovim-nightly-overlay": { 145 + "inputs": { 146 + "flake-parts": [ 147 + "flake-parts" 148 + ], 149 + "neovim-src": "neovim-src", 150 + "nixpkgs": [ 151 + "nixpkgs" 152 + ] 153 + }, 154 + "locked": { 155 + "lastModified": 1766016290, 156 + "narHash": "sha256-YMf/PUyY4z7RlIe/Dzn1NnxZGS0Vp2eHxcMNWJM9q+A=", 157 + "owner": "nix-community", 158 + "repo": "neovim-nightly-overlay", 159 + "rev": "f7fbc4e3d4ccea45eaa5b187884592eb42dfdbbd", 160 + "type": "github" 161 + }, 162 + "original": { 163 + "owner": "nix-community", 164 + "repo": "neovim-nightly-overlay", 165 + "type": "github" 166 + } 167 + }, 168 + "neovim-src": { 169 + "flake": false, 170 + "locked": { 171 + "lastModified": 1766014002, 172 + "narHash": "sha256-KE/ufBGH8XFXTw3Vt1DrK1rQmAEp1Q+oyLQibX5UKO0=", 173 + "owner": "neovim", 174 + "repo": "neovim", 175 + "rev": "c172fd9f464d5766eab9071e8f4770504c920c05", 176 + "type": "github" 177 + }, 178 + "original": { 179 + "owner": "neovim", 180 + "repo": "neovim", 181 + "type": "github" 182 + } 183 + }, 184 + "nixos-wsl": { 185 + "inputs": { 186 + "flake-compat": [], 187 + "nixpkgs": [ 188 + "nixpkgs-stable" 189 + ] 190 + }, 191 + "locked": { 192 + "lastModified": 1761969132, 193 + "narHash": "sha256-0me4+e+1VxNuvySSw0voqMCWU/eUmTuth7f4+Q2jbUY=", 194 + "owner": "nix-community", 195 + "repo": "nixos-wsl", 196 + "rev": "761582d6ab431549fe1396d2cd681e3fe9376020", 197 + "type": "github" 198 + }, 199 + "original": { 200 + "owner": "nix-community", 201 + "repo": "nixos-wsl", 202 + "type": "github" 203 + } 204 + }, 205 + "nixpkgs": { 206 + "locked": { 207 + "lastModified": 1761880412, 208 + "narHash": "sha256-QoJjGd4NstnyOG4mm4KXF+weBzA2AH/7gn1Pmpfcb0A=", 209 + "owner": "nixos", 210 + "repo": "nixpkgs", 211 + "rev": "a7fc11be66bdfb5cdde611ee5ce381c183da8386", 212 + "type": "github" 213 + }, 214 + "original": { 215 + "owner": "nixos", 216 + "ref": "nixpkgs-unstable", 217 + "repo": "nixpkgs", 218 + "type": "github" 219 + } 220 + }, 221 + "nixpkgs-stable": { 222 + "locked": { 223 + "lastModified": 1762081535, 224 + "narHash": "sha256-+j+CUiaUoa87EhnSOqG5pwXdJWahP8vo6BE0ekssdzs=", 225 + "owner": "nixos", 226 + "repo": "nixpkgs", 227 + "rev": "2afc9d6e79b59ea9bcaf620d335623b0f7c2ce96", 228 + "type": "github" 229 + }, 230 + "original": { 231 + "owner": "nixos", 232 + "ref": "release-25.05", 233 + "repo": "nixpkgs", 234 + "type": "github" 235 + } 236 + }, 237 + "provider": { 238 + "inputs": { 239 + "den": [ 240 + "den" 241 + ], 242 + "flake-aspects": [ 243 + "flake-aspects" 244 + ], 245 + "flake-parts": [ 246 + "flake-parts" 247 + ], 248 + "import-tree": [ 249 + "import-tree" 250 + ], 251 + "nixpkgs": [ 252 + "nixpkgs" 253 + ] 254 + }, 255 + "locked": { 256 + "path": "./provider", 257 + "type": "path" 258 + }, 259 + "original": { 260 + "path": "./provider", 261 + "type": "path" 262 + }, 263 + "parent": [] 264 + }, 265 + "root": { 266 + "inputs": { 267 + "darwin": "darwin", 268 + "den": "den", 269 + "flake-aspects": "flake-aspects", 270 + "flake-file": "flake-file", 271 + "flake-parts": "flake-parts", 272 + "home-manager": "home-manager", 273 + "home-manager-stable": "home-manager-stable", 274 + "import-tree": "import-tree", 275 + "neovim-nightly-overlay": "neovim-nightly-overlay", 276 + "nixos-wsl": "nixos-wsl", 277 + "nixpkgs": "nixpkgs", 278 + "nixpkgs-lib": [ 279 + "nixpkgs" 280 + ], 281 + "nixpkgs-stable": "nixpkgs-stable", 282 + "provider": "provider", 283 + "systems": "systems" 284 + } 285 + }, 286 + "systems": { 287 + "locked": { 288 + "lastModified": 1681028828, 289 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 290 + "owner": "nix-systems", 291 + "repo": "default", 292 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 293 + "type": "github" 294 + }, 295 + "original": { 296 + "owner": "nix-systems", 297 + "repo": "default", 298 + "type": "github" 299 + } 300 + } 301 + }, 302 + "root": "root", 303 + "version": 7 304 + }
+58
templates/ci/flake.nix
··· 1 + # DO-NOT-EDIT. This file was auto-generated using github:vic/flake-file. 2 + # Use `nix run .#write-flake` to regenerate it. 3 + { 4 + 5 + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 6 + 7 + inputs = { 8 + darwin = { 9 + inputs.nixpkgs.follows = "nixpkgs"; 10 + url = "github:nix-darwin/nix-darwin"; 11 + }; 12 + den.url = "github:vic/den"; 13 + flake-aspects.url = "github:vic/flake-aspects"; 14 + flake-file.url = "github:vic/flake-file"; 15 + flake-parts = { 16 + inputs.nixpkgs-lib.follows = "nixpkgs-lib"; 17 + url = "github:hercules-ci/flake-parts"; 18 + }; 19 + home-manager = { 20 + inputs.nixpkgs.follows = "nixpkgs"; 21 + url = "github:nix-community/home-manager"; 22 + }; 23 + home-manager-stable = { 24 + inputs.nixpkgs.follows = "nixpkgs-stable"; 25 + url = "github:nix-community/home-manager/release-25.05"; 26 + }; 27 + import-tree.url = "github:vic/import-tree"; 28 + neovim-nightly-overlay = { 29 + inputs = { 30 + flake-parts.follows = "flake-parts"; 31 + nixpkgs.follows = "nixpkgs"; 32 + }; 33 + url = "github:nix-community/neovim-nightly-overlay"; 34 + }; 35 + nixos-wsl = { 36 + inputs = { 37 + flake-compat.follows = ""; 38 + nixpkgs.follows = "nixpkgs-stable"; 39 + }; 40 + url = "github:nix-community/nixos-wsl"; 41 + }; 42 + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 43 + nixpkgs-lib.follows = "nixpkgs"; 44 + nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 45 + provider = { 46 + inputs = { 47 + den.follows = "den"; 48 + flake-aspects.follows = "flake-aspects"; 49 + flake-parts.follows = "flake-parts"; 50 + import-tree.follows = "import-tree"; 51 + nixpkgs.follows = "nixpkgs"; 52 + }; 53 + url = "path:./provider"; 54 + }; 55 + systems.url = "github:nix-systems/default"; 56 + }; 57 + 58 + }
+81
templates/ci/modules/angle-bracket-deep.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + ns, 5 + # deadnix: skip 6 + __findFile ? __findFile, 7 + ... 8 + }: 9 + let 10 + treeModule.nixos.options.tree = lib.mkOption { 11 + type = lib.types.listOf lib.types.str; 12 + }; 13 + inputX = { 14 + denful.ns.root = { 15 + nixos.tree = [ "X-root" ]; 16 + provides.branch.nixos.tree = [ "X-branch" ]; 17 + provides.branch.provides.leaf.nixos.tree = [ "X-leaf" ]; 18 + }; 19 + }; 20 + inputY = { 21 + denful.ns.root = { 22 + nixos.tree = [ "Y-root" ]; 23 + provides.branch.nixos.tree = [ "Y-branch" ]; 24 + provides.branch.provides.leaf.nixos.tree = [ "Y-leaf" ]; 25 + }; 26 + }; 27 + in 28 + { 29 + 30 + imports = [ 31 + (inputs.den.namespace "ns" [ 32 + true 33 + inputX 34 + inputY 35 + ]) 36 + ]; 37 + 38 + ns.root = { 39 + nixos.tree = [ "local-root" ]; 40 + provides.branch.nixos.tree = [ "local-branch" ]; 41 + provides.branch.provides.leaf.nixos.tree = [ "local-leaf" ]; 42 + }; 43 + 44 + den.aspects.rockhopper.includes = [ 45 + treeModule 46 + <ns/root> 47 + <ns/root/branch> 48 + <ns/root/branch/leaf> 49 + ]; 50 + 51 + perSystem = 52 + { checkCond, rockhopper, ... }: 53 + let 54 + vals = lib.sort (a: b: a < b) rockhopper.config.tree; 55 + in 56 + { 57 + checks.ns-angle-bracket-root = checkCond "angle-bracket access root" (<ns/root> == ns.root); 58 + 59 + checks.ns-angle-bracket-branch = checkCond "angle-bracket access branch" ( 60 + <ns/root/branch> == ns.root._.branch 61 + ); 62 + 63 + checks.ns-angle-bracket-leaf = checkCond "angle-bracket access leaf" ( 64 + <ns/root/branch/leaf> == ns.root._.branch._.leaf 65 + ); 66 + 67 + checks.ns-tree-all-levels-merged = checkCond "all tree levels merged" ( 68 + vals == [ 69 + "X-branch" 70 + "X-leaf" 71 + "X-root" 72 + "Y-branch" 73 + "Y-leaf" 74 + "Y-root" 75 + "local-branch" 76 + "local-leaf" 77 + "local-root" 78 + ] 79 + ); 80 + }; 81 + }
+28
templates/ci/modules/auto-imported.nix
··· 1 + # configures class-automatic module auto imports for hosts/users/homes. 2 + # See documentation at modules/aspects/provides/import-tree.nix 3 + { 4 + # deadnix: skip 5 + __findFile ? __findFile, 6 + ... 7 + }: 8 + { 9 + 10 + # alice imports non-dendritic <class> modules from ../non-dendritic/alice/_<class>/*.nix 11 + den.aspects.alice.includes = [ (<den/import-tree> ./../non-dendritic/alice) ]; 12 + 13 + # See the documentation at batteries/import-tree.nix 14 + den.default.includes = [ 15 + (<den/import-tree/host> ./../non-dendritic/hosts) 16 + (<den/import-tree/user> ./../non-dendritic/users) 17 + (<den/import-tree/home> ./../non-dendritic/homes) 18 + ]; 19 + 20 + # tests 21 + perSystem = 22 + { checkCond, rockhopper, ... }: 23 + { 24 + checks.import-tree = checkCond "auto-imported from rockhopper/_nixos" ( 25 + rockhopper.config.auto-imported 26 + ); 27 + }; 28 + }
+76
templates/ci/modules/base-conf-modules.nix
··· 1 + # tests for extending `den.base.*` modules with capabilities (options). 2 + # these allow you to expend all hosts/users/homes with custom option 3 + # that can later be used by aspects for providing features. 4 + # 5 + { lib, ... }: 6 + { 7 + 8 + # This module is base for all host configs. 9 + den.base.host = 10 + { host, ... }: 11 + { 12 + options.capabilities.ssh-server = lib.mkEnableOption "Does host ${host.name} provide ssh?"; 13 + }; 14 + 15 + # This module is base for all user configs. 16 + den.base.user = 17 + { user, ... }: 18 + { 19 + options.isAdmin = lib.mkOption { 20 + type = lib.types.bool; 21 + default = user.name == "alice"; # only alice is always admin 22 + }; 23 + }; 24 + 25 + # This module is base for all home configs. 26 + # den.base.home = { home, ... }: { }; 27 + 28 + # This one is included on each host/user/home 29 + # it cannot access host/user/home values since this conf is generic. 30 + den.base.conf = { 31 + options.foo = lib.mkOption { 32 + type = lib.types.str; 33 + default = "bar"; 34 + }; 35 + }; 36 + 37 + # Now hosts and users can set any option defined in base modules. 38 + den.hosts.x86_64-linux.rockhopper = { 39 + capabilities.ssh-server = true; 40 + foo = "boo"; 41 + users.alice = { 42 + # isAdmin = true; # alice is admin by default, nothing explicit here. 43 + foo = "moo"; 44 + }; 45 + }; 46 + 47 + den.aspects.rockhopper.includes = 48 + let 49 + # An aspect can make use of these options to provide configuration. 50 + sshCapable = 51 + { host, ... }: 52 + { 53 + nixos.services.sshd.enable = host.capabilities.ssh-server; 54 + homeManager.services.ssh-agent.enable = host.capabilities.ssh-server; 55 + }; 56 + in 57 + [ sshCapable ]; 58 + 59 + # CI checks 60 + perSystem = 61 + { 62 + checkCond, 63 + rockhopper, 64 + alice-at-rockhopper, 65 + ... 66 + }: 67 + { 68 + checks.host-conf-rockhopper-sshd = checkCond "sshd enabled" ( 69 + rockhopper.config.services.sshd.enable == true 70 + ); 71 + checks.host-conf-alice-sshd = checkCond "ssh-agent enabled" ( 72 + alice-at-rockhopper.services.ssh-agent.enable == true 73 + ); 74 + }; 75 + 76 + }
+27
templates/ci/modules/builds.nix
··· 1 + # Adds some checks for CI 2 + { 3 + perSystem = 4 + { 5 + pkgs, 6 + checkFile, 7 + rockhopper, 8 + honeycrisp, 9 + cam, 10 + bob, 11 + ... 12 + }: 13 + let 14 + checks.x86_64-linux = { 15 + vm = checkFile "vm-builds" "${rockhopper.config.system.build.vm}/bin/run-rockhopper-vm"; 16 + hosts-rockhopper = checkFile "nixos-builds" rockhopper.config.system.build.toplevel; 17 + homes-cam = checkFile "home-builds" cam.activation-script; 18 + }; 19 + checks.aarch64-darwin = { 20 + hosts-honeycrisp = checkFile "darwin-builds" honeycrisp.config.system.build.toplevel; 21 + homes-bob = checkFile "darwin-home-builds" bob.activation-script; 22 + }; 23 + in 24 + { 25 + checks = checks.${pkgs.stdenv.hostPlatform.system} or { }; 26 + }; 27 + }
+67
templates/ci/modules/cross-flake-parametric.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + provider, 5 + ... 6 + }: 7 + let 8 + providerModule.nixos.options.providerVars = lib.mkOption { 9 + type = lib.types.attrsOf lib.types.str; 10 + default = { }; 11 + }; 12 + in 13 + { 14 + flake-file.inputs.provider = { 15 + url = "path:./provider"; 16 + inputs.den.follows = "den"; 17 + inputs.flake-aspects.follows = "flake-aspects"; 18 + inputs.flake-parts.follows = "flake-parts"; 19 + inputs.import-tree.follows = "import-tree"; 20 + inputs.nixpkgs.follows = "nixpkgs"; 21 + }; 22 + 23 + imports = [ 24 + (inputs.den.namespace "provider" [ 25 + true 26 + inputs.provider 27 + ]) 28 + ]; 29 + 30 + provider.tools._.dev._.editors = { 31 + nixos.providerVars.LOCAL_EDITOR = "emacs"; 32 + }; 33 + 34 + den.aspects.rockhopper.includes = [ 35 + providerModule 36 + provider.tools._.dev._.editors 37 + provider.tools._.dev._.shells 38 + ]; 39 + 40 + perSystem = 41 + { checkCond, rockhopper, ... }: 42 + let 43 + vars = rockhopper.config.providerVars; 44 + env = rockhopper.config.environment.variables; 45 + in 46 + { 47 + checks.cross-flake-provider-editor = checkCond "provider editor var set" ( 48 + env.PROVIDER_EDITOR == "vim" 49 + ); 50 + 51 + checks.cross-flake-provider-shell = checkCond "provider shell var set" ( 52 + env.PROVIDER_SHELL == "fish" 53 + ); 54 + 55 + checks.cross-flake-local-editor = checkCond "local editor var set" (vars.LOCAL_EDITOR == "emacs"); 56 + 57 + checks.cross-flake-namespace-merged = checkCond "namespace merged from provider input" ( 58 + provider.tools._.dev._.editors == inputs.self.denful.provider.tools._.dev._.editors 59 + && provider.tools._.dev._.shells == inputs.self.denful.provider.tools._.dev._.shells 60 + ); 61 + 62 + checks.cross-flake-input-denful = checkCond "input provider denful accessible" ( 63 + inputs.provider.denful.provider.tools._.dev._.editors.description 64 + == "Editor configurations from provider flake" 65 + ); 66 + }; 67 + }
+30
templates/ci/modules/custom-home-managed-package.nix
··· 1 + { 2 + # Including an static aspect should not cause duplicate definitions 3 + den.aspects.alice.includes = [ 4 + { 5 + homeManager = 6 + { pkgs, ... }: 7 + { 8 + programs.emacs.enable = true; 9 + programs.emacs.package = pkgs.emacs30-nox; 10 + }; 11 + } 12 + ]; 13 + 14 + perSystem = 15 + { 16 + checkCond, 17 + alice-at-rockhopper, 18 + lib, 19 + ... 20 + }: 21 + { 22 + checks.alice-custom-emacs = checkCond "set uniquely via a static includes" ( 23 + let 24 + expr = lib.getName alice-at-rockhopper.programs.emacs.package; 25 + expected = "emacs-nox"; 26 + in 27 + expr == expected 28 + ); 29 + }; 30 + }
+35
templates/ci/modules/custom-nixos-module.nix
··· 1 + { lib, ... }: 2 + let 3 + # A custom `nixos` class module that defines an option `names`. 4 + # Used to test that we are not duplicating values from owned configs. 5 + nixosNames = names: { options.${names} = lib.mkOption { type = lib.types.listOf lib.types.str; }; }; 6 + in 7 + { 8 + den.default.nixos.imports = [ (nixosNames "people") ]; 9 + den.default.includes = [ 10 + ( 11 + { user, ... }: 12 + { 13 + nixos.people = [ user.name ]; 14 + } 15 + ) 16 + ]; 17 + 18 + den.aspects.rockhopper.includes = [ 19 + # Example: importing a third-party nixos module. 20 + { nixos.imports = [ (nixosNames "names") ]; } 21 + ]; 22 + 23 + den.aspects.rockhopper.nixos.names = [ "tux" ]; 24 + 25 + perSystem = 26 + { checkCond, rockhopper, ... }: 27 + { 28 + checks.rockhopper-default-people = checkCond "set from den.default for each user" ( 29 + rockhopper.config.people == [ "alice" ] 30 + ); 31 + checks.rockhopper-names-single-entry = checkCond "custom nixos array option set once" ( 32 + rockhopper.config.names == [ "tux" ] 33 + ); 34 + }; 35 + }
+78
templates/ci/modules/deep-nested-namespace.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + deep, 5 + ... 6 + }: 7 + let 8 + deepModule.nixos.options.deepVals = lib.mkOption { 9 + type = lib.types.listOf lib.types.str; 10 + }; 11 + depthA = { 12 + denful.deep.a._.b._.c._.d._.e = { 13 + description = "5 levels deep from inputA"; 14 + nixos.deepVals = [ "inputA-5-levels" ]; 15 + provides.f.nixos.deepVals = [ "inputA-6-levels" ]; 16 + }; 17 + }; 18 + depthB = { 19 + denful.deep.a._.b._.c._.d._.e = { 20 + nixos.deepVals = [ "inputB-5-levels" ]; 21 + provides.f.nixos.deepVals = [ "inputB-6-levels" ]; 22 + }; 23 + }; 24 + in 25 + { 26 + imports = [ 27 + (inputs.den.namespace "deep" [ 28 + true 29 + depthA 30 + depthB 31 + ]) 32 + ]; 33 + 34 + deep.a._.b._.c._.d._.e = { 35 + nixos.deepVals = [ "local-5-levels" ]; 36 + provides.f.nixos.deepVals = [ "local-6-levels" ]; 37 + }; 38 + 39 + den.aspects.rockhopper.includes = [ 40 + deepModule 41 + deep.a._.b._.c._.d._.e 42 + deep.a._.b._.c._.d._.e._.f 43 + ]; 44 + 45 + perSystem = 46 + { checkCond, rockhopper, ... }: 47 + let 48 + vals = lib.sort (a: b: a < b) rockhopper.config.deepVals; 49 + in 50 + { 51 + checks.deep-nest-5-levels = checkCond "5 levels deep merged correctly" ( 52 + builtins.elem "inputA-5-levels" vals 53 + && builtins.elem "inputB-5-levels" vals 54 + && builtins.elem "local-5-levels" vals 55 + ); 56 + 57 + checks.deep-nest-6-levels = checkCond "6 levels deep merged correctly" ( 58 + builtins.elem "inputA-6-levels" vals 59 + && builtins.elem "inputB-6-levels" vals 60 + && builtins.elem "local-6-levels" vals 61 + ); 62 + 63 + checks.deep-nest-all-merged = checkCond "all values merged" ( 64 + vals == [ 65 + "inputA-5-levels" 66 + "inputA-6-levels" 67 + "inputB-5-levels" 68 + "inputB-6-levels" 69 + "local-5-levels" 70 + "local-6-levels" 71 + ] 72 + ); 73 + 74 + checks.deep-nest-flake-output = checkCond "deep namespace as flake output" ( 75 + inputs.self.denful.deep.a._.b._.c._.d._.e._.f == deep.a._.b._.c._.d._.e._.f 76 + ); 77 + }; 78 + }
+14
templates/ci/modules/defaults.nix
··· 1 + # User TODO: Remove this file. 2 + { 3 + # default aspect can be used for global static settings. 4 + den.default = { 5 + # static values. 6 + darwin.system.stateVersion = 6; 7 + nixos.system.stateVersion = "25.05"; 8 + homeManager.home.stateVersion = "25.05"; 9 + 10 + # these defaults are set for checking with CI. 11 + nixos.programs.vim.enable = true; 12 + darwin.programs.zsh.enable = true; 13 + }; 14 + }
+27
templates/ci/modules/define-user.nix
··· 1 + { den, ... }: 2 + { 3 + den.default.includes = [ 4 + # Example: parametric over many contexts: { home }, { host, user }, { fromUser, toHost } 5 + den.provides.define-user 6 + ]; 7 + 8 + perSystem = 9 + { 10 + checkCond, 11 + rockhopper, 12 + adelie, 13 + ... 14 + }: 15 + { 16 + 17 + checks.alice-exists-on-rockhopper = checkCond "den.default.user.includes defines user on host" ( 18 + rockhopper.config.users.users.alice.isNormalUser 19 + ); 20 + checks.alice-not-exists-on-adelie = checkCond "den.default.user.includes defines user on host" ( 21 + !adelie.config.users.users ? alice 22 + ); 23 + checks.will-exists-on-adelie = checkCond "den.default.user.includes defines user on host" ( 24 + adelie.config.users.users.will.isNormalUser 25 + ); 26 + }; 27 + }
+9
templates/ci/modules/dendritic.nix
··· 1 + { inputs, lib, ... }: 2 + { 3 + flake-file.inputs.flake-file.url = lib.mkDefault "github:vic/flake-file"; 4 + flake-file.inputs.den.url = lib.mkDefault "github:vic/den"; 5 + imports = [ 6 + (inputs.flake-file.flakeModules.dendritic or { }) 7 + (inputs.den.flakeModules.dendritic or { }) 8 + ]; 9 + }
+8
templates/ci/modules/enable-wsl-host.nix
··· 1 + { inputs, ... }: 2 + { 3 + # Example: adelie host using github:nix-community/NixOS-WSL 4 + den.aspects.adelie.nixos = { 5 + imports = [ inputs.nixos-wsl.nixosModules.default ]; 6 + wsl.enable = true; 7 + }; 8 + }
+37
templates/ci/modules/helpers.nix
··· 1 + { self, ... }: 2 + { 3 + perSystem = 4 + { pkgs, ... }: 5 + let 6 + checkFile = 7 + name: file: 8 + pkgs.runCommandLocal name { } '' 9 + ls -la ${file} | tee $out 10 + ''; 11 + 12 + checkCond = 13 + name: cond: 14 + let 15 + code = if cond then "touch $out" else ''echo "Cond-Failed: ${name}"''; 16 + in 17 + pkgs.runCommandLocal name { } code; 18 + 19 + rockhopper = self.nixosConfigurations.rockhopper; 20 + honeycrisp = self.darwinConfigurations.honeycrisp; 21 + adelie = self.wslConfigurations.adelie; 22 + cam = self.homeConfigurations.cam; 23 + bob = self.homeConfigurations.bob; 24 + luke = self.homeConfigurations.luke; 25 + 26 + alice-at-rockhopper = rockhopper.config.home-manager.users.alice; 27 + alice-at-honeycrisp = honeycrisp.config.home-manager.users.alice; 28 + in 29 + { 30 + _module.args = { 31 + inherit checkCond checkFile; 32 + inherit rockhopper honeycrisp adelie; 33 + inherit cam bob luke; 34 + inherit alice-at-rockhopper alice-at-honeycrisp; 35 + }; 36 + }; 37 + }
+29
templates/ci/modules/hm-enabled-host.nix
··· 1 + { den, inputs, ... }: 2 + { 3 + # The `{ HM-OS-HOST }` context is activated ONLY for hosts that have 4 + # a HM supported OS and at least one user with homeManager class. 5 + den.aspects.hm-global-pkgs = 6 + { HM-OS-HOST }: 7 + den.lib.take.unused [ HM-OS-HOST.host ] # access host from context if needed 8 + { 9 + nixos.home-manager.useGlobalPkgs = true; 10 + }; 11 + 12 + den.default.includes = [ den.aspects.hm-global-pkgs ]; 13 + 14 + den.hosts.x86_64-linux.no-homes = { }; 15 + 16 + perSystem = 17 + { checkCond, rockhopper, ... }: 18 + { 19 + checks.rockhopper-hm-global-pkgs = checkCond "rockhopper-hm-global-pkgs" ( 20 + rockhopper.config.home-manager.useGlobalPkgs 21 + ); 22 + 23 + checks.no-homes-hm-global-pkgs = checkCond "no-homes-hm-global-pkgs" ( 24 + # no home-manager enabled nor useGlobalPkgs 25 + !inputs.self.nixosConfigurations.no-homes.config ? home-manager.useGlobalPkgs 26 + ); 27 + }; 28 + 29 + }
+12
templates/ci/modules/home-managed.nix
··· 1 + { den, ... }: 2 + { 3 + # see batteries/home-manager.nix 4 + den.default.includes = [ den._.home-manager ]; 5 + 6 + # enable home-manager dependency. 7 + flake-file.inputs.home-manager = { 8 + url = "github:nix-community/home-manager"; 9 + inputs.nixpkgs.follows = "nixpkgs"; 10 + }; 11 + 12 + }
+31
templates/ci/modules/homes.nix
··· 1 + # Example standalone home-manager configurations. 2 + # These are independent of any host configuration. 3 + # See documentation at <den>/nix/types.nix 4 + { inputs, ... }: 5 + { 6 + den.homes.x86_64-linux.cam = { }; 7 + 8 + den.homes.aarch64-darwin.bob = { 9 + userName = "robert"; 10 + aspect = "developer"; 11 + }; 12 + 13 + # Example: custom home-manager instantiate for passing extraSpecialArgs. 14 + den.homes.x86_64-linux.luke = 15 + let 16 + osConfig = inputs.self.nixosConfigurations.rockhopper.config; 17 + in 18 + { 19 + # Example: luke standalone-homemanager needs access to rockhopper osConfig. 20 + instantiate = 21 + { pkgs, modules }: 22 + inputs.home-manager.lib.homeManagerConfiguration { 23 + inherit pkgs modules; 24 + extraSpecialArgs.osConfig = osConfig; 25 + }; 26 + 27 + # Example: custom attribute instead of specialArgs 28 + programToDependOn = "vim"; 29 + }; 30 + 31 + }
+13
templates/ci/modules/host-configures-users.nix
··· 1 + { 2 + 3 + # Example: host provides static config to all its users hm. 4 + den.aspects.rockhopper.homeManager.programs.direnv.enable = true; 5 + 6 + perSystem = 7 + { checkCond, alice-at-rockhopper, ... }: 8 + { 9 + checks.host-contributes-to-user = checkCond "rockhopper contributes to all its users" ( 10 + alice-at-rockhopper.programs.direnv.enable 11 + ); 12 + }; 13 + }
+48
templates/ci/modules/host-user-conditional-hm.nix
··· 1 + { lib, ... }: 2 + let 3 + # Example: configuration that depends on both host and user. provides only to HM. 4 + program-conditional = 5 + program: 6 + { 7 + user, 8 + host, 9 + ... 10 + }: 11 + if user.userName == "alice" && !lib.hasSuffix "darwin" host.system then 12 + { 13 + homeManager.programs.${program}.enable = true; 14 + } 15 + else 16 + { }; 17 + in 18 + { 19 + 20 + # Example: host parametric includes. conditional user configuration. 21 + den.aspects.rockhopper.includes = [ (program-conditional "git") ]; 22 + 23 + # Example: user parametric includes 24 + den.aspects.alice.includes = [ (program-conditional "mpv") ]; 25 + 26 + perSystem = 27 + { 28 + checkCond, 29 + alice-at-rockhopper, 30 + alice-at-honeycrisp, 31 + ... 32 + }: 33 + { 34 + 35 + checks.alice-hm-git-enabled-on = checkCond "home-managed git for alice at rockhopper" ( 36 + alice-at-rockhopper.programs.git.enable 37 + ); 38 + checks.alice-hm-git-enabled-off = checkCond "home-managed git for alice at honeycrisp" ( 39 + !alice-at-honeycrisp.programs.git.enable 40 + ); 41 + 42 + checks.alice-hm-mpv-enabled-rockhopper = checkCond "home-managed mpv for alice at rockhopper" ( 43 + alice-at-rockhopper.programs.mpv.enable 44 + ); 45 + 46 + }; 47 + 48 + }
+50
templates/ci/modules/hosts.nix
··· 1 + # This is a fully working example configuration. 2 + # Feel free to remove it, adapt or split into several modules. 3 + # See documentation at <den>/nix/types.nix 4 + { inputs, ... }: 5 + { 6 + den.hosts.aarch64-darwin.honeycrisp.users.alice = { }; 7 + 8 + den.hosts.aarch64-linux.emperor.users.alice = { }; 9 + 10 + den.hosts.x86_64-linux = { 11 + rockhopper = { 12 + description = "rockhopper is a kind of penguin"; 13 + users.alice = { }; 14 + }; 15 + 16 + adelie = { 17 + description = "wsl on windows"; 18 + users.will = { }; 19 + intoAttr = "wslConfigurations"; 20 + # custom nixpkgs channel. 21 + instantiate = inputs.nixpkgs-stable.lib.nixosSystem; 22 + # custom attribute (see home-manager.nix) 23 + hm-module = inputs.home-manager-stable.nixosModules.home-manager; 24 + # custom attribute (see primary-user.nix) 25 + wsl = { }; 26 + }; 27 + }; 28 + 29 + # move these inputs to any module you want. 30 + # they are here for all our examples to work on CI. 31 + flake-file.inputs = { 32 + 33 + # these stable inputs are for wsl 34 + nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 35 + home-manager-stable.url = "github:nix-community/home-manager/release-25.05"; 36 + home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable"; 37 + 38 + nixos-wsl = { 39 + url = "github:nix-community/nixos-wsl"; 40 + inputs.nixpkgs.follows = "nixpkgs-stable"; 41 + inputs.flake-compat.follows = ""; 42 + }; 43 + 44 + darwin = { 45 + url = "github:nix-darwin/nix-darwin"; 46 + inputs.nixpkgs.follows = "nixpkgs"; 47 + }; 48 + }; 49 + 50 + }
+81
templates/ci/modules/namespace.nix
··· 1 + { 2 + inputs, 3 + den, 4 + lib, 5 + eg, 6 + # deadnix: skip 7 + __findFile ? __findFile, 8 + ... 9 + }: 10 + let 11 + # module for testing inclusion of namespaces 12 + simsModule.nixos.options.sims = lib.mkOption { 13 + type = lib.types.listOf lib.types.str; 14 + }; 15 + in 16 + { 17 + # enable <angle/bracket> syntax for finding aspects. 18 + _module.args.__findFile = den.lib.__findFile; 19 + 20 + imports = [ 21 + # create a local namespace and output at flake.denful.eg 22 + (inputs.den.namespace "eg" true) 23 + 24 + # you can also mount a namespace from many input sources. 25 + # the second argument becomes an array of inputs. 26 + ( 27 + let 28 + # NOTE: here we simulate inputA and inputB are flakes. 29 + inputA.denful.sim.ul._.a._.tion.nixos.sims = [ "inputA simulation" ]; 30 + inputB.denful.sim.ul._.a._.tion.nixos.sims = [ "inputB simulation" ]; 31 + exposeToFlake = true; 32 + in 33 + inputs.den.namespace "sim" [ 34 + inputA 35 + inputB 36 + exposeToFlake 37 + ] 38 + ) 39 + ]; 40 + 41 + # define nested aspects in local namespace 42 + eg.foo.provides.bar.provides.baz = { 43 + nixos.sims = [ "local namespace" ]; 44 + }; 45 + 46 + # augment aspects on a mounted namespace 47 + sim.ul._.a._.tion.nixos.sims = [ "local simulation" ]; 48 + 49 + den.aspects.rockhopper.includes = [ 50 + simsModule 51 + <eg/foo/bar/baz> 52 + <sim/ul/a/tion> 53 + ]; 54 + 55 + perSystem = 56 + { checkCond, rockhopper, ... }: 57 + { 58 + checks.namespace-eg-flake-output = checkCond "namespace enabled as flake output" ( 59 + eg == den.ful.eg && eg == <eg> && eg == inputs.self.denful.eg 60 + ); 61 + 62 + checks.namespace-eg-provides-accessible = checkCond "exact same value" ( 63 + eg.foo._.bar._.baz == <eg/foo/bar/baz> 64 + && eg.foo._.bar._.baz == inputs.self.denful.eg.foo._.bar._.baz 65 + ); 66 + 67 + checks.namespace-sim-merged = checkCond "merges from all sources" ( 68 + let 69 + expected = [ 70 + "inputA simulation" 71 + "inputB simulation" 72 + "local namespace" 73 + "local simulation" 74 + ]; 75 + actual = lib.sort (a: b: a < b) rockhopper.config.sims; 76 + in 77 + expected == actual 78 + ); 79 + 80 + }; 81 + }
+41
templates/ci/modules/one-os-package-per-user.nix
··· 1 + let 2 + 3 + # Example: adds hello into each user. provides only to OS. 4 + hello-package-for-user = 5 + { 6 + user, 7 + host, 8 + ... 9 + }: 10 + { 11 + ${host.class} = 12 + { pkgs, ... }: 13 + { 14 + users.users.${user.userName}.packages = [ pkgs.hello ]; 15 + }; 16 + }; 17 + 18 + in 19 + { 20 + 21 + den.default.includes = [ hello-package-for-user ]; 22 + 23 + perSystem = 24 + { 25 + checkCond, 26 + rockhopper, 27 + lib, 28 + ... 29 + }: 30 + { 31 + checks.alice-hello-enabled-by-default = checkCond "added hello at user packages" ( 32 + let 33 + progs = rockhopper.config.users.users.alice.packages; 34 + expr = map lib.getName progs; 35 + expected = [ "hello" ]; 36 + in 37 + expr == expected 38 + ); 39 + }; 40 + 41 + }
+145
templates/ci/modules/parametric-with-owned.nix
··· 1 + { 2 + den, 3 + lib, 4 + ... 5 + }: 6 + let 7 + # a test module to check context was forwarded 8 + fwdModule.options.fwd = { 9 + a = strOpt; 10 + b = strOpt; 11 + c = strOpt; 12 + d = strOpt; 13 + e = strOpt; 14 + f = strOpt; 15 + # unlike strings, pkgs cannot be duplicated/merged, we use this to 16 + # ensure no-dups are created from parametric owned modules. 17 + pkg = pkgOpt; 18 + pkg2 = pkgOpt; 19 + pkg3 = pkgOpt; 20 + }; 21 + strOpt = lib.mkOption { type = lib.types.str; }; 22 + pkgOpt = lib.mkOption { type = lib.types.package; }; 23 + 24 + inherit (den.lib) parametric; 25 + in 26 + { 27 + den.aspects.rockhopper.includes = [ 28 + { nixos.imports = [ fwdModule ]; } 29 + { homeManager.imports = [ fwdModule ]; } 30 + den.aspects.fwd._.first 31 + ]; 32 + den.aspects.rockhopper.nixos.fwd.c = "host owned C"; 33 + den.aspects.rockhopper.homeManager.fwd.a = "host home-managed A"; 34 + 35 + # this aspect will take any context and also forward it 36 + # into any includes function that can take same context. 37 + den.aspects.fwd._.first = parametric { 38 + nixos = 39 + { pkgs, ... }: 40 + { 41 + fwd.a = "First owned A"; 42 + fwd.pkg = pkgs.hello; 43 + }; 44 + homeManager = 45 + { pkgs, ... }: 46 + { 47 + fwd.pkg = pkgs.vim; 48 + }; 49 + includes = [ 50 + den.aspects.fwd._.second 51 + { nixos.fwd.d = "First static includes D"; } 52 + den.aspects.fwd._.never 53 + den.aspects.fwd._.fourth 54 + ]; 55 + }; 56 + 57 + # Note that second has named arguments, while first does not. 58 + # the first aspect forwards whatever context it receives. 59 + den.aspects.fwd._.second = 60 + { host, ... }: 61 + parametric.fixedTo { third = "Impact"; } { 62 + includes = [ den.aspects.fwd._.third ]; 63 + nixos = 64 + { pkgs, ... }: 65 + { 66 + fwd.b = "Second owned B for ${host.name}"; 67 + fwd.pkg2 = pkgs.bat; 68 + }; 69 + homeManager = 70 + { pkgs, ... }: 71 + { 72 + fwd.pkg2 = pkgs.helix; 73 + }; 74 + }; 75 + 76 + den.aspects.fwd._.third = 77 + { third, ... }: 78 + { 79 + nixos.fwd.e = "Third ${third}"; 80 + }; 81 + 82 + den.aspects.fwd._.fourth = parametric.expands { planet = "Earth"; } { 83 + includes = [ den.aspects.fwd._.fifth ]; 84 + nixos = 85 + { pkgs, ... }: 86 + { 87 + fwd.pkg3 = pkgs.emacs-nox; 88 + }; 89 + homeManager = 90 + { pkgs, ... }: 91 + { 92 + fwd.pkg3 = pkgs.emacs-nox; 93 + }; 94 + }; 95 + 96 + den.aspects.fwd._.fifth = 97 + { host, planet, ... }: 98 + { 99 + nixos.fwd.f = "Fifth ${planet} ${host.name}"; 100 + }; 101 + 102 + den.aspects.fwd._.never = 103 + { never-matches }: 104 + { 105 + nixos.fwd.a = "Imposibru! should never be included ${never-matches}"; 106 + }; 107 + 108 + perSystem = 109 + { 110 + checkCond, 111 + rockhopper, 112 + alice-at-rockhopper, 113 + ... 114 + }: 115 + { 116 + checks.parametric-fwd-a = checkCond "fwd-a" (rockhopper.config.fwd.a == "First owned A"); 117 + checks.parametric-fwd-b = checkCond "fwd-b" ( 118 + rockhopper.config.fwd.b == "Second owned B for rockhopper" 119 + ); 120 + checks.parametric-fwd-c = checkCond "fwd-c" (rockhopper.config.fwd.c == "host owned C"); 121 + checks.parametric-fwd-d = checkCond "fwd-d" (rockhopper.config.fwd.d == "First static includes D"); 122 + checks.parametric-fwd-e = checkCond "fwd-e" (rockhopper.config.fwd.e == "Third Impact"); 123 + checks.parametric-fwd-f = checkCond "fwd-f" (rockhopper.config.fwd.f == "Fifth Earth rockhopper"); 124 + 125 + checks.parametric-fwd-pkg = checkCond "fwd-pkg" (lib.getName rockhopper.config.fwd.pkg == "hello"); 126 + checks.parametric-fwd-pkg2 = checkCond "fwd-pkg2" (lib.getName rockhopper.config.fwd.pkg2 == "bat"); 127 + checks.parametric-fwd-pkg3 = checkCond "fwd-pkg3" ( 128 + lib.getName rockhopper.config.fwd.pkg3 == "emacs-nox" 129 + ); 130 + 131 + checks.parametric-fwd-hm-a = checkCond "fwd-hm-a" ( 132 + alice-at-rockhopper.fwd.a == "host home-managed A" 133 + ); 134 + checks.parametric-fwd-hm-pkg = checkCond "fwd-hm-pkg" ( 135 + lib.getName alice-at-rockhopper.fwd.pkg == "vim" 136 + ); 137 + checks.parametric-fwd-hm-pkg2 = checkCond "fwd-hm-pkg2" ( 138 + lib.getName alice-at-rockhopper.fwd.pkg2 == "helix" 139 + ); 140 + checks.parametric-fwd-hm-pkg3 = checkCond "fwd-hm-pkg3" ( 141 + lib.getName alice-at-rockhopper.fwd.pkg3 == "emacs-nox" 142 + ); 143 + }; 144 + 145 + }
+29
templates/ci/modules/primary-user.nix
··· 1 + { den, ... }: 2 + { 3 + den.aspects.alice.includes = [ 4 + # alice is always admin in all its hosts 5 + den._.primary-user 6 + ]; 7 + 8 + den.aspects.will.includes = [ 9 + # will is primary user in WSL NixOS. 10 + den._.primary-user 11 + ]; 12 + 13 + perSystem = 14 + { 15 + checkCond, 16 + honeycrisp, 17 + adelie, 18 + ... 19 + }: 20 + { 21 + checks.alice-primary-on-macos = checkCond "den._.primary-user sets macos primary" ( 22 + honeycrisp.config.system.primaryUser == "alice" 23 + ); 24 + 25 + checks.will-is-wsl-default = checkCond "wsl.defaultUser defined" ( 26 + adelie.config.wsl.defaultUser == "will" 27 + ); 28 + }; 29 + }
+29
templates/ci/modules/set-hostname.nix
··· 1 + { 2 + den.default.includes = 3 + let 4 + # Example: parametric host aspect to automatically set hostName on any host. 5 + set-host-name = 6 + { host, ... }: 7 + { 8 + ${host.class}.networking.hostName = host.name; 9 + }; 10 + in 11 + [ set-host-name ]; 12 + 13 + perSystem = 14 + { 15 + checkCond, 16 + rockhopper, 17 + honeycrisp, 18 + ... 19 + }: 20 + { 21 + checks.rockhopper-hostname = checkCond "den.default.host.includes sets hostName" ( 22 + rockhopper.config.networking.hostName == "rockhopper" 23 + ); 24 + 25 + checks.honeycrisp-hostname = checkCond "den.default.host.includes sets hostName" ( 26 + honeycrisp.config.networking.hostName == "honeycrisp" 27 + ); 28 + }; 29 + }
+53
templates/ci/modules/shared-parametric-aspects.nix
··· 1 + { 2 + inputs, 3 + lib, 4 + shared, 5 + ... 6 + }: 7 + let 8 + dataModule.nixos.options.data = lib.mkOption { 9 + type = lib.types.listOf lib.types.str; 10 + }; 11 + mkInputFlake = name: { 12 + denful.shared.gaming._.retro._.sega = { 13 + nixos.data = [ "${name}-sega-static" ]; 14 + }; 15 + }; 16 + inputFoo = mkInputFlake "foo"; 17 + inputBar = mkInputFlake "bar"; 18 + in 19 + { 20 + imports = [ 21 + (inputs.den.namespace "shared" [ 22 + true 23 + inputFoo 24 + inputBar 25 + ]) 26 + ]; 27 + 28 + shared.gaming._.retro._.sega.nixos.data = [ "local-sega-static" ]; 29 + 30 + den.aspects.rockhopper.includes = [ 31 + dataModule 32 + shared.gaming._.retro._.sega 33 + ]; 34 + 35 + perSystem = 36 + { checkCond, rockhopper, ... }: 37 + let 38 + vals = lib.sort (a: b: a < b) rockhopper.config.data; 39 + in 40 + { 41 + checks.shared-parametric-all-merged = checkCond "all parametric merged" ( 42 + vals == [ 43 + "bar-sega-static" 44 + "foo-sega-static" 45 + "local-sega-static" 46 + ] 47 + ); 48 + 49 + checks.shared-flake-output-matches = checkCond "flake output matches" ( 50 + shared.gaming._.retro._.sega == inputs.self.denful.shared.gaming._.retro._.sega 51 + ); 52 + }; 53 + }
+70
templates/ci/modules/special-args.nix
··· 1 + { den, withSystem, ... }: 2 + let 3 + testModule = 4 + { 5 + inputs', 6 + lib, 7 + self', 8 + ... 9 + }: 10 + { 11 + options.specialArgsTest = { 12 + test-package = lib.mkOption { type = lib.types.package; }; 13 + neovim-package = lib.mkOption { type = lib.types.package; }; 14 + }; 15 + 16 + config.specialArgsTest = { 17 + test-package = self'.packages.hello; 18 + neovim-package = inputs'.neovim-nightly-overlay.packages.neovim; 19 + }; 20 + }; 21 + in 22 + { 23 + flake-file.inputs.neovim-nightly-overlay = { 24 + url = "github:nix-community/neovim-nightly-overlay"; 25 + inputs.nixpkgs.follows = "nixpkgs"; 26 + inputs.flake-parts.follows = "flake-parts"; 27 + }; 28 + 29 + den.default.includes = [ 30 + den._.self' 31 + den._.inputs' 32 + ]; 33 + 34 + den.aspects.rockhopper.nixos.imports = [ testModule ]; 35 + den.aspects.cam.homeManager.imports = [ testModule ]; 36 + 37 + flake.checks.x86_64-linux = withSystem "x86_64-linux" ( 38 + { 39 + checkCond, 40 + rockhopper, 41 + cam, 42 + self', 43 + inputs', 44 + ... 45 + }: 46 + { 47 + special-args-self-nixos = checkCond "self' provides same package to nixos" ( 48 + rockhopper.config.specialArgsTest.test-package == self'.packages.hello 49 + ); 50 + 51 + special-args-inputs-nixos = checkCond "inputs' provides same package to nixos" ( 52 + rockhopper.config.specialArgsTest.neovim-package == inputs'.neovim-nightly-overlay.packages.neovim 53 + ); 54 + 55 + special-args-self-hm = checkCond "self' provides same package to home-manager" ( 56 + cam.config.specialArgsTest.test-package == self'.packages.hello 57 + ); 58 + 59 + special-args-inputs-hm = checkCond "inputs' provides same package to home-manager" ( 60 + cam.config.specialArgsTest.neovim-package == inputs'.neovim-nightly-overlay.packages.neovim 61 + ); 62 + } 63 + ); 64 + 65 + perSystem = 66 + { pkgs, ... }: 67 + { 68 + packages.hello = pkgs.hello; 69 + }; 70 + }
+31
templates/ci/modules/standalone-hm-special-args-osconfig.nix
··· 1 + let 2 + 3 + # Example: luke standalone home-manager has access to rockhopper osConfig specialArg. 4 + os-conditional-hm = 5 + { home, ... }: 6 + { 7 + # access osConfig, wired via extraSpecialArgs in homes.nix. 8 + homeManager = 9 + { osConfig, ... }: 10 + { 11 + programs.bat.enable = osConfig.programs.${home.programToDependOn}.enable; 12 + }; 13 + }; 14 + in 15 + { 16 + 17 + # Example: standalone-hm config depends on osConfig (non-recursive) 18 + # NOTE: this will only work for standalone hm, and not for hosted hm 19 + # since a hosted hm configuration cannot depend on the os configuration. 20 + den.aspects.luke.includes = [ 21 + os-conditional-hm 22 + ]; 23 + 24 + perSystem = 25 + { checkCond, luke, ... }: 26 + { 27 + checks.luke-hm-depends-on-osConfig = checkCond "standalone hm can depend on osConfig" ( 28 + luke.config.programs.bat.enable 29 + ); 30 + }; 31 + }
+50
templates/ci/modules/top-level-parametric.nix
··· 1 + # it is possible for top-level aspects directly under 2 + # den.aspects to take a context argument. 3 + { den, lib, ... }: 4 + let 5 + # A module to test that toplevel had context. 6 + topLevel = name: { 7 + config.tops = name; 8 + options.tops = lib.mkOption { type = lib.types.str; }; 9 + }; 10 + in 11 + { 12 + 13 + den.aspects.toplevel-user = 14 + { user, ... }: 15 + { 16 + nixos.imports = [ (topLevel user.name) ]; 17 + }; 18 + 19 + den.aspects.toplevel-host = 20 + { host, ... }: 21 + { 22 + homeManager.imports = [ (topLevel host.name) ]; 23 + }; 24 + 25 + den.aspects.rockhopper.includes = [ 26 + den.aspects.toplevel-host 27 + ]; 28 + 29 + den.aspects.alice.includes = [ 30 + den.aspects.toplevel-user 31 + ]; 32 + 33 + perSystem = 34 + { 35 + checkCond, 36 + alice-at-rockhopper, 37 + rockhopper, 38 + ... 39 + }: 40 + { 41 + checks.alice-toplevel-user = checkCond "alice toplevel param aspect" ( 42 + rockhopper.config.tops == "alice" 43 + ); 44 + 45 + checks.alice-toplevel-host = checkCond "alice toplevel param aspect" ( 46 + alice-at-rockhopper.tops == "rockhopper" 47 + ); 48 + }; 49 + 50 + }
+25
templates/ci/modules/unfree.nix
··· 1 + { den, ... }: 2 + 3 + let 4 + codeAspect = { 5 + includes = [ (den._.unfree [ "vscode" ]) ]; 6 + homeManager.programs.vscode.enable = true; 7 + }; 8 + discordAspect = { 9 + includes = [ 10 + (den._.unfree [ "discord" ]) 11 + ]; 12 + homeManager = 13 + { pkgs, ... }: 14 + { 15 + home.packages = [ pkgs.discord ]; 16 + }; 17 + }; 18 + in 19 + { 20 + # cam uses unfree vscode and discord loaded from different aspects. 21 + den.aspects.cam.includes = [ 22 + codeAspect 23 + discordAspect 24 + ]; 25 + }
+12
templates/ci/modules/user-configures-hosts.nix
··· 1 + { 2 + # Example: user provides static config to all its nixos hosts. 3 + den.aspects.alice.nixos.users.users.alice.description = "Alice Q. User"; 4 + 5 + perSystem = 6 + { checkCond, rockhopper, ... }: 7 + { 8 + checks.user-contributes-to-host = checkCond "alice.nixos sets on rockhopper host" ( 9 + rockhopper.config.users.users.alice.description == "Alice Q. User" 10 + ); 11 + }; 12 + }
+12
templates/ci/modules/user-home-defaults.nix
··· 1 + { 2 + # globally enable fish on all homes ever. 3 + den.default.homeManager.programs.fish.enable = true; 4 + 5 + perSystem = 6 + { checkCond, alice-at-rockhopper, ... }: 7 + { 8 + checks.alice-hm-fish-enabled-by-default = checkCond "home-managed fish for alice" ( 9 + alice-at-rockhopper.programs.fish.enable 10 + ); 11 + }; 12 + }
+38
templates/ci/modules/user-host-conditional-os.nix
··· 1 + { lib, ... }: 2 + let 3 + 4 + # Example: configuration that depends on both host and user. provides anytime { user, host } is in context. 5 + user-to-host-conditional = 6 + { user, host, ... }: 7 + if user.userName == "alice" && !lib.hasSuffix "darwin" host.system then 8 + { 9 + nixos.programs.tmux.enable = true; 10 + } 11 + else 12 + { }; 13 + in 14 + { 15 + 16 + # Example: user provides parametric host configuration. 17 + den.aspects.alice.includes = [ 18 + user-to-host-conditional 19 + ]; 20 + 21 + perSystem = 22 + { 23 + checkCond, 24 + rockhopper, 25 + honeycrisp, 26 + ... 27 + }: 28 + { 29 + checks.alice-os-tmux-enabled-on = checkCond "os tmux for hosts having alice" ( 30 + rockhopper.config.programs.tmux.enable 31 + ); 32 + checks.alice-os-tmux-enabled-off = checkCond "os tmux for hosts having alice" ( 33 + !honeycrisp.config.programs.tmux.enable 34 + ); 35 + 36 + }; 37 + 38 + }
+17
templates/ci/modules/user-shell.nix
··· 1 + { den, lib, ... }: 2 + { 3 + 4 + den.aspects.will.includes = [ 5 + # will has always loved red snappers 6 + (den._.user-shell "fish") 7 + ]; 8 + 9 + perSystem = 10 + { checkCond, adelie, ... }: 11 + { 12 + checks.will-always-love-you = checkCond "red-snapper fish is default shell" ( 13 + "fish" == lib.getName adelie.config.users.users.will.shell 14 + ); 15 + }; 16 + 17 + }
+13
templates/ci/modules/user-specific-hm-config.nix
··· 1 + { 2 + # Example: enable helix for alice on all its home-managed hosts. 3 + den.aspects.alice.homeManager.programs.helix.enable = true; 4 + 5 + perSystem = 6 + { checkCond, alice-at-rockhopper, ... }: 7 + { 8 + checks.alice-hm-helix-enabled-by-user = checkCond "home-managed helix for alice" ( 9 + alice-at-rockhopper.programs.helix.enable 10 + ); 11 + }; 12 + 13 + }
+16
templates/ci/modules/vm-bootable.nix
··· 1 + let 2 + # Example: A static aspect for vm installers. 3 + vm-bootable = { 4 + nixos = 5 + { modulesPath, ... }: 6 + { 7 + imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix") ]; 8 + }; 9 + }; 10 + in 11 + { 12 + den.default.includes = [ 13 + # Example: static aspect 14 + vm-bootable 15 + ]; 16 + }
+6
templates/ci/non-dendritic/hosts/honeycrisp/_darwin/something.nix
··· 1 + # this is a non-dendritic darwin class module file. 2 + # automatically discovered by `den.import-tree` as enabled in auto-imports.nix 3 + { ... }: 4 + { 5 + # see nix-darwin options. 6 + }
+14
templates/ci/non-dendritic/hosts/rockhopper/_nixos/hardware-auto-generated.nix
··· 1 + # this is a non-dendritic nix class module file. 2 + # automatically discovered by `den.import-tree` as enabled in auto-imports.nix 3 + # 4 + # USER TODO: Remove this file. 5 + # suppose this file was auto-generated by nixos-generate-config or some other hardware tooling. 6 + { lib, ... }: 7 + { 8 + # used in CI to test this file was actually imported. 9 + options.auto-imported = lib.mkOption { 10 + readOnly = true; 11 + type = lib.types.bool; 12 + default = true; 13 + }; 14 + }
+12
templates/ci/provider/flake.nix
··· 1 + { 2 + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 3 + 4 + inputs = { 5 + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 + flake-parts.url = "github:hercules-ci/flake-parts"; 7 + flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 8 + flake-aspects.url = "github:vic/flake-aspects"; 9 + import-tree.url = "github:vic/import-tree"; 10 + den.url = "github:vic/den"; 11 + }; 12 + }
+28
templates/ci/provider/modules/den.nix
··· 1 + { inputs, ... }: 2 + { 3 + systems = [ 4 + "x86_64-linux" 5 + "aarch64-darwin" 6 + ]; 7 + 8 + imports = [ 9 + inputs.den.flakeModule 10 + (inputs.den.namespace "provider" true) 11 + ]; 12 + 13 + provider.tools._.dev._.editors = { 14 + description = "Editor configurations from provider flake"; 15 + nixos.environment.variables.PROVIDER_EDITOR = "vim"; 16 + homeManager = 17 + { pkgs, ... }: 18 + { 19 + home.packages = [ pkgs.vim ]; 20 + }; 21 + }; 22 + 23 + provider.tools._.dev._.shells = { 24 + description = "Shell configurations from provider flake"; 25 + nixos.environment.variables.PROVIDER_SHELL = "fish"; 26 + nixos.environment.systemPackages = [ ]; 27 + }; 28 + }
+1 -17
templates/default/README.md
··· 16 16 nix flake check 17 17 ``` 18 18 19 - - Read [modules/den.nix](modules/den.nix) where hosts and homes definitions are for this example. 20 - 21 - - Read [modules/namespace.nix](modules/namespace.nix) where a new `eg` (an example) aspects namespace is created. 22 - 23 - - Read [modules/aspects/igloo.nix](modules/aspects/igloo.nix) where the `igloo` host is configured. 24 - 25 - - Read [modules/aspects/alice.nix](modules/aspects/alice.nix) where the `alice` user is configured. 26 - 27 - - Run the VM. 28 - 29 - ```console 30 - nix run .#vm 31 - ``` 32 - 33 - - Edit and run VM loop. 34 - 35 - Feel free to add more aspects, organize things to your liking. 19 + - Edit [modules/hosts.nix](modules/hosts.nix)
-21
templates/default/flake.lock
··· 1 1 { 2 2 "nodes": { 3 - "darwin": { 4 - "inputs": { 5 - "nixpkgs": [ 6 - "nixpkgs" 7 - ] 8 - }, 9 - "locked": { 10 - "lastModified": 1762627886, 11 - "narHash": "sha256-/QLk1bzmbcqJt9sU43+y/3tHtXhAy0l8Ck0MoO2+evQ=", 12 - "owner": "nix-darwin", 13 - "repo": "nix-darwin", 14 - "rev": "5125a3cd414dc98bbe2c528227aa6b62ee61f733", 15 - "type": "github" 16 - }, 17 - "original": { 18 - "owner": "nix-darwin", 19 - "repo": "nix-darwin", 20 - "type": "github" 21 - } 22 - }, 23 3 "den": { 24 4 "locked": { 25 5 "lastModified": 1763707606, ··· 138 118 }, 139 119 "root": { 140 120 "inputs": { 141 - "darwin": "darwin", 142 121 "den": "den", 143 122 "flake-aspects": "flake-aspects", 144 123 "flake-file": "flake-file",
-4
templates/default/flake.nix
··· 5 5 outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 6 6 7 7 inputs = { 8 - darwin = { 9 - inputs.nixpkgs.follows = "nixpkgs"; 10 - url = "github:nix-darwin/nix-darwin"; 11 - }; 12 8 den.url = "github:vic/den"; 13 9 flake-aspects.url = "github:vic/flake-aspects"; 14 10 flake-file.url = "github:vic/flake-file";
-71
templates/default/modules/aspects/alice.nix
··· 1 - { den, eg, ... }: 2 - { 3 - den.aspects.alice = { 4 - 5 - # Alice can include other aspects. 6 - # For small, private one-shot aspects, use let-bindings like here. 7 - # for more complex or re-usable ones, define on their own modules, 8 - # as part of any aspect-subtree. 9 - includes = 10 - let 11 - # hack for nixf linter to keep findFile :/ 12 - unused = den.lib.take.unused __findFile; 13 - __findFile = unused den.lib.__findFile; 14 - 15 - customEmacs.homeManager = 16 - { pkgs, ... }: 17 - { 18 - programs.emacs.enable = true; 19 - programs.emacs.package = pkgs.emacs30-nox; 20 - }; 21 - in 22 - [ 23 - # from local bindings. 24 - customEmacs 25 - # from the aspect tree, cooper example is defined bellow 26 - den.aspects.cooper 27 - den.aspects.setHost 28 - # from the `eg` namespace. 29 - eg.autologin 30 - # den included batteries that provide common configs. 31 - <den/primary-user> # alice is admin always. 32 - (<den/user-shell> "fish") # default user shell 33 - ]; 34 - 35 - # Alice configures NixOS hosts it lives on. 36 - nixos = 37 - { pkgs, ... }: 38 - { 39 - users.users.alice.packages = [ pkgs.vim ]; 40 - }; 41 - 42 - # Alice home-manager. 43 - homeManager = 44 - { pkgs, ... }: 45 - { 46 - home.packages = [ pkgs.htop ]; 47 - }; 48 - 49 - # <user>.provides.<host>, via eg/routes.nix 50 - provides.igloo = 51 - { host, ... }: 52 - { 53 - nixos.programs.nh.enable = host.name == "igloo"; 54 - }; 55 - }; 56 - 57 - # This is a context-aware aspect, that emits configurations 58 - # **anytime** at least the `user` data is in context. 59 - # read more at https://vic.github.io/den/context-aware.html 60 - den.aspects.cooper = 61 - { user, ... }: 62 - { 63 - nixos.users.users.${user.userName}.description = "Alice Cooper"; 64 - }; 65 - 66 - den.aspects.setHost = 67 - { host, ... }: 68 - { 69 - networking.hostName = host.hostName; 70 - }; 71 - }
-47
templates/default/modules/aspects/defaults.nix
··· 1 - { 2 - config, 3 - # deadnix: skip # enable <den/brackets> syntax for demo. 4 - __findFile ? __findFile, 5 - den, 6 - ... 7 - }: 8 - { 9 - # Lets also configure some defaults using aspects. 10 - # These are global static settings. 11 - den.default = { 12 - darwin.system.stateVersion = 6; 13 - nixos.system.stateVersion = "25.05"; 14 - homeManager.home.stateVersion = "25.05"; 15 - }; 16 - 17 - # These are functions that produce configs 18 - den.default.includes = [ 19 - # ${user}.provides.${host} and ${host}.provides.${user} 20 - <eg/routes> 21 - 22 - # Enable home-manager on all hosts. 23 - <den/home-manager> 24 - 25 - # Automatically create the user on host. 26 - <den/define-user> 27 - 28 - # Disable booting when running on CI on all NixOS hosts. 29 - (if config ? _module.args.CI then <eg/ci-no-boot> else { }) 30 - 31 - # NOTE: be cautious when adding fully parametric functions to defaults. 32 - # defaults are included on EVERY host/user/home, and IF you are not careful 33 - # you could be duplicating config values. For example: 34 - # 35 - # # This will append 42 into foo option for the {host} and for EVERY {host,user} 36 - # ({ host, ... }: { nixos.foo = [ 42 ]; }) # DO-NOT-DO-THIS. 37 - # 38 - # # Instead try to be explicit if a function is intended for ONLY { host }. 39 - (den.lib.take.exactly ( 40 - { OS, host }: 41 - den.lib.take.unused OS { 42 - nixos.networking.hostName = host.hostName; 43 - } 44 - )) 45 - 46 - ]; 47 - }
-15
templates/default/modules/aspects/eg/autologin.nix
··· 1 - { 2 - # autologin is context-aware, parametric aspect. 3 - # it applies only if the context has at least { user } 4 - # meaning that has access to user data 5 - eg.autologin = 6 - { user, ... }: 7 - { 8 - nixos = 9 - { config, lib, ... }: 10 - lib.mkIf config.services.displayManager.enable { 11 - services.displayManager.autoLogin.enable = true; 12 - services.displayManager.autoLogin.user = user.userName; 13 - }; 14 - }; 15 - }
-9
templates/default/modules/aspects/eg/ci-no-boot.nix
··· 1 - { 2 - eg.ci-no-boot = { 3 - description = "Disables booting during CI"; 4 - nixos = { 5 - boot.loader.grub.enable = false; 6 - fileSystems."/".device = "/dev/null"; 7 - }; 8 - }; 9 - }
-36
templates/default/modules/aspects/eg/routes.nix
··· 1 - # This example implements an aspect "routing" pattern. 2 - # 3 - # Unlike `den.default` which is `parametric.atLeast` 4 - # we use `parametric.fixedTo` here, which help us 5 - # propagate an already computed context to all includes. 6 - # 7 - # This aspect, when installed in a `parametric.atLeast` 8 - # will just forward the same context. 9 - # The `mutual` helper returns an static configuration which 10 - # is ignored by parametric aspects, thus allowing 11 - # non-existing aspects to be just ignored. 12 - # 13 - # Be sure to read: https://vic.github.io/den/dependencies.html 14 - # See usage at: defaults.nix, alice.nix, igloo.nix 15 - # 16 - { den, ... }: 17 - { 18 - # Usage: `den.default.includes [ eg.routes ]` 19 - eg.routes = 20 - let 21 - inherit (den.lib) parametric; 22 - 23 - # eg, `<user>._.<host>` and `<host>._.<user>` 24 - mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { }; 25 - 26 - routes = 27 - { host, user, ... }@ctx: 28 - parametric.fixedTo ctx { 29 - includes = [ 30 - (mutual user host) 31 - (mutual host user) 32 - ]; 33 - }; 34 - in 35 - routes; 36 - }
-16
templates/default/modules/aspects/eg/vm-bootable.nix
··· 1 - let 2 - installer = variant: { 3 - nixos = 4 - { modulesPath, ... }: 5 - { 6 - imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-${variant}.nix") ]; 7 - }; 8 - }; 9 - in 10 - { 11 - # make USB/VM installers. 12 - eg.vm-bootable.provides = { 13 - tui = installer "minimal"; 14 - gui = installer "graphical-base"; 15 - }; 16 - }
-15
templates/default/modules/aspects/eg/vm.nix
··· 1 - { eg, ... }: 2 - { 3 - eg.vm.provides = { 4 - gui.includes = [ 5 - eg.vm 6 - eg.vm-bootable._.gui 7 - eg.xfce-desktop 8 - ]; 9 - 10 - tui.includes = [ 11 - eg.vm 12 - eg.vm-bootable._.tui 13 - ]; 14 - }; 15 - }
-19
templates/default/modules/aspects/eg/xfce-desktop.nix
··· 1 - { 2 - eg.xfce-desktop.nixos = 3 - { lib, ... }: 4 - { 5 - # https://gist.github.com/nat-418/1101881371c9a7b419ba5f944a7118b0 6 - services.xserver = { 7 - enable = true; 8 - desktopManager = { 9 - xterm.enable = false; 10 - xfce.enable = true; 11 - }; 12 - }; 13 - 14 - services.displayManager = { 15 - defaultSession = lib.mkDefault "xfce"; 16 - enable = true; 17 - }; 18 - }; 19 - }
-20
templates/default/modules/aspects/igloo.nix
··· 1 - { 2 - den.aspects.igloo = { 3 - # igloo host provides some home-manager defaults to its users. 4 - homeManager.programs.direnv.enable = true; 5 - 6 - # NixOS configuration for igloo. 7 - nixos = 8 - { pkgs, ... }: 9 - { 10 - environment.systemPackages = [ pkgs.hello ]; 11 - }; 12 - 13 - # <host>.provides.<user>, via eg/routes.nix 14 - provides.alice = 15 - { user, ... }: 16 - { 17 - homeManager.programs.helix.enable = user.name == "alice"; 18 - }; 19 - }; 20 - }
-5
templates/default/modules/den.nix
··· 1 - { 2 - den.hosts.x86_64-linux.igloo.users.alice = { }; 3 - den.hosts.aarch64-darwin.apple.users.alice = { }; 4 - den.homes.x86_64-linux.alice = { }; 5 - }
+11 -3
templates/default/modules/dendritic.nix
··· 1 - { inputs, lib, ... }: 1 + { inputs, ... }: 2 2 { 3 - flake-file.inputs.flake-file.url = lib.mkDefault "github:vic/flake-file"; 4 - flake-file.inputs.den.url = lib.mkDefault "github:vic/den"; 5 3 imports = [ 6 4 (inputs.flake-file.flakeModules.dendritic or { }) 7 5 (inputs.den.flakeModules.dendritic or { }) 8 6 ]; 7 + 8 + # other inputs may be defined at a module using them. 9 + flake-file.inputs = { 10 + den.url = "github:vic/den"; 11 + flake-file.url = "github:vic/flake-file"; 12 + home-manager = { 13 + url = "github:nix-community/home-manager"; 14 + inputs.nixpkgs.follows = "nixpkgs"; 15 + }; 16 + }; 9 17 }
+19
templates/default/modules/hosts.nix
··· 1 + # defines all hosts + users + homes. 2 + # then config their aspects in as many files you want 3 + { 4 + # tux user at igloo host. 5 + den.hosts.x86_64-linux.igloo.users.tux = { }; 6 + 7 + # define an standalone home-manager for tux 8 + # den.homes.x86_64-linux.tux = { }; 9 + 10 + # be sure to add nix-darwin input for this: 11 + # den.hosts.aarch64-darwin.apple.users.alice = { }; 12 + 13 + # other hosts can also have user tux. 14 + # den.hosts.x86_64-linux.south = { 15 + # wsl = { }; # add nixos-wsl input for this. 16 + # users.tux = { }; 17 + # users.orca = { }; 18 + # }; 19 + }
+18
templates/default/modules/igloo.nix
··· 1 + { 2 + # host aspect 3 + den.aspects.igloo = { 4 + # host NixOS configuration 5 + nixos = 6 + { pkgs, ... }: 7 + { 8 + environment.systemPackages = [ pkgs.hello ]; 9 + }; 10 + 11 + # host provides default home environment for its users 12 + homeManager = 13 + { pkgs, ... }: 14 + { 15 + home.packages = [ pkgs.vim ]; 16 + }; 17 + }; 18 + }
-37
templates/default/modules/inputs.nix
··· 1 - # This repo was generated with github:vic/flake-file#dendritic template. 2 - # Run `nix run .#write-flake` after changing any input. 3 - # 4 - # Inputs can be placed in any module, the best practice is to have them 5 - # as close as possible to their actual usage. 6 - # See: https://vic.github.io/dendrix/Dendritic.html#minimal-and-focused-flakenix 7 - # 8 - # For our template, we enable home-manager and nix-darwin by default, but 9 - # you are free to remove them if not being used by you. 10 - { ... }: 11 - { 12 - 13 - flake-file.inputs = { 14 - home-manager = { 15 - url = "github:nix-community/home-manager"; 16 - inputs.nixpkgs.follows = "nixpkgs"; 17 - }; 18 - 19 - darwin = { 20 - url = "github:nix-darwin/nix-darwin"; 21 - inputs.nixpkgs.follows = "nixpkgs"; 22 - }; 23 - 24 - ## these stable inputs are for wsl 25 - #nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 26 - #home-manager-stable.url = "github:nix-community/home-manager/release-25.05"; 27 - #home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable"; 28 - 29 - #nixos-wsl = { 30 - # url = "github:nix-community/nixos-wsl"; 31 - # inputs.nixpkgs.follows = "nixpkgs-stable"; 32 - # inputs.flake-compat.follows = ""; 33 - #}; 34 - 35 - }; 36 - 37 - }
-15
templates/default/modules/namespace.nix
··· 1 - { inputs, den, ... }: 2 - { 3 - # create an `eg` (example!) namespace. (flake exposed) 4 - imports = [ (inputs.den.namespace "eg" true) ]; 5 - 6 - # you can have more than one namespace (false = not flake exposed) 7 - # imports = [ (inputs.den.namespace "my" false) ]; 8 - 9 - # you can also merge many namespaces from remote flakes. 10 - # keep in mind a namespace is defined only once, so give it an array: 11 - # imports = [ (inputs.den.namespace "ours" [inputs.ours inputs.theirs]) ]; 12 - 13 - # this line enables den angle brackets syntax in modules. 14 - _module.args.__findFile = den.lib.__findFile; 15 - }
-34
templates/default/modules/tests.nix
··· 1 - # Some CI checks to ensure this template always works. 2 - # Feel free to adapt or remove when this repo is yours. 3 - { inputs, ... }: 4 - { 5 - perSystem = 6 - { 7 - pkgs, 8 - self', 9 - lib, 10 - ... 11 - }: 12 - let 13 - checkCond = name: cond: pkgs.runCommandLocal name { } (if cond then "touch $out" else ""); 14 - apple = inputs.self.darwinConfigurations.apple.config; 15 - igloo = inputs.self.nixosConfigurations.igloo.config; 16 - alice-at-igloo = igloo.home-manager.users.alice; 17 - vmBuilds = !pkgs.stdenvNoCC.isLinux || builtins.pathExists (self'.packages.vm + "/bin/vm"); 18 - iglooBuilds = !pkgs.stdenvNoCC.isLinux || builtins.pathExists (igloo.system.build.toplevel); 19 - appleBuilds = !pkgs.stdenvNoCC.isDarwin || builtins.pathExists (apple.system.build.toplevel); 20 - in 21 - { 22 - checks."igloo builds" = checkCond "igloo-builds" iglooBuilds; 23 - checks."apple builds" = checkCond "apple-builds" appleBuilds; 24 - checks."vm builds" = checkCond "vm-builds" vmBuilds; 25 - 26 - checks."alice enabled igloo nh" = checkCond "alice.provides.igloo" igloo.programs.nh.enable; 27 - checks."igloo enabled alice helix" = 28 - checkCond "igloo.provides.alice" alice-at-igloo.programs.helix.enable; 29 - 30 - checks."alice-custom-emacs" = checkCond "hm.programs.emacs.package" ( 31 - "emacs-nox" == lib.getName alice-at-igloo.programs.emacs.package 32 - ); 33 - }; 34 - }
+20
templates/default/modules/tux.nix
··· 1 + { den, ... }: 2 + { 3 + # user aspect 4 + den.aspects.tux = { 5 + includes = [ 6 + den.provides.primary-user 7 + (den.provides.user-shell "fish") 8 + ]; 9 + 10 + homeManager = 11 + { pkgs, ... }: 12 + { 13 + home.packages = [ pkgs.htop ]; 14 + }; 15 + 16 + # user can provide NixOS configurations 17 + # to any host it is included on 18 + # nixos = { pkgs, ... }: { }; 19 + }; 20 + }
-22
templates/default/modules/vm.nix
··· 1 - # enables `nix run .#vm`. it is very useful to have a VM 2 - # you can edit your config and launch the VM to test stuff 3 - # instead of having to reboot each time. 4 - { inputs, eg, ... }: 5 - { 6 - 7 - den.aspects.igloo.includes = [ 8 - eg.vm._.gui 9 - # eg.vm._.tui 10 - ]; 11 - 12 - perSystem = 13 - { pkgs, ... }: 14 - { 15 - packages.vm = pkgs.writeShellApplication { 16 - name = "vm"; 17 - text = '' 18 - ${inputs.self.nixosConfigurations.igloo.config.system.build.vm}/bin/run-igloo-vm "$@" 19 - ''; 20 - }; 21 - }; 22 - }
+27
templates/example/.github/workflows/test.yml
··· 1 + on: 2 + pull_request: 3 + push: 4 + concurrency: 5 + group: ${{ github.workflow }}-${{ github.ref }} 6 + cancel-in-progress: true 7 + jobs: 8 + flake-check: 9 + strategy: 10 + matrix: 11 + os: [ubuntu-latest, macos-latest] 12 + name: Nix flake check ${{matrix.os}} 13 + runs-on: ${{matrix.os}} 14 + steps: 15 + - uses: wimpysworld/nothing-but-nix@main 16 + if: matrix.os == 'ubuntu-latest' 17 + - uses: DeterminateSystems/nix-installer-action@main 18 + - uses: DeterminateSystems/magic-nix-cache-action@main 19 + - uses: actions/checkout@v5 20 + - run: nix flake metadata 21 + - run: | 22 + cat <<-EOF > modules/ci-runtime.nix 23 + { 24 + _module.args.CI = true; 25 + } 26 + EOF 27 + - run: nix flake check
+38
templates/example/README.md
··· 1 + # Examples 2 + 3 + This template provides some basic examples of how to use Den features. 4 + However, you will learn more by reading templates/ci which tests all of Den. 5 + 6 + Steps you can follow after cloning this template: 7 + 8 + - Be sure to read the [den documentation](https://vic.github.io/den) 9 + 10 + - Update den input. 11 + 12 + ```console 13 + nix flake update den 14 + ``` 15 + 16 + - Run checks to test everything works. 17 + 18 + ```console 19 + nix flake check 20 + ``` 21 + 22 + - Read [modules/den.nix](modules/den.nix) where hosts and homes definitions are for this example. 23 + 24 + - Read [modules/namespace.nix](modules/namespace.nix) where a new `eg` (an example) aspects namespace is created. 25 + 26 + - Read [modules/aspects/igloo.nix](modules/aspects/igloo.nix) where the `igloo` host is configured. 27 + 28 + - Read [modules/aspects/alice.nix](modules/aspects/alice.nix) where the `alice` user is configured. 29 + 30 + - Run the VM. 31 + 32 + ```console 33 + nix run .#vm 34 + ``` 35 + 36 + - Edit and run VM loop. 37 + 38 + Feel free to add more aspects, organize things to your liking.
+173
templates/example/flake.lock
··· 1 + { 2 + "nodes": { 3 + "darwin": { 4 + "inputs": { 5 + "nixpkgs": [ 6 + "nixpkgs" 7 + ] 8 + }, 9 + "locked": { 10 + "lastModified": 1762627886, 11 + "narHash": "sha256-/QLk1bzmbcqJt9sU43+y/3tHtXhAy0l8Ck0MoO2+evQ=", 12 + "owner": "nix-darwin", 13 + "repo": "nix-darwin", 14 + "rev": "5125a3cd414dc98bbe2c528227aa6b62ee61f733", 15 + "type": "github" 16 + }, 17 + "original": { 18 + "owner": "nix-darwin", 19 + "repo": "nix-darwin", 20 + "type": "github" 21 + } 22 + }, 23 + "den": { 24 + "locked": { 25 + "lastModified": 1763707606, 26 + "narHash": "sha256-l9v3NNdKj3GJvV5LhzsWDs4Sl2bg0tuKNFFkMeFvUWo=", 27 + "owner": "vic", 28 + "repo": "den", 29 + "rev": "8164e0d89c59839d67757bc9a1fb61770dc6e8b7", 30 + "type": "github" 31 + }, 32 + "original": { 33 + "owner": "vic", 34 + "repo": "den", 35 + "type": "github" 36 + } 37 + }, 38 + "flake-aspects": { 39 + "locked": { 40 + "lastModified": 1769717274, 41 + "narHash": "sha256-U15OaMr9AcJiB1wW2uCFzFO+DnQ3jJSvln+ZR/+Q0vE=", 42 + "owner": "vic", 43 + "repo": "flake-aspects", 44 + "rev": "a35ed5efc9a629694d659d606230ba18a76cefaa", 45 + "type": "github" 46 + }, 47 + "original": { 48 + "owner": "vic", 49 + "repo": "flake-aspects", 50 + "type": "github" 51 + } 52 + }, 53 + "flake-file": { 54 + "locked": { 55 + "lastModified": 1763706734, 56 + "narHash": "sha256-kR1Rrh9evfiJaTb6ufWCSk6GMtrnPKFydqQUV0Bb4Eg=", 57 + "owner": "vic", 58 + "repo": "flake-file", 59 + "rev": "9af20d5e62c94b658b4d0671829393c1b8bb0b3e", 60 + "type": "github" 61 + }, 62 + "original": { 63 + "owner": "vic", 64 + "repo": "flake-file", 65 + "type": "github" 66 + } 67 + }, 68 + "flake-parts": { 69 + "inputs": { 70 + "nixpkgs-lib": [ 71 + "nixpkgs-lib" 72 + ] 73 + }, 74 + "locked": { 75 + "lastModified": 1762810396, 76 + "narHash": "sha256-dxFVgQPG+R72dkhXTtqUm7KpxElw3u6E+YlQ2WaDgt8=", 77 + "owner": "hercules-ci", 78 + "repo": "flake-parts", 79 + "rev": "0bdadb1b265fb4143a75bd1ec7d8c915898a9923", 80 + "type": "github" 81 + }, 82 + "original": { 83 + "owner": "hercules-ci", 84 + "repo": "flake-parts", 85 + "type": "github" 86 + } 87 + }, 88 + "home-manager": { 89 + "inputs": { 90 + "nixpkgs": [ 91 + "nixpkgs" 92 + ] 93 + }, 94 + "locked": { 95 + "lastModified": 1762787259, 96 + "narHash": "sha256-t2U/GLLXHa2+kJkwnFNRVc2fEJ/lUfyZXBE5iKzJdcs=", 97 + "owner": "nix-community", 98 + "repo": "home-manager", 99 + "rev": "37a3d97f2873e0f68711117c34d04b7c7ead8f4e", 100 + "type": "github" 101 + }, 102 + "original": { 103 + "owner": "nix-community", 104 + "repo": "home-manager", 105 + "type": "github" 106 + } 107 + }, 108 + "import-tree": { 109 + "locked": { 110 + "lastModified": 1762327901, 111 + "narHash": "sha256-AJ96FNj50DU0bTyIzAPkPOjCZTHqjURVjok8qoXvmqM=", 112 + "owner": "vic", 113 + "repo": "import-tree", 114 + "rev": "90fa129798be99cde036b78658e89475710966a1", 115 + "type": "github" 116 + }, 117 + "original": { 118 + "owner": "vic", 119 + "repo": "import-tree", 120 + "type": "github" 121 + } 122 + }, 123 + "nixpkgs": { 124 + "locked": { 125 + "lastModified": 1762482733, 126 + "narHash": "sha256-g/da4FzvckvbiZT075Sb1/YDNDr+tGQgh4N8i5ceYMg=", 127 + "owner": "nixos", 128 + "repo": "nixpkgs", 129 + "rev": "e1ebeec86b771e9d387dd02d82ffdc77ac753abc", 130 + "type": "github" 131 + }, 132 + "original": { 133 + "owner": "nixos", 134 + "ref": "nixpkgs-unstable", 135 + "repo": "nixpkgs", 136 + "type": "github" 137 + } 138 + }, 139 + "root": { 140 + "inputs": { 141 + "darwin": "darwin", 142 + "den": "den", 143 + "flake-aspects": "flake-aspects", 144 + "flake-file": "flake-file", 145 + "flake-parts": "flake-parts", 146 + "home-manager": "home-manager", 147 + "import-tree": "import-tree", 148 + "nixpkgs": "nixpkgs", 149 + "nixpkgs-lib": [ 150 + "nixpkgs" 151 + ], 152 + "systems": "systems" 153 + } 154 + }, 155 + "systems": { 156 + "locked": { 157 + "lastModified": 1681028828, 158 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 159 + "owner": "nix-systems", 160 + "repo": "default", 161 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 162 + "type": "github" 163 + }, 164 + "original": { 165 + "owner": "nix-systems", 166 + "repo": "default", 167 + "type": "github" 168 + } 169 + } 170 + }, 171 + "root": "root", 172 + "version": 7 173 + }
+29
templates/example/flake.nix
··· 1 + # DO-NOT-EDIT. This file was auto-generated using github:vic/flake-file. 2 + # Use `nix run .#write-flake` to regenerate it. 3 + { 4 + 5 + outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 6 + 7 + inputs = { 8 + darwin = { 9 + inputs.nixpkgs.follows = "nixpkgs"; 10 + url = "github:nix-darwin/nix-darwin"; 11 + }; 12 + den.url = "github:vic/den"; 13 + flake-aspects.url = "github:vic/flake-aspects"; 14 + flake-file.url = "github:vic/flake-file"; 15 + flake-parts = { 16 + inputs.nixpkgs-lib.follows = "nixpkgs-lib"; 17 + url = "github:hercules-ci/flake-parts"; 18 + }; 19 + home-manager = { 20 + inputs.nixpkgs.follows = "nixpkgs"; 21 + url = "github:nix-community/home-manager"; 22 + }; 23 + import-tree.url = "github:vic/import-tree"; 24 + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 25 + nixpkgs-lib.follows = "nixpkgs"; 26 + systems.url = "github:nix-systems/default"; 27 + }; 28 + 29 + }
+71
templates/example/modules/aspects/alice.nix
··· 1 + { den, eg, ... }: 2 + { 3 + den.aspects.alice = { 4 + 5 + # Alice can include other aspects. 6 + # For small, private one-shot aspects, use let-bindings like here. 7 + # for more complex or re-usable ones, define on their own modules, 8 + # as part of any aspect-subtree. 9 + includes = 10 + let 11 + # hack for nixf linter to keep findFile :/ 12 + unused = den.lib.take.unused __findFile; 13 + __findFile = unused den.lib.__findFile; 14 + 15 + customEmacs.homeManager = 16 + { pkgs, ... }: 17 + { 18 + programs.emacs.enable = true; 19 + programs.emacs.package = pkgs.emacs30-nox; 20 + }; 21 + in 22 + [ 23 + # from local bindings. 24 + customEmacs 25 + # from the aspect tree, cooper example is defined bellow 26 + den.aspects.cooper 27 + den.aspects.setHost 28 + # from the `eg` namespace. 29 + eg.autologin 30 + # den included batteries that provide common configs. 31 + <den/primary-user> # alice is admin always. 32 + (<den/user-shell> "fish") # default user shell 33 + ]; 34 + 35 + # Alice configures NixOS hosts it lives on. 36 + nixos = 37 + { pkgs, ... }: 38 + { 39 + users.users.alice.packages = [ pkgs.vim ]; 40 + }; 41 + 42 + # Alice home-manager. 43 + homeManager = 44 + { pkgs, ... }: 45 + { 46 + home.packages = [ pkgs.htop ]; 47 + }; 48 + 49 + # <user>.provides.<host>, via eg/routes.nix 50 + provides.igloo = 51 + { host, ... }: 52 + { 53 + nixos.programs.nh.enable = host.name == "igloo"; 54 + }; 55 + }; 56 + 57 + # This is a context-aware aspect, that emits configurations 58 + # **anytime** at least the `user` data is in context. 59 + # read more at https://vic.github.io/den/context-aware.html 60 + den.aspects.cooper = 61 + { user, ... }: 62 + { 63 + nixos.users.users.${user.userName}.description = "Alice Cooper"; 64 + }; 65 + 66 + den.aspects.setHost = 67 + { host, ... }: 68 + { 69 + networking.hostName = host.hostName; 70 + }; 71 + }
+47
templates/example/modules/aspects/defaults.nix
··· 1 + { 2 + config, 3 + # deadnix: skip # enable <den/brackets> syntax for demo. 4 + __findFile ? __findFile, 5 + den, 6 + ... 7 + }: 8 + { 9 + # Lets also configure some defaults using aspects. 10 + # These are global static settings. 11 + den.default = { 12 + darwin.system.stateVersion = 6; 13 + nixos.system.stateVersion = "25.05"; 14 + homeManager.home.stateVersion = "25.05"; 15 + }; 16 + 17 + # These are functions that produce configs 18 + den.default.includes = [ 19 + # ${user}.provides.${host} and ${host}.provides.${user} 20 + <eg/routes> 21 + 22 + # Enable home-manager on all hosts. 23 + <den/home-manager> 24 + 25 + # Automatically create the user on host. 26 + <den/define-user> 27 + 28 + # Disable booting when running on CI on all NixOS hosts. 29 + (if config ? _module.args.CI then <eg/ci-no-boot> else { }) 30 + 31 + # NOTE: be cautious when adding fully parametric functions to defaults. 32 + # defaults are included on EVERY host/user/home, and IF you are not careful 33 + # you could be duplicating config values. For example: 34 + # 35 + # # This will append 42 into foo option for the {host} and for EVERY {host,user} 36 + # ({ host, ... }: { nixos.foo = [ 42 ]; }) # DO-NOT-DO-THIS. 37 + # 38 + # # Instead try to be explicit if a function is intended for ONLY { host }. 39 + (den.lib.take.exactly ( 40 + { OS, host }: 41 + den.lib.take.unused OS { 42 + nixos.networking.hostName = host.hostName; 43 + } 44 + )) 45 + 46 + ]; 47 + }
+15
templates/example/modules/aspects/eg/autologin.nix
··· 1 + { 2 + # autologin is context-aware, parametric aspect. 3 + # it applies only if the context has at least { user } 4 + # meaning that has access to user data 5 + eg.autologin = 6 + { user, ... }: 7 + { 8 + nixos = 9 + { config, lib, ... }: 10 + lib.mkIf config.services.displayManager.enable { 11 + services.displayManager.autoLogin.enable = true; 12 + services.displayManager.autoLogin.user = user.userName; 13 + }; 14 + }; 15 + }
+9
templates/example/modules/aspects/eg/ci-no-boot.nix
··· 1 + { 2 + eg.ci-no-boot = { 3 + description = "Disables booting during CI"; 4 + nixos = { 5 + boot.loader.grub.enable = false; 6 + fileSystems."/".device = "/dev/null"; 7 + }; 8 + }; 9 + }
+36
templates/example/modules/aspects/eg/routes.nix
··· 1 + # This example implements an aspect "routing" pattern. 2 + # 3 + # Unlike `den.default` which is `parametric.atLeast` 4 + # we use `parametric.fixedTo` here, which help us 5 + # propagate an already computed context to all includes. 6 + # 7 + # This aspect, when installed in a `parametric.atLeast` 8 + # will just forward the same context. 9 + # The `mutual` helper returns an static configuration which 10 + # is ignored by parametric aspects, thus allowing 11 + # non-existing aspects to be just ignored. 12 + # 13 + # Be sure to read: https://vic.github.io/den/dependencies.html 14 + # See usage at: defaults.nix, alice.nix, igloo.nix 15 + # 16 + { den, ... }: 17 + { 18 + # Usage: `den.default.includes [ eg.routes ]` 19 + eg.routes = 20 + let 21 + inherit (den.lib) parametric; 22 + 23 + # eg, `<user>._.<host>` and `<host>._.<user>` 24 + mutual = from: to: den.aspects.${from.aspect}._.${to.aspect} or { }; 25 + 26 + routes = 27 + { host, user, ... }@ctx: 28 + parametric.fixedTo ctx { 29 + includes = [ 30 + (mutual user host) 31 + (mutual host user) 32 + ]; 33 + }; 34 + in 35 + routes; 36 + }
+16
templates/example/modules/aspects/eg/vm-bootable.nix
··· 1 + let 2 + installer = variant: { 3 + nixos = 4 + { modulesPath, ... }: 5 + { 6 + imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-${variant}.nix") ]; 7 + }; 8 + }; 9 + in 10 + { 11 + # make USB/VM installers. 12 + eg.vm-bootable.provides = { 13 + tui = installer "minimal"; 14 + gui = installer "graphical-base"; 15 + }; 16 + }
+15
templates/example/modules/aspects/eg/vm.nix
··· 1 + { eg, ... }: 2 + { 3 + eg.vm.provides = { 4 + gui.includes = [ 5 + eg.vm 6 + eg.vm-bootable._.gui 7 + eg.xfce-desktop 8 + ]; 9 + 10 + tui.includes = [ 11 + eg.vm 12 + eg.vm-bootable._.tui 13 + ]; 14 + }; 15 + }
+19
templates/example/modules/aspects/eg/xfce-desktop.nix
··· 1 + { 2 + eg.xfce-desktop.nixos = 3 + { lib, ... }: 4 + { 5 + # https://gist.github.com/nat-418/1101881371c9a7b419ba5f944a7118b0 6 + services.xserver = { 7 + enable = true; 8 + desktopManager = { 9 + xterm.enable = false; 10 + xfce.enable = true; 11 + }; 12 + }; 13 + 14 + services.displayManager = { 15 + defaultSession = lib.mkDefault "xfce"; 16 + enable = true; 17 + }; 18 + }; 19 + }
+20
templates/example/modules/aspects/igloo.nix
··· 1 + { 2 + den.aspects.igloo = { 3 + # igloo host provides some home-manager defaults to its users. 4 + homeManager.programs.direnv.enable = true; 5 + 6 + # NixOS configuration for igloo. 7 + nixos = 8 + { pkgs, ... }: 9 + { 10 + environment.systemPackages = [ pkgs.hello ]; 11 + }; 12 + 13 + # <host>.provides.<user>, via eg/routes.nix 14 + provides.alice = 15 + { user, ... }: 16 + { 17 + homeManager.programs.helix.enable = user.name == "alice"; 18 + }; 19 + }; 20 + }
+5
templates/example/modules/den.nix
··· 1 + { 2 + den.hosts.x86_64-linux.igloo.users.alice = { }; 3 + den.hosts.aarch64-darwin.apple.users.alice = { }; 4 + den.homes.x86_64-linux.alice = { }; 5 + }
+9
templates/example/modules/dendritic.nix
··· 1 + { inputs, lib, ... }: 2 + { 3 + flake-file.inputs.flake-file.url = lib.mkDefault "github:vic/flake-file"; 4 + flake-file.inputs.den.url = lib.mkDefault "github:vic/den"; 5 + imports = [ 6 + (inputs.flake-file.flakeModules.dendritic or { }) 7 + (inputs.den.flakeModules.dendritic or { }) 8 + ]; 9 + }
+37
templates/example/modules/inputs.nix
··· 1 + # This repo was generated with github:vic/flake-file#dendritic template. 2 + # Run `nix run .#write-flake` after changing any input. 3 + # 4 + # Inputs can be placed in any module, the best practice is to have them 5 + # as close as possible to their actual usage. 6 + # See: https://vic.github.io/dendrix/Dendritic.html#minimal-and-focused-flakenix 7 + # 8 + # For our template, we enable home-manager and nix-darwin by default, but 9 + # you are free to remove them if not being used by you. 10 + { ... }: 11 + { 12 + 13 + flake-file.inputs = { 14 + home-manager = { 15 + url = "github:nix-community/home-manager"; 16 + inputs.nixpkgs.follows = "nixpkgs"; 17 + }; 18 + 19 + darwin = { 20 + url = "github:nix-darwin/nix-darwin"; 21 + inputs.nixpkgs.follows = "nixpkgs"; 22 + }; 23 + 24 + ## these stable inputs are for wsl 25 + #nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 26 + #home-manager-stable.url = "github:nix-community/home-manager/release-25.05"; 27 + #home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable"; 28 + 29 + #nixos-wsl = { 30 + # url = "github:nix-community/nixos-wsl"; 31 + # inputs.nixpkgs.follows = "nixpkgs-stable"; 32 + # inputs.flake-compat.follows = ""; 33 + #}; 34 + 35 + }; 36 + 37 + }
+15
templates/example/modules/namespace.nix
··· 1 + { inputs, den, ... }: 2 + { 3 + # create an `eg` (example!) namespace. (flake exposed) 4 + imports = [ (inputs.den.namespace "eg" true) ]; 5 + 6 + # you can have more than one namespace (false = not flake exposed) 7 + # imports = [ (inputs.den.namespace "my" false) ]; 8 + 9 + # you can also merge many namespaces from remote flakes. 10 + # keep in mind a namespace is defined only once, so give it an array: 11 + # imports = [ (inputs.den.namespace "ours" [inputs.ours inputs.theirs]) ]; 12 + 13 + # this line enables den angle brackets syntax in modules. 14 + _module.args.__findFile = den.lib.__findFile; 15 + }
+34
templates/example/modules/tests.nix
··· 1 + # Some CI checks to ensure this template always works. 2 + # Feel free to adapt or remove when this repo is yours. 3 + { inputs, ... }: 4 + { 5 + perSystem = 6 + { 7 + pkgs, 8 + self', 9 + lib, 10 + ... 11 + }: 12 + let 13 + checkCond = name: cond: pkgs.runCommandLocal name { } (if cond then "touch $out" else ""); 14 + apple = inputs.self.darwinConfigurations.apple.config; 15 + igloo = inputs.self.nixosConfigurations.igloo.config; 16 + alice-at-igloo = igloo.home-manager.users.alice; 17 + vmBuilds = !pkgs.stdenvNoCC.isLinux || builtins.pathExists (self'.packages.vm + "/bin/vm"); 18 + iglooBuilds = !pkgs.stdenvNoCC.isLinux || builtins.pathExists (igloo.system.build.toplevel); 19 + appleBuilds = !pkgs.stdenvNoCC.isDarwin || builtins.pathExists (apple.system.build.toplevel); 20 + in 21 + { 22 + checks."igloo builds" = checkCond "igloo-builds" iglooBuilds; 23 + checks."apple builds" = checkCond "apple-builds" appleBuilds; 24 + checks."vm builds" = checkCond "vm-builds" vmBuilds; 25 + 26 + checks."alice enabled igloo nh" = checkCond "alice.provides.igloo" igloo.programs.nh.enable; 27 + checks."igloo enabled alice helix" = 28 + checkCond "igloo.provides.alice" alice-at-igloo.programs.helix.enable; 29 + 30 + checks."alice-custom-emacs" = checkCond "hm.programs.emacs.package" ( 31 + "emacs-nox" == lib.getName alice-at-igloo.programs.emacs.package 32 + ); 33 + }; 34 + }
+22
templates/example/modules/vm.nix
··· 1 + # enables `nix run .#vm`. it is very useful to have a VM 2 + # you can edit your config and launch the VM to test stuff 3 + # instead of having to reboot each time. 4 + { inputs, eg, ... }: 5 + { 6 + 7 + den.aspects.igloo.includes = [ 8 + eg.vm._.gui 9 + # eg.vm._.tui 10 + ]; 11 + 12 + perSystem = 13 + { pkgs, ... }: 14 + { 15 + packages.vm = pkgs.writeShellApplication { 16 + name = "vm"; 17 + text = '' 18 + ${inputs.self.nixosConfigurations.igloo.config.system.build.vm}/bin/run-igloo-vm "$@" 19 + ''; 20 + }; 21 + }; 22 + }
-304
templates/examples/flake.lock
··· 1 - { 2 - "nodes": { 3 - "darwin": { 4 - "inputs": { 5 - "nixpkgs": [ 6 - "nixpkgs" 7 - ] 8 - }, 9 - "locked": { 10 - "lastModified": 1762039661, 11 - "narHash": "sha256-oM5BwAGE78IBLZn+AqxwH/saqwq3e926rNq5HmOulkc=", 12 - "owner": "nix-darwin", 13 - "repo": "nix-darwin", 14 - "rev": "c3c8c9f2a5ed43175ac4dc030308756620e6e4e4", 15 - "type": "github" 16 - }, 17 - "original": { 18 - "owner": "nix-darwin", 19 - "repo": "nix-darwin", 20 - "type": "github" 21 - } 22 - }, 23 - "den": { 24 - "locked": { 25 - "lastModified": 1767645236, 26 - "narHash": "sha256-VMrWett3fWRb0hQh9K1JUC3zV2OlV4zOajFZHGr1GPw=", 27 - "owner": "vic", 28 - "repo": "den", 29 - "rev": "e7837daa0ea0f03fba62a2653ce16314a7d9d1c8", 30 - "type": "github" 31 - }, 32 - "original": { 33 - "owner": "vic", 34 - "repo": "den", 35 - "type": "github" 36 - } 37 - }, 38 - "flake-aspects": { 39 - "locked": { 40 - "lastModified": 1769717274, 41 - "narHash": "sha256-U15OaMr9AcJiB1wW2uCFzFO+DnQ3jJSvln+ZR/+Q0vE=", 42 - "owner": "vic", 43 - "repo": "flake-aspects", 44 - "rev": "a35ed5efc9a629694d659d606230ba18a76cefaa", 45 - "type": "github" 46 - }, 47 - "original": { 48 - "owner": "vic", 49 - "repo": "flake-aspects", 50 - "type": "github" 51 - } 52 - }, 53 - "flake-file": { 54 - "locked": { 55 - "lastModified": 1763706734, 56 - "narHash": "sha256-kR1Rrh9evfiJaTb6ufWCSk6GMtrnPKFydqQUV0Bb4Eg=", 57 - "owner": "vic", 58 - "repo": "flake-file", 59 - "rev": "9af20d5e62c94b658b4d0671829393c1b8bb0b3e", 60 - "type": "github" 61 - }, 62 - "original": { 63 - "owner": "vic", 64 - "repo": "flake-file", 65 - "type": "github" 66 - } 67 - }, 68 - "flake-parts": { 69 - "inputs": { 70 - "nixpkgs-lib": [ 71 - "nixpkgs-lib" 72 - ] 73 - }, 74 - "locked": { 75 - "lastModified": 1762040540, 76 - "narHash": "sha256-z5PlZ47j50VNF3R+IMS9LmzI5fYRGY/Z5O5tol1c9I4=", 77 - "owner": "hercules-ci", 78 - "repo": "flake-parts", 79 - "rev": "0010412d62a25d959151790968765a70c436598b", 80 - "type": "github" 81 - }, 82 - "original": { 83 - "owner": "hercules-ci", 84 - "repo": "flake-parts", 85 - "type": "github" 86 - } 87 - }, 88 - "home-manager": { 89 - "inputs": { 90 - "nixpkgs": [ 91 - "nixpkgs" 92 - ] 93 - }, 94 - "locked": { 95 - "lastModified": 1762087455, 96 - "narHash": "sha256-hpbPma1eUKwLAmiVRoMgIHbHiIKFkcACobJLbDt6ABw=", 97 - "owner": "nix-community", 98 - "repo": "home-manager", 99 - "rev": "43e205606aeb253bfcee15fd8a4a01d8ce8384ca", 100 - "type": "github" 101 - }, 102 - "original": { 103 - "owner": "nix-community", 104 - "repo": "home-manager", 105 - "type": "github" 106 - } 107 - }, 108 - "home-manager-stable": { 109 - "inputs": { 110 - "nixpkgs": [ 111 - "nixpkgs-stable" 112 - ] 113 - }, 114 - "locked": { 115 - "lastModified": 1758463745, 116 - "narHash": "sha256-uhzsV0Q0I9j2y/rfweWeGif5AWe0MGrgZ/3TjpDYdGA=", 117 - "owner": "nix-community", 118 - "repo": "home-manager", 119 - "rev": "3b955f5f0a942f9f60cdc9cacb7844335d0f21c3", 120 - "type": "github" 121 - }, 122 - "original": { 123 - "owner": "nix-community", 124 - "ref": "release-25.05", 125 - "repo": "home-manager", 126 - "type": "github" 127 - } 128 - }, 129 - "import-tree": { 130 - "locked": { 131 - "lastModified": 1761120675, 132 - "narHash": "sha256-TEbh9zISiQcU82VwVoEbmXHnSGlUxTwvjJA9g9ErSDA=", 133 - "owner": "vic", 134 - "repo": "import-tree", 135 - "rev": "a037ed2a58fc0ebed9e93b9ef79b0646e648f719", 136 - "type": "github" 137 - }, 138 - "original": { 139 - "owner": "vic", 140 - "repo": "import-tree", 141 - "type": "github" 142 - } 143 - }, 144 - "neovim-nightly-overlay": { 145 - "inputs": { 146 - "flake-parts": [ 147 - "flake-parts" 148 - ], 149 - "neovim-src": "neovim-src", 150 - "nixpkgs": [ 151 - "nixpkgs" 152 - ] 153 - }, 154 - "locked": { 155 - "lastModified": 1766016290, 156 - "narHash": "sha256-YMf/PUyY4z7RlIe/Dzn1NnxZGS0Vp2eHxcMNWJM9q+A=", 157 - "owner": "nix-community", 158 - "repo": "neovim-nightly-overlay", 159 - "rev": "f7fbc4e3d4ccea45eaa5b187884592eb42dfdbbd", 160 - "type": "github" 161 - }, 162 - "original": { 163 - "owner": "nix-community", 164 - "repo": "neovim-nightly-overlay", 165 - "type": "github" 166 - } 167 - }, 168 - "neovim-src": { 169 - "flake": false, 170 - "locked": { 171 - "lastModified": 1766014002, 172 - "narHash": "sha256-KE/ufBGH8XFXTw3Vt1DrK1rQmAEp1Q+oyLQibX5UKO0=", 173 - "owner": "neovim", 174 - "repo": "neovim", 175 - "rev": "c172fd9f464d5766eab9071e8f4770504c920c05", 176 - "type": "github" 177 - }, 178 - "original": { 179 - "owner": "neovim", 180 - "repo": "neovim", 181 - "type": "github" 182 - } 183 - }, 184 - "nixos-wsl": { 185 - "inputs": { 186 - "flake-compat": [], 187 - "nixpkgs": [ 188 - "nixpkgs-stable" 189 - ] 190 - }, 191 - "locked": { 192 - "lastModified": 1761969132, 193 - "narHash": "sha256-0me4+e+1VxNuvySSw0voqMCWU/eUmTuth7f4+Q2jbUY=", 194 - "owner": "nix-community", 195 - "repo": "nixos-wsl", 196 - "rev": "761582d6ab431549fe1396d2cd681e3fe9376020", 197 - "type": "github" 198 - }, 199 - "original": { 200 - "owner": "nix-community", 201 - "repo": "nixos-wsl", 202 - "type": "github" 203 - } 204 - }, 205 - "nixpkgs": { 206 - "locked": { 207 - "lastModified": 1761880412, 208 - "narHash": "sha256-QoJjGd4NstnyOG4mm4KXF+weBzA2AH/7gn1Pmpfcb0A=", 209 - "owner": "nixos", 210 - "repo": "nixpkgs", 211 - "rev": "a7fc11be66bdfb5cdde611ee5ce381c183da8386", 212 - "type": "github" 213 - }, 214 - "original": { 215 - "owner": "nixos", 216 - "ref": "nixpkgs-unstable", 217 - "repo": "nixpkgs", 218 - "type": "github" 219 - } 220 - }, 221 - "nixpkgs-stable": { 222 - "locked": { 223 - "lastModified": 1762081535, 224 - "narHash": "sha256-+j+CUiaUoa87EhnSOqG5pwXdJWahP8vo6BE0ekssdzs=", 225 - "owner": "nixos", 226 - "repo": "nixpkgs", 227 - "rev": "2afc9d6e79b59ea9bcaf620d335623b0f7c2ce96", 228 - "type": "github" 229 - }, 230 - "original": { 231 - "owner": "nixos", 232 - "ref": "release-25.05", 233 - "repo": "nixpkgs", 234 - "type": "github" 235 - } 236 - }, 237 - "provider": { 238 - "inputs": { 239 - "den": [ 240 - "den" 241 - ], 242 - "flake-aspects": [ 243 - "flake-aspects" 244 - ], 245 - "flake-parts": [ 246 - "flake-parts" 247 - ], 248 - "import-tree": [ 249 - "import-tree" 250 - ], 251 - "nixpkgs": [ 252 - "nixpkgs" 253 - ] 254 - }, 255 - "locked": { 256 - "path": "./provider", 257 - "type": "path" 258 - }, 259 - "original": { 260 - "path": "./provider", 261 - "type": "path" 262 - }, 263 - "parent": [] 264 - }, 265 - "root": { 266 - "inputs": { 267 - "darwin": "darwin", 268 - "den": "den", 269 - "flake-aspects": "flake-aspects", 270 - "flake-file": "flake-file", 271 - "flake-parts": "flake-parts", 272 - "home-manager": "home-manager", 273 - "home-manager-stable": "home-manager-stable", 274 - "import-tree": "import-tree", 275 - "neovim-nightly-overlay": "neovim-nightly-overlay", 276 - "nixos-wsl": "nixos-wsl", 277 - "nixpkgs": "nixpkgs", 278 - "nixpkgs-lib": [ 279 - "nixpkgs" 280 - ], 281 - "nixpkgs-stable": "nixpkgs-stable", 282 - "provider": "provider", 283 - "systems": "systems" 284 - } 285 - }, 286 - "systems": { 287 - "locked": { 288 - "lastModified": 1681028828, 289 - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 290 - "owner": "nix-systems", 291 - "repo": "default", 292 - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 293 - "type": "github" 294 - }, 295 - "original": { 296 - "owner": "nix-systems", 297 - "repo": "default", 298 - "type": "github" 299 - } 300 - } 301 - }, 302 - "root": "root", 303 - "version": 7 304 - }
-58
templates/examples/flake.nix
··· 1 - # DO-NOT-EDIT. This file was auto-generated using github:vic/flake-file. 2 - # Use `nix run .#write-flake` to regenerate it. 3 - { 4 - 5 - outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 6 - 7 - inputs = { 8 - darwin = { 9 - inputs.nixpkgs.follows = "nixpkgs"; 10 - url = "github:nix-darwin/nix-darwin"; 11 - }; 12 - den.url = "github:vic/den"; 13 - flake-aspects.url = "github:vic/flake-aspects"; 14 - flake-file.url = "github:vic/flake-file"; 15 - flake-parts = { 16 - inputs.nixpkgs-lib.follows = "nixpkgs-lib"; 17 - url = "github:hercules-ci/flake-parts"; 18 - }; 19 - home-manager = { 20 - inputs.nixpkgs.follows = "nixpkgs"; 21 - url = "github:nix-community/home-manager"; 22 - }; 23 - home-manager-stable = { 24 - inputs.nixpkgs.follows = "nixpkgs-stable"; 25 - url = "github:nix-community/home-manager/release-25.05"; 26 - }; 27 - import-tree.url = "github:vic/import-tree"; 28 - neovim-nightly-overlay = { 29 - inputs = { 30 - flake-parts.follows = "flake-parts"; 31 - nixpkgs.follows = "nixpkgs"; 32 - }; 33 - url = "github:nix-community/neovim-nightly-overlay"; 34 - }; 35 - nixos-wsl = { 36 - inputs = { 37 - flake-compat.follows = ""; 38 - nixpkgs.follows = "nixpkgs-stable"; 39 - }; 40 - url = "github:nix-community/nixos-wsl"; 41 - }; 42 - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 43 - nixpkgs-lib.follows = "nixpkgs"; 44 - nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 45 - provider = { 46 - inputs = { 47 - den.follows = "den"; 48 - flake-aspects.follows = "flake-aspects"; 49 - flake-parts.follows = "flake-parts"; 50 - import-tree.follows = "import-tree"; 51 - nixpkgs.follows = "nixpkgs"; 52 - }; 53 - url = "path:./provider"; 54 - }; 55 - systems.url = "github:nix-systems/default"; 56 - }; 57 - 58 - }
-5
templates/examples/modules/_example/README.md
··· 1 - User TODO: REMOVE this directory (or disable its import from den.nix) 2 - 3 - It is used to implement tests for all den features so we can validate at CI. 4 - 5 - Use it as reference to see how den features are used, however, be aware that this might not be the best practices for file/aspect organization.
-6
templates/examples/modules/_example/ci/_non_dendritic/hosts/honeycrisp/_darwin/something.nix
··· 1 - # this is a non-dendritic darwin class module file. 2 - # automatically discovered by `den.import-tree` as enabled in auto-imports.nix 3 - { ... }: 4 - { 5 - # see nix-darwin options. 6 - }
-14
templates/examples/modules/_example/ci/_non_dendritic/hosts/rockhopper/_nixos/hardware-auto-generated.nix
··· 1 - # this is a non-dendritic nix class module file. 2 - # automatically discovered by `den.import-tree` as enabled in auto-imports.nix 3 - # 4 - # USER TODO: Remove this file. 5 - # suppose this file was auto-generated by nixos-generate-config or some other hardware tooling. 6 - { lib, ... }: 7 - { 8 - # used in CI to test this file was actually imported. 9 - options.auto-imported = lib.mkOption { 10 - readOnly = true; 11 - type = lib.types.bool; 12 - default = true; 13 - }; 14 - }
-81
templates/examples/modules/_example/ci/angle-bracket-deep.nix
··· 1 - { 2 - inputs, 3 - lib, 4 - ns, 5 - # deadnix: skip 6 - __findFile ? __findFile, 7 - ... 8 - }: 9 - let 10 - treeModule.nixos.options.tree = lib.mkOption { 11 - type = lib.types.listOf lib.types.str; 12 - }; 13 - inputX = { 14 - denful.ns.root = { 15 - nixos.tree = [ "X-root" ]; 16 - provides.branch.nixos.tree = [ "X-branch" ]; 17 - provides.branch.provides.leaf.nixos.tree = [ "X-leaf" ]; 18 - }; 19 - }; 20 - inputY = { 21 - denful.ns.root = { 22 - nixos.tree = [ "Y-root" ]; 23 - provides.branch.nixos.tree = [ "Y-branch" ]; 24 - provides.branch.provides.leaf.nixos.tree = [ "Y-leaf" ]; 25 - }; 26 - }; 27 - in 28 - { 29 - 30 - imports = [ 31 - (inputs.den.namespace "ns" [ 32 - true 33 - inputX 34 - inputY 35 - ]) 36 - ]; 37 - 38 - ns.root = { 39 - nixos.tree = [ "local-root" ]; 40 - provides.branch.nixos.tree = [ "local-branch" ]; 41 - provides.branch.provides.leaf.nixos.tree = [ "local-leaf" ]; 42 - }; 43 - 44 - den.aspects.rockhopper.includes = [ 45 - treeModule 46 - <ns/root> 47 - <ns/root/branch> 48 - <ns/root/branch/leaf> 49 - ]; 50 - 51 - perSystem = 52 - { checkCond, rockhopper, ... }: 53 - let 54 - vals = lib.sort (a: b: a < b) rockhopper.config.tree; 55 - in 56 - { 57 - checks.ns-angle-bracket-root = checkCond "angle-bracket access root" (<ns/root> == ns.root); 58 - 59 - checks.ns-angle-bracket-branch = checkCond "angle-bracket access branch" ( 60 - <ns/root/branch> == ns.root._.branch 61 - ); 62 - 63 - checks.ns-angle-bracket-leaf = checkCond "angle-bracket access leaf" ( 64 - <ns/root/branch/leaf> == ns.root._.branch._.leaf 65 - ); 66 - 67 - checks.ns-tree-all-levels-merged = checkCond "all tree levels merged" ( 68 - vals == [ 69 - "X-branch" 70 - "X-leaf" 71 - "X-root" 72 - "Y-branch" 73 - "Y-leaf" 74 - "Y-root" 75 - "local-branch" 76 - "local-leaf" 77 - "local-root" 78 - ] 79 - ); 80 - }; 81 - }
-76
templates/examples/modules/_example/ci/base-conf-modules.nix
··· 1 - # tests for extending `den.base.*` modules with capabilities (options). 2 - # these allow you to expend all hosts/users/homes with custom option 3 - # that can later be used by aspects for providing features. 4 - # 5 - { lib, ... }: 6 - { 7 - 8 - # This module is base for all host configs. 9 - den.base.host = 10 - { host, ... }: 11 - { 12 - options.capabilities.ssh-server = lib.mkEnableOption "Does host ${host.name} provide ssh?"; 13 - }; 14 - 15 - # This module is base for all user configs. 16 - den.base.user = 17 - { user, ... }: 18 - { 19 - options.isAdmin = lib.mkOption { 20 - type = lib.types.bool; 21 - default = user.name == "alice"; # only alice is always admin 22 - }; 23 - }; 24 - 25 - # This module is base for all home configs. 26 - # den.base.home = { home, ... }: { }; 27 - 28 - # This one is included on each host/user/home 29 - # it cannot access host/user/home values since this conf is generic. 30 - den.base.conf = { 31 - options.foo = lib.mkOption { 32 - type = lib.types.str; 33 - default = "bar"; 34 - }; 35 - }; 36 - 37 - # Now hosts and users can set any option defined in base modules. 38 - den.hosts.x86_64-linux.rockhopper = { 39 - capabilities.ssh-server = true; 40 - foo = "boo"; 41 - users.alice = { 42 - # isAdmin = true; # alice is admin by default, nothing explicit here. 43 - foo = "moo"; 44 - }; 45 - }; 46 - 47 - den.aspects.rockhopper.includes = 48 - let 49 - # An aspect can make use of these options to provide configuration. 50 - sshCapable = 51 - { host, ... }: 52 - { 53 - nixos.services.sshd.enable = host.capabilities.ssh-server; 54 - homeManager.services.ssh-agent.enable = host.capabilities.ssh-server; 55 - }; 56 - in 57 - [ sshCapable ]; 58 - 59 - # CI checks 60 - perSystem = 61 - { 62 - checkCond, 63 - rockhopper, 64 - alice-at-rockhopper, 65 - ... 66 - }: 67 - { 68 - checks.host-conf-rockhopper-sshd = checkCond "sshd enabled" ( 69 - rockhopper.config.services.sshd.enable == true 70 - ); 71 - checks.host-conf-alice-sshd = checkCond "ssh-agent enabled" ( 72 - alice-at-rockhopper.services.ssh-agent.enable == true 73 - ); 74 - }; 75 - 76 - }
-27
templates/examples/modules/_example/ci/builds.nix
··· 1 - # Adds some checks for CI 2 - { 3 - perSystem = 4 - { 5 - pkgs, 6 - checkFile, 7 - rockhopper, 8 - honeycrisp, 9 - cam, 10 - bob, 11 - ... 12 - }: 13 - let 14 - checks.x86_64-linux = { 15 - vm = checkFile "vm-builds" "${rockhopper.config.system.build.vm}/bin/run-rockhopper-vm"; 16 - hosts-rockhopper = checkFile "nixos-builds" rockhopper.config.system.build.toplevel; 17 - homes-cam = checkFile "home-builds" cam.activation-script; 18 - }; 19 - checks.aarch64-darwin = { 20 - hosts-honeycrisp = checkFile "darwin-builds" honeycrisp.config.system.build.toplevel; 21 - homes-bob = checkFile "darwin-home-builds" bob.activation-script; 22 - }; 23 - in 24 - { 25 - checks = checks.${pkgs.stdenv.hostPlatform.system} or { }; 26 - }; 27 - }
-67
templates/examples/modules/_example/ci/cross-flake-parametric.nix
··· 1 - { 2 - inputs, 3 - lib, 4 - provider, 5 - ... 6 - }: 7 - let 8 - providerModule.nixos.options.providerVars = lib.mkOption { 9 - type = lib.types.attrsOf lib.types.str; 10 - default = { }; 11 - }; 12 - in 13 - { 14 - flake-file.inputs.provider = { 15 - url = "path:./provider"; 16 - inputs.den.follows = "den"; 17 - inputs.flake-aspects.follows = "flake-aspects"; 18 - inputs.flake-parts.follows = "flake-parts"; 19 - inputs.import-tree.follows = "import-tree"; 20 - inputs.nixpkgs.follows = "nixpkgs"; 21 - }; 22 - 23 - imports = [ 24 - (inputs.den.namespace "provider" [ 25 - true 26 - inputs.provider 27 - ]) 28 - ]; 29 - 30 - provider.tools._.dev._.editors = { 31 - nixos.providerVars.LOCAL_EDITOR = "emacs"; 32 - }; 33 - 34 - den.aspects.rockhopper.includes = [ 35 - providerModule 36 - provider.tools._.dev._.editors 37 - provider.tools._.dev._.shells 38 - ]; 39 - 40 - perSystem = 41 - { checkCond, rockhopper, ... }: 42 - let 43 - vars = rockhopper.config.providerVars; 44 - env = rockhopper.config.environment.variables; 45 - in 46 - { 47 - checks.cross-flake-provider-editor = checkCond "provider editor var set" ( 48 - env.PROVIDER_EDITOR == "vim" 49 - ); 50 - 51 - checks.cross-flake-provider-shell = checkCond "provider shell var set" ( 52 - env.PROVIDER_SHELL == "fish" 53 - ); 54 - 55 - checks.cross-flake-local-editor = checkCond "local editor var set" (vars.LOCAL_EDITOR == "emacs"); 56 - 57 - checks.cross-flake-namespace-merged = checkCond "namespace merged from provider input" ( 58 - provider.tools._.dev._.editors == inputs.self.denful.provider.tools._.dev._.editors 59 - && provider.tools._.dev._.shells == inputs.self.denful.provider.tools._.dev._.shells 60 - ); 61 - 62 - checks.cross-flake-input-denful = checkCond "input provider denful accessible" ( 63 - inputs.provider.denful.provider.tools._.dev._.editors.description 64 - == "Editor configurations from provider flake" 65 - ); 66 - }; 67 - }
-30
templates/examples/modules/_example/ci/custom-home-managed-package.nix
··· 1 - { 2 - # Including an static aspect should not cause duplicate definitions 3 - den.aspects.alice.includes = [ 4 - { 5 - homeManager = 6 - { pkgs, ... }: 7 - { 8 - programs.emacs.enable = true; 9 - programs.emacs.package = pkgs.emacs30-nox; 10 - }; 11 - } 12 - ]; 13 - 14 - perSystem = 15 - { 16 - checkCond, 17 - alice-at-rockhopper, 18 - lib, 19 - ... 20 - }: 21 - { 22 - checks.alice-custom-emacs = checkCond "set uniquely via a static includes" ( 23 - let 24 - expr = lib.getName alice-at-rockhopper.programs.emacs.package; 25 - expected = "emacs-nox"; 26 - in 27 - expr == expected 28 - ); 29 - }; 30 - }
-35
templates/examples/modules/_example/ci/custom-nixos-module.nix
··· 1 - { lib, ... }: 2 - let 3 - # A custom `nixos` class module that defines an option `names`. 4 - # Used to test that we are not duplicating values from owned configs. 5 - nixosNames = names: { options.${names} = lib.mkOption { type = lib.types.listOf lib.types.str; }; }; 6 - in 7 - { 8 - den.default.nixos.imports = [ (nixosNames "people") ]; 9 - den.default.includes = [ 10 - ( 11 - { user, ... }: 12 - { 13 - nixos.people = [ user.name ]; 14 - } 15 - ) 16 - ]; 17 - 18 - den.aspects.rockhopper.includes = [ 19 - # Example: importing a third-party nixos module. 20 - { nixos.imports = [ (nixosNames "names") ]; } 21 - ]; 22 - 23 - den.aspects.rockhopper.nixos.names = [ "tux" ]; 24 - 25 - perSystem = 26 - { checkCond, rockhopper, ... }: 27 - { 28 - checks.rockhopper-default-people = checkCond "set from den.default for each user" ( 29 - rockhopper.config.people == [ "alice" ] 30 - ); 31 - checks.rockhopper-names-single-entry = checkCond "custom nixos array option set once" ( 32 - rockhopper.config.names == [ "tux" ] 33 - ); 34 - }; 35 - }
-78
templates/examples/modules/_example/ci/deep-nested-namespace.nix
··· 1 - { 2 - inputs, 3 - lib, 4 - deep, 5 - ... 6 - }: 7 - let 8 - deepModule.nixos.options.deepVals = lib.mkOption { 9 - type = lib.types.listOf lib.types.str; 10 - }; 11 - depthA = { 12 - denful.deep.a._.b._.c._.d._.e = { 13 - description = "5 levels deep from inputA"; 14 - nixos.deepVals = [ "inputA-5-levels" ]; 15 - provides.f.nixos.deepVals = [ "inputA-6-levels" ]; 16 - }; 17 - }; 18 - depthB = { 19 - denful.deep.a._.b._.c._.d._.e = { 20 - nixos.deepVals = [ "inputB-5-levels" ]; 21 - provides.f.nixos.deepVals = [ "inputB-6-levels" ]; 22 - }; 23 - }; 24 - in 25 - { 26 - imports = [ 27 - (inputs.den.namespace "deep" [ 28 - true 29 - depthA 30 - depthB 31 - ]) 32 - ]; 33 - 34 - deep.a._.b._.c._.d._.e = { 35 - nixos.deepVals = [ "local-5-levels" ]; 36 - provides.f.nixos.deepVals = [ "local-6-levels" ]; 37 - }; 38 - 39 - den.aspects.rockhopper.includes = [ 40 - deepModule 41 - deep.a._.b._.c._.d._.e 42 - deep.a._.b._.c._.d._.e._.f 43 - ]; 44 - 45 - perSystem = 46 - { checkCond, rockhopper, ... }: 47 - let 48 - vals = lib.sort (a: b: a < b) rockhopper.config.deepVals; 49 - in 50 - { 51 - checks.deep-nest-5-levels = checkCond "5 levels deep merged correctly" ( 52 - builtins.elem "inputA-5-levels" vals 53 - && builtins.elem "inputB-5-levels" vals 54 - && builtins.elem "local-5-levels" vals 55 - ); 56 - 57 - checks.deep-nest-6-levels = checkCond "6 levels deep merged correctly" ( 58 - builtins.elem "inputA-6-levels" vals 59 - && builtins.elem "inputB-6-levels" vals 60 - && builtins.elem "local-6-levels" vals 61 - ); 62 - 63 - checks.deep-nest-all-merged = checkCond "all values merged" ( 64 - vals == [ 65 - "inputA-5-levels" 66 - "inputA-6-levels" 67 - "inputB-5-levels" 68 - "inputB-6-levels" 69 - "local-5-levels" 70 - "local-6-levels" 71 - ] 72 - ); 73 - 74 - checks.deep-nest-flake-output = checkCond "deep namespace as flake output" ( 75 - inputs.self.denful.deep.a._.b._.c._.d._.e._.f == deep.a._.b._.c._.d._.e._.f 76 - ); 77 - }; 78 - }
-27
templates/examples/modules/_example/ci/define-user.nix
··· 1 - { den, ... }: 2 - { 3 - den.default.includes = [ 4 - # Example: parametric over many contexts: { home }, { host, user }, { fromUser, toHost } 5 - den.provides.define-user 6 - ]; 7 - 8 - perSystem = 9 - { 10 - checkCond, 11 - rockhopper, 12 - adelie, 13 - ... 14 - }: 15 - { 16 - 17 - checks.alice-exists-on-rockhopper = checkCond "den.default.user.includes defines user on host" ( 18 - rockhopper.config.users.users.alice.isNormalUser 19 - ); 20 - checks.alice-not-exists-on-adelie = checkCond "den.default.user.includes defines user on host" ( 21 - !adelie.config.users.users ? alice 22 - ); 23 - checks.will-exists-on-adelie = checkCond "den.default.user.includes defines user on host" ( 24 - adelie.config.users.users.will.isNormalUser 25 - ); 26 - }; 27 - }
-8
templates/examples/modules/_example/ci/enable-wsl-host.nix
··· 1 - { inputs, ... }: 2 - { 3 - # Example: adelie host using github:nix-community/NixOS-WSL 4 - den.aspects.adelie.nixos = { 5 - imports = [ inputs.nixos-wsl.nixosModules.default ]; 6 - wsl.enable = true; 7 - }; 8 - }
-37
templates/examples/modules/_example/ci/helpers.nix
··· 1 - { self, ... }: 2 - { 3 - perSystem = 4 - { pkgs, ... }: 5 - let 6 - checkFile = 7 - name: file: 8 - pkgs.runCommandLocal name { } '' 9 - ls -la ${file} | tee $out 10 - ''; 11 - 12 - checkCond = 13 - name: cond: 14 - let 15 - code = if cond then "touch $out" else ''echo "Cond-Failed: ${name}"''; 16 - in 17 - pkgs.runCommandLocal name { } code; 18 - 19 - rockhopper = self.nixosConfigurations.rockhopper; 20 - honeycrisp = self.darwinConfigurations.honeycrisp; 21 - adelie = self.wslConfigurations.adelie; 22 - cam = self.homeConfigurations.cam; 23 - bob = self.homeConfigurations.bob; 24 - luke = self.homeConfigurations.luke; 25 - 26 - alice-at-rockhopper = rockhopper.config.home-manager.users.alice; 27 - alice-at-honeycrisp = honeycrisp.config.home-manager.users.alice; 28 - in 29 - { 30 - _module.args = { 31 - inherit checkCond checkFile; 32 - inherit rockhopper honeycrisp adelie; 33 - inherit cam bob luke; 34 - inherit alice-at-rockhopper alice-at-honeycrisp; 35 - }; 36 - }; 37 - }
-29
templates/examples/modules/_example/ci/hm-enabled-host.nix
··· 1 - { den, inputs, ... }: 2 - { 3 - # The `{ HM-OS-HOST }` context is activated ONLY for hosts that have 4 - # a HM supported OS and at least one user with homeManager class. 5 - den.aspects.hm-global-pkgs = 6 - { HM-OS-HOST }: 7 - den.lib.take.unused [ HM-OS-HOST.host ] # access host from context if needed 8 - { 9 - nixos.home-manager.useGlobalPkgs = true; 10 - }; 11 - 12 - den.default.includes = [ den.aspects.hm-global-pkgs ]; 13 - 14 - den.hosts.x86_64-linux.no-homes = { }; 15 - 16 - perSystem = 17 - { checkCond, rockhopper, ... }: 18 - { 19 - checks.rockhopper-hm-global-pkgs = checkCond "rockhopper-hm-global-pkgs" ( 20 - rockhopper.config.home-manager.useGlobalPkgs 21 - ); 22 - 23 - checks.no-homes-hm-global-pkgs = checkCond "no-homes-hm-global-pkgs" ( 24 - # no home-manager enabled nor useGlobalPkgs 25 - !inputs.self.nixosConfigurations.no-homes.config ? home-manager.useGlobalPkgs 26 - ); 27 - }; 28 - 29 - }
-13
templates/examples/modules/_example/ci/host-configures-users.nix
··· 1 - { 2 - 3 - # Example: host provides static config to all its users hm. 4 - den.aspects.rockhopper.homeManager.programs.direnv.enable = true; 5 - 6 - perSystem = 7 - { checkCond, alice-at-rockhopper, ... }: 8 - { 9 - checks.host-contributes-to-user = checkCond "rockhopper contributes to all its users" ( 10 - alice-at-rockhopper.programs.direnv.enable 11 - ); 12 - }; 13 - }
-42
templates/examples/modules/_example/ci/host-user-conditional-hm.nix
··· 1 - { lib, ... }: 2 - let 3 - # Example: configuration that depends on both host and user. provides only to HM. 4 - host-to-user-conditional = 5 - { 6 - user, 7 - host, 8 - ... 9 - }: 10 - if user.userName == "alice" && !lib.hasSuffix "darwin" host.system then 11 - { 12 - homeManager.programs.git.enable = true; 13 - } 14 - else 15 - { }; 16 - in 17 - { 18 - 19 - den.aspects.rockhopper.includes = [ 20 - # Example: host provides parametric user configuration. 21 - host-to-user-conditional 22 - ]; 23 - 24 - perSystem = 25 - { 26 - checkCond, 27 - alice-at-rockhopper, 28 - alice-at-honeycrisp, 29 - ... 30 - }: 31 - { 32 - 33 - checks.alice-hm-git-enabled-on = checkCond "home-managed git for alice at rockhopper" ( 34 - alice-at-rockhopper.programs.git.enable 35 - ); 36 - checks.alice-hm-git-enabled-off = checkCond "home-managed git for alice at honeycrisp" ( 37 - !alice-at-honeycrisp.programs.git.enable 38 - ); 39 - 40 - }; 41 - 42 - }
-28
templates/examples/modules/_example/ci/import-tree.nix
··· 1 - # configures class-automatic module auto imports for hosts/users/homes. 2 - # See documentation at modules/aspects/provides/import-tree.nix 3 - { 4 - # deadnix: skip 5 - __findFile ? __findFile, 6 - ... 7 - }: 8 - { 9 - 10 - # alice imports non-dendritic <class> modules from _non_dendritic/alice/_<class>/*.nix 11 - den.aspects.alice.includes = [ (<den/import-tree> ./_non_dendritic/alice) ]; 12 - 13 - # See the documentation at batteries/import-tree.nix 14 - den.default.includes = [ 15 - (<den/import-tree/host> ./_non_dendritic/hosts) 16 - (<den/import-tree/user> ./_non_dendritic/users) 17 - (<den/import-tree/home> ./_non_dendritic/homes) 18 - ]; 19 - 20 - # tests 21 - perSystem = 22 - { checkCond, rockhopper, ... }: 23 - { 24 - checks.import-tree = checkCond "auto-imported from rockhopper/_nixos" ( 25 - rockhopper.config.auto-imported 26 - ); 27 - }; 28 - }
-81
templates/examples/modules/_example/ci/namespace.nix
··· 1 - { 2 - inputs, 3 - den, 4 - lib, 5 - eg, 6 - # deadnix: skip 7 - __findFile ? __findFile, 8 - ... 9 - }: 10 - let 11 - # module for testing inclusion of namespaces 12 - simsModule.nixos.options.sims = lib.mkOption { 13 - type = lib.types.listOf lib.types.str; 14 - }; 15 - in 16 - { 17 - # enable <angle/bracket> syntax for finding aspects. 18 - _module.args.__findFile = den.lib.__findFile; 19 - 20 - imports = [ 21 - # create a local namespace and output at flake.denful.eg 22 - (inputs.den.namespace "eg" true) 23 - 24 - # you can also mount a namespace from many input sources. 25 - # the second argument becomes an array of inputs. 26 - ( 27 - let 28 - # NOTE: here we simulate inputA and inputB are flakes. 29 - inputA.denful.sim.ul._.a._.tion.nixos.sims = [ "inputA simulation" ]; 30 - inputB.denful.sim.ul._.a._.tion.nixos.sims = [ "inputB simulation" ]; 31 - exposeToFlake = true; 32 - in 33 - inputs.den.namespace "sim" [ 34 - inputA 35 - inputB 36 - exposeToFlake 37 - ] 38 - ) 39 - ]; 40 - 41 - # define nested aspects in local namespace 42 - eg.foo.provides.bar.provides.baz = { 43 - nixos.sims = [ "local namespace" ]; 44 - }; 45 - 46 - # augment aspects on a mounted namespace 47 - sim.ul._.a._.tion.nixos.sims = [ "local simulation" ]; 48 - 49 - den.aspects.rockhopper.includes = [ 50 - simsModule 51 - <eg/foo/bar/baz> 52 - <sim/ul/a/tion> 53 - ]; 54 - 55 - perSystem = 56 - { checkCond, rockhopper, ... }: 57 - { 58 - checks.namespace-eg-flake-output = checkCond "namespace enabled as flake output" ( 59 - eg == den.ful.eg && eg == <eg> && eg == inputs.self.denful.eg 60 - ); 61 - 62 - checks.namespace-eg-provides-accessible = checkCond "exact same value" ( 63 - eg.foo._.bar._.baz == <eg/foo/bar/baz> 64 - && eg.foo._.bar._.baz == inputs.self.denful.eg.foo._.bar._.baz 65 - ); 66 - 67 - checks.namespace-sim-merged = checkCond "merges from all sources" ( 68 - let 69 - expected = [ 70 - "inputA simulation" 71 - "inputB simulation" 72 - "local namespace" 73 - "local simulation" 74 - ]; 75 - actual = lib.sort (a: b: a < b) rockhopper.config.sims; 76 - in 77 - expected == actual 78 - ); 79 - 80 - }; 81 - }
-41
templates/examples/modules/_example/ci/one-os-package-per-user.nix
··· 1 - let 2 - 3 - # Example: adds hello into each user. provides only to OS. 4 - hello-package-for-user = 5 - { 6 - user, 7 - host, 8 - ... 9 - }: 10 - { 11 - ${host.class} = 12 - { pkgs, ... }: 13 - { 14 - users.users.${user.userName}.packages = [ pkgs.hello ]; 15 - }; 16 - }; 17 - 18 - in 19 - { 20 - 21 - den.default.includes = [ hello-package-for-user ]; 22 - 23 - perSystem = 24 - { 25 - checkCond, 26 - rockhopper, 27 - lib, 28 - ... 29 - }: 30 - { 31 - checks.alice-hello-enabled-by-default = checkCond "added hello at user packages" ( 32 - let 33 - progs = rockhopper.config.users.users.alice.packages; 34 - expr = map lib.getName progs; 35 - expected = [ "hello" ]; 36 - in 37 - expr == expected 38 - ); 39 - }; 40 - 41 - }
-145
templates/examples/modules/_example/ci/parametric-with-owned.nix
··· 1 - { 2 - den, 3 - lib, 4 - ... 5 - }: 6 - let 7 - # a test module to check context was forwarded 8 - fwdModule.options.fwd = { 9 - a = strOpt; 10 - b = strOpt; 11 - c = strOpt; 12 - d = strOpt; 13 - e = strOpt; 14 - f = strOpt; 15 - # unlike strings, pkgs cannot be duplicated/merged, we use this to 16 - # ensure no-dups are created from parametric owned modules. 17 - pkg = pkgOpt; 18 - pkg2 = pkgOpt; 19 - pkg3 = pkgOpt; 20 - }; 21 - strOpt = lib.mkOption { type = lib.types.str; }; 22 - pkgOpt = lib.mkOption { type = lib.types.package; }; 23 - 24 - inherit (den.lib) parametric; 25 - in 26 - { 27 - den.aspects.rockhopper.includes = [ 28 - { nixos.imports = [ fwdModule ]; } 29 - { homeManager.imports = [ fwdModule ]; } 30 - den.aspects.fwd._.first 31 - ]; 32 - den.aspects.rockhopper.nixos.fwd.c = "host owned C"; 33 - den.aspects.rockhopper.homeManager.fwd.a = "host home-managed A"; 34 - 35 - # this aspect will take any context and also forward it 36 - # into any includes function that can take same context. 37 - den.aspects.fwd._.first = parametric { 38 - nixos = 39 - { pkgs, ... }: 40 - { 41 - fwd.a = "First owned A"; 42 - fwd.pkg = pkgs.hello; 43 - }; 44 - homeManager = 45 - { pkgs, ... }: 46 - { 47 - fwd.pkg = pkgs.vim; 48 - }; 49 - includes = [ 50 - den.aspects.fwd._.second 51 - { nixos.fwd.d = "First static includes D"; } 52 - den.aspects.fwd._.never 53 - den.aspects.fwd._.fourth 54 - ]; 55 - }; 56 - 57 - # Note that second has named arguments, while first does not. 58 - # the first aspect forwards whatever context it receives. 59 - den.aspects.fwd._.second = 60 - { host, ... }: 61 - parametric.fixedTo { third = "Impact"; } { 62 - includes = [ den.aspects.fwd._.third ]; 63 - nixos = 64 - { pkgs, ... }: 65 - { 66 - fwd.b = "Second owned B for ${host.name}"; 67 - fwd.pkg2 = pkgs.bat; 68 - }; 69 - homeManager = 70 - { pkgs, ... }: 71 - { 72 - fwd.pkg2 = pkgs.helix; 73 - }; 74 - }; 75 - 76 - den.aspects.fwd._.third = 77 - { third, ... }: 78 - { 79 - nixos.fwd.e = "Third ${third}"; 80 - }; 81 - 82 - den.aspects.fwd._.fourth = parametric.expands { planet = "Earth"; } { 83 - includes = [ den.aspects.fwd._.fifth ]; 84 - nixos = 85 - { pkgs, ... }: 86 - { 87 - fwd.pkg3 = pkgs.emacs-nox; 88 - }; 89 - homeManager = 90 - { pkgs, ... }: 91 - { 92 - fwd.pkg3 = pkgs.emacs-nox; 93 - }; 94 - }; 95 - 96 - den.aspects.fwd._.fifth = 97 - { host, planet, ... }: 98 - { 99 - nixos.fwd.f = "Fifth ${planet} ${host.name}"; 100 - }; 101 - 102 - den.aspects.fwd._.never = 103 - { never-matches }: 104 - { 105 - nixos.fwd.a = "Imposibru! should never be included ${never-matches}"; 106 - }; 107 - 108 - perSystem = 109 - { 110 - checkCond, 111 - rockhopper, 112 - alice-at-rockhopper, 113 - ... 114 - }: 115 - { 116 - checks.parametric-fwd-a = checkCond "fwd-a" (rockhopper.config.fwd.a == "First owned A"); 117 - checks.parametric-fwd-b = checkCond "fwd-b" ( 118 - rockhopper.config.fwd.b == "Second owned B for rockhopper" 119 - ); 120 - checks.parametric-fwd-c = checkCond "fwd-c" (rockhopper.config.fwd.c == "host owned C"); 121 - checks.parametric-fwd-d = checkCond "fwd-d" (rockhopper.config.fwd.d == "First static includes D"); 122 - checks.parametric-fwd-e = checkCond "fwd-e" (rockhopper.config.fwd.e == "Third Impact"); 123 - checks.parametric-fwd-f = checkCond "fwd-f" (rockhopper.config.fwd.f == "Fifth Earth rockhopper"); 124 - 125 - checks.parametric-fwd-pkg = checkCond "fwd-pkg" (lib.getName rockhopper.config.fwd.pkg == "hello"); 126 - checks.parametric-fwd-pkg2 = checkCond "fwd-pkg2" (lib.getName rockhopper.config.fwd.pkg2 == "bat"); 127 - checks.parametric-fwd-pkg3 = checkCond "fwd-pkg3" ( 128 - lib.getName rockhopper.config.fwd.pkg3 == "emacs-nox" 129 - ); 130 - 131 - checks.parametric-fwd-hm-a = checkCond "fwd-hm-a" ( 132 - alice-at-rockhopper.fwd.a == "host home-managed A" 133 - ); 134 - checks.parametric-fwd-hm-pkg = checkCond "fwd-hm-pkg" ( 135 - lib.getName alice-at-rockhopper.fwd.pkg == "vim" 136 - ); 137 - checks.parametric-fwd-hm-pkg2 = checkCond "fwd-hm-pkg2" ( 138 - lib.getName alice-at-rockhopper.fwd.pkg2 == "helix" 139 - ); 140 - checks.parametric-fwd-hm-pkg3 = checkCond "fwd-hm-pkg3" ( 141 - lib.getName alice-at-rockhopper.fwd.pkg3 == "emacs-nox" 142 - ); 143 - }; 144 - 145 - }
-29
templates/examples/modules/_example/ci/primary-user.nix
··· 1 - { den, ... }: 2 - { 3 - den.aspects.alice.includes = [ 4 - # alice is always admin in all its hosts 5 - den._.primary-user 6 - ]; 7 - 8 - den.aspects.will.includes = [ 9 - # will is primary user in WSL NixOS. 10 - den._.primary-user 11 - ]; 12 - 13 - perSystem = 14 - { 15 - checkCond, 16 - honeycrisp, 17 - adelie, 18 - ... 19 - }: 20 - { 21 - checks.alice-primary-on-macos = checkCond "den._.primary-user sets macos primary" ( 22 - honeycrisp.config.system.primaryUser == "alice" 23 - ); 24 - 25 - checks.will-is-wsl-default = checkCond "wsl.defaultUser defined" ( 26 - adelie.config.wsl.defaultUser == "will" 27 - ); 28 - }; 29 - }
-29
templates/examples/modules/_example/ci/set-hostname.nix
··· 1 - { 2 - den.default.includes = 3 - let 4 - # Example: parametric host aspect to automatically set hostName on any host. 5 - set-host-name = 6 - { host, ... }: 7 - { 8 - ${host.class}.networking.hostName = host.name; 9 - }; 10 - in 11 - [ set-host-name ]; 12 - 13 - perSystem = 14 - { 15 - checkCond, 16 - rockhopper, 17 - honeycrisp, 18 - ... 19 - }: 20 - { 21 - checks.rockhopper-hostname = checkCond "den.default.host.includes sets hostName" ( 22 - rockhopper.config.networking.hostName == "rockhopper" 23 - ); 24 - 25 - checks.honeycrisp-hostname = checkCond "den.default.host.includes sets hostName" ( 26 - honeycrisp.config.networking.hostName == "honeycrisp" 27 - ); 28 - }; 29 - }
-53
templates/examples/modules/_example/ci/shared-parametric-aspects.nix
··· 1 - { 2 - inputs, 3 - lib, 4 - shared, 5 - ... 6 - }: 7 - let 8 - dataModule.nixos.options.data = lib.mkOption { 9 - type = lib.types.listOf lib.types.str; 10 - }; 11 - mkInputFlake = name: { 12 - denful.shared.gaming._.retro._.sega = { 13 - nixos.data = [ "${name}-sega-static" ]; 14 - }; 15 - }; 16 - inputFoo = mkInputFlake "foo"; 17 - inputBar = mkInputFlake "bar"; 18 - in 19 - { 20 - imports = [ 21 - (inputs.den.namespace "shared" [ 22 - true 23 - inputFoo 24 - inputBar 25 - ]) 26 - ]; 27 - 28 - shared.gaming._.retro._.sega.nixos.data = [ "local-sega-static" ]; 29 - 30 - den.aspects.rockhopper.includes = [ 31 - dataModule 32 - shared.gaming._.retro._.sega 33 - ]; 34 - 35 - perSystem = 36 - { checkCond, rockhopper, ... }: 37 - let 38 - vals = lib.sort (a: b: a < b) rockhopper.config.data; 39 - in 40 - { 41 - checks.shared-parametric-all-merged = checkCond "all parametric merged" ( 42 - vals == [ 43 - "bar-sega-static" 44 - "foo-sega-static" 45 - "local-sega-static" 46 - ] 47 - ); 48 - 49 - checks.shared-flake-output-matches = checkCond "flake output matches" ( 50 - shared.gaming._.retro._.sega == inputs.self.denful.shared.gaming._.retro._.sega 51 - ); 52 - }; 53 - }
-70
templates/examples/modules/_example/ci/special-args.nix
··· 1 - { den, withSystem, ... }: 2 - let 3 - testModule = 4 - { 5 - inputs', 6 - lib, 7 - self', 8 - ... 9 - }: 10 - { 11 - options.specialArgsTest = { 12 - test-package = lib.mkOption { type = lib.types.package; }; 13 - neovim-package = lib.mkOption { type = lib.types.package; }; 14 - }; 15 - 16 - config.specialArgsTest = { 17 - test-package = self'.packages.hello; 18 - neovim-package = inputs'.neovim-nightly-overlay.packages.neovim; 19 - }; 20 - }; 21 - in 22 - { 23 - flake-file.inputs.neovim-nightly-overlay = { 24 - url = "github:nix-community/neovim-nightly-overlay"; 25 - inputs.nixpkgs.follows = "nixpkgs"; 26 - inputs.flake-parts.follows = "flake-parts"; 27 - }; 28 - 29 - den.default.includes = [ 30 - den._.self' 31 - den._.inputs' 32 - ]; 33 - 34 - den.aspects.rockhopper.nixos.imports = [ testModule ]; 35 - den.aspects.cam.homeManager.imports = [ testModule ]; 36 - 37 - flake.checks.x86_64-linux = withSystem "x86_64-linux" ( 38 - { 39 - checkCond, 40 - rockhopper, 41 - cam, 42 - self', 43 - inputs', 44 - ... 45 - }: 46 - { 47 - special-args-self-nixos = checkCond "self' provides same package to nixos" ( 48 - rockhopper.config.specialArgsTest.test-package == self'.packages.hello 49 - ); 50 - 51 - special-args-inputs-nixos = checkCond "inputs' provides same package to nixos" ( 52 - rockhopper.config.specialArgsTest.neovim-package == inputs'.neovim-nightly-overlay.packages.neovim 53 - ); 54 - 55 - special-args-self-hm = checkCond "self' provides same package to home-manager" ( 56 - cam.config.specialArgsTest.test-package == self'.packages.hello 57 - ); 58 - 59 - special-args-inputs-hm = checkCond "inputs' provides same package to home-manager" ( 60 - cam.config.specialArgsTest.neovim-package == inputs'.neovim-nightly-overlay.packages.neovim 61 - ); 62 - } 63 - ); 64 - 65 - perSystem = 66 - { pkgs, ... }: 67 - { 68 - packages.hello = pkgs.hello; 69 - }; 70 - }
-31
templates/examples/modules/_example/ci/standalone-hm-special-args-osconfig.nix
··· 1 - let 2 - 3 - # Example: luke standalone home-manager has access to rockhopper osConfig specialArg. 4 - os-conditional-hm = 5 - { home, ... }: 6 - { 7 - # access osConfig, wired via extraSpecialArgs in homes.nix. 8 - homeManager = 9 - { osConfig, ... }: 10 - { 11 - programs.bat.enable = osConfig.programs.${home.programToDependOn}.enable; 12 - }; 13 - }; 14 - in 15 - { 16 - 17 - # Example: standalone-hm config depends on osConfig (non-recursive) 18 - # NOTE: this will only work for standalone hm, and not for hosted hm 19 - # since a hosted hm configuration cannot depend on the os configuration. 20 - den.aspects.luke.includes = [ 21 - os-conditional-hm 22 - ]; 23 - 24 - perSystem = 25 - { checkCond, luke, ... }: 26 - { 27 - checks.luke-hm-depends-on-osConfig = checkCond "standalone hm can depend on osConfig" ( 28 - luke.config.programs.bat.enable 29 - ); 30 - }; 31 - }
-50
templates/examples/modules/_example/ci/top-level-parametric.nix
··· 1 - # it is possible for top-level aspects directly under 2 - # den.aspects to take a context argument. 3 - { den, lib, ... }: 4 - let 5 - # A module to test that toplevel had context. 6 - topLevel = name: { 7 - config.tops = name; 8 - options.tops = lib.mkOption { type = lib.types.str; }; 9 - }; 10 - in 11 - { 12 - 13 - den.aspects.toplevel-user = 14 - { user, ... }: 15 - { 16 - nixos.imports = [ (topLevel user.name) ]; 17 - }; 18 - 19 - den.aspects.toplevel-host = 20 - { host, ... }: 21 - { 22 - homeManager.imports = [ (topLevel host.name) ]; 23 - }; 24 - 25 - den.aspects.rockhopper.includes = [ 26 - den.aspects.toplevel-host 27 - ]; 28 - 29 - den.aspects.alice.includes = [ 30 - den.aspects.toplevel-user 31 - ]; 32 - 33 - perSystem = 34 - { 35 - checkCond, 36 - alice-at-rockhopper, 37 - rockhopper, 38 - ... 39 - }: 40 - { 41 - checks.alice-toplevel-user = checkCond "alice toplevel param aspect" ( 42 - rockhopper.config.tops == "alice" 43 - ); 44 - 45 - checks.alice-toplevel-host = checkCond "alice toplevel param aspect" ( 46 - alice-at-rockhopper.tops == "rockhopper" 47 - ); 48 - }; 49 - 50 - }
-25
templates/examples/modules/_example/ci/unfree.nix
··· 1 - { den, ... }: 2 - 3 - let 4 - codeAspect = { 5 - includes = [ (den._.unfree [ "vscode" ]) ]; 6 - homeManager.programs.vscode.enable = true; 7 - }; 8 - discordAspect = { 9 - includes = [ 10 - (den._.unfree [ "discord" ]) 11 - ]; 12 - homeManager = 13 - { pkgs, ... }: 14 - { 15 - home.packages = [ pkgs.discord ]; 16 - }; 17 - }; 18 - in 19 - { 20 - # cam uses unfree vscode and discord loaded from different aspects. 21 - den.aspects.cam.includes = [ 22 - codeAspect 23 - discordAspect 24 - ]; 25 - }
-12
templates/examples/modules/_example/ci/user-configures-hosts.nix
··· 1 - { 2 - # Example: user provides static config to all its nixos hosts. 3 - den.aspects.alice.nixos.users.users.alice.description = "Alice Q. User"; 4 - 5 - perSystem = 6 - { checkCond, rockhopper, ... }: 7 - { 8 - checks.user-contributes-to-host = checkCond "alice.nixos sets on rockhopper host" ( 9 - rockhopper.config.users.users.alice.description == "Alice Q. User" 10 - ); 11 - }; 12 - }
-12
templates/examples/modules/_example/ci/user-home-defaults.nix
··· 1 - { 2 - # globally enable fish on all homes ever. 3 - den.default.homeManager.programs.fish.enable = true; 4 - 5 - perSystem = 6 - { checkCond, alice-at-rockhopper, ... }: 7 - { 8 - checks.alice-hm-fish-enabled-by-default = checkCond "home-managed fish for alice" ( 9 - alice-at-rockhopper.programs.fish.enable 10 - ); 11 - }; 12 - }
-38
templates/examples/modules/_example/ci/user-host-conditional-os.nix
··· 1 - { lib, ... }: 2 - let 3 - 4 - # Example: configuration that depends on both host and user. provides anytime { user, host } is in context. 5 - user-to-host-conditional = 6 - { user, host, ... }: 7 - if user.userName == "alice" && !lib.hasSuffix "darwin" host.system then 8 - { 9 - nixos.programs.tmux.enable = true; 10 - } 11 - else 12 - { }; 13 - in 14 - { 15 - 16 - # Example: user provides parametric host configuration. 17 - den.aspects.alice.includes = [ 18 - user-to-host-conditional 19 - ]; 20 - 21 - perSystem = 22 - { 23 - checkCond, 24 - rockhopper, 25 - honeycrisp, 26 - ... 27 - }: 28 - { 29 - checks.alice-os-tmux-enabled-on = checkCond "os tmux for hosts having alice" ( 30 - rockhopper.config.programs.tmux.enable 31 - ); 32 - checks.alice-os-tmux-enabled-off = checkCond "os tmux for hosts having alice" ( 33 - !honeycrisp.config.programs.tmux.enable 34 - ); 35 - 36 - }; 37 - 38 - }
-17
templates/examples/modules/_example/ci/user-shell.nix
··· 1 - { den, lib, ... }: 2 - { 3 - 4 - den.aspects.will.includes = [ 5 - # will has always loved red snappers 6 - (den._.user-shell "fish") 7 - ]; 8 - 9 - perSystem = 10 - { checkCond, adelie, ... }: 11 - { 12 - checks.will-always-love-you = checkCond "red-snapper fish is default shell" ( 13 - "fish" == lib.getName adelie.config.users.users.will.shell 14 - ); 15 - }; 16 - 17 - }
-13
templates/examples/modules/_example/ci/user-specific-hm-config.nix
··· 1 - { 2 - # Example: enable helix for alice on all its home-managed hosts. 3 - den.aspects.alice.homeManager.programs.helix.enable = true; 4 - 5 - perSystem = 6 - { checkCond, alice-at-rockhopper, ... }: 7 - { 8 - checks.alice-hm-helix-enabled-by-user = checkCond "home-managed helix for alice" ( 9 - alice-at-rockhopper.programs.helix.enable 10 - ); 11 - }; 12 - 13 - }
-16
templates/examples/modules/_example/ci/vm-bootable.nix
··· 1 - let 2 - # Example: A static aspect for vm installers. 3 - vm-bootable = { 4 - nixos = 5 - { modulesPath, ... }: 6 - { 7 - imports = [ (modulesPath + "/installer/cd-dvd/installation-cd-minimal.nix") ]; 8 - }; 9 - }; 10 - in 11 - { 12 - den.default.includes = [ 13 - # Example: static aspect 14 - vm-bootable 15 - ]; 16 - }
-14
templates/examples/modules/_example/defaults.nix
··· 1 - # User TODO: Remove this file. 2 - { 3 - # default aspect can be used for global static settings. 4 - den.default = { 5 - # static values. 6 - darwin.system.stateVersion = 6; 7 - nixos.system.stateVersion = "25.05"; 8 - homeManager.home.stateVersion = "25.05"; 9 - 10 - # these defaults are set for checking with CI. 11 - nixos.programs.vim.enable = true; 12 - darwin.programs.zsh.enable = true; 13 - }; 14 - }
-12
templates/examples/modules/_example/home-managed.nix
··· 1 - { den, ... }: 2 - { 3 - # see batteries/home-manager.nix 4 - den.default.includes = [ den._.home-manager ]; 5 - 6 - # enable home-manager dependency. 7 - flake-file.inputs.home-manager = { 8 - url = "github:nix-community/home-manager"; 9 - inputs.nixpkgs.follows = "nixpkgs"; 10 - }; 11 - 12 - }
-31
templates/examples/modules/_example/homes.nix
··· 1 - # Example standalone home-manager configurations. 2 - # These are independent of any host configuration. 3 - # See documentation at <den>/nix/types.nix 4 - { inputs, ... }: 5 - { 6 - den.homes.x86_64-linux.cam = { }; 7 - 8 - den.homes.aarch64-darwin.bob = { 9 - userName = "robert"; 10 - aspect = "developer"; 11 - }; 12 - 13 - # Example: custom home-manager instantiate for passing extraSpecialArgs. 14 - den.homes.x86_64-linux.luke = 15 - let 16 - osConfig = inputs.self.nixosConfigurations.rockhopper.config; 17 - in 18 - { 19 - # Example: luke standalone-homemanager needs access to rockhopper osConfig. 20 - instantiate = 21 - { pkgs, modules }: 22 - inputs.home-manager.lib.homeManagerConfiguration { 23 - inherit pkgs modules; 24 - extraSpecialArgs.osConfig = osConfig; 25 - }; 26 - 27 - # Example: custom attribute instead of specialArgs 28 - programToDependOn = "vim"; 29 - }; 30 - 31 - }
-50
templates/examples/modules/_example/hosts.nix
··· 1 - # This is a fully working example configuration. 2 - # Feel free to remove it, adapt or split into several modules. 3 - # See documentation at <den>/nix/types.nix 4 - { inputs, ... }: 5 - { 6 - den.hosts.aarch64-darwin.honeycrisp.users.alice = { }; 7 - 8 - den.hosts.aarch64-linux.emperor.users.alice = { }; 9 - 10 - den.hosts.x86_64-linux = { 11 - rockhopper = { 12 - description = "rockhopper is a kind of penguin"; 13 - users.alice = { }; 14 - }; 15 - 16 - adelie = { 17 - description = "wsl on windows"; 18 - users.will = { }; 19 - intoAttr = "wslConfigurations"; 20 - # custom nixpkgs channel. 21 - instantiate = inputs.nixpkgs-stable.lib.nixosSystem; 22 - # custom attribute (see home-manager.nix) 23 - hm-module = inputs.home-manager-stable.nixosModules.home-manager; 24 - # custom attribute (see primary-user.nix) 25 - wsl = { }; 26 - }; 27 - }; 28 - 29 - # move these inputs to any module you want. 30 - # they are here for all our examples to work on CI. 31 - flake-file.inputs = { 32 - 33 - # these stable inputs are for wsl 34 - nixpkgs-stable.url = "github:nixos/nixpkgs/release-25.05"; 35 - home-manager-stable.url = "github:nix-community/home-manager/release-25.05"; 36 - home-manager-stable.inputs.nixpkgs.follows = "nixpkgs-stable"; 37 - 38 - nixos-wsl = { 39 - url = "github:nix-community/nixos-wsl"; 40 - inputs.nixpkgs.follows = "nixpkgs-stable"; 41 - inputs.flake-compat.follows = ""; 42 - }; 43 - 44 - darwin = { 45 - url = "github:nix-darwin/nix-darwin"; 46 - inputs.nixpkgs.follows = "nixpkgs"; 47 - }; 48 - }; 49 - 50 - }
-10
templates/examples/modules/den.nix
··· 1 - # USER TODO: remove this file. 2 - # copy any desired module to your ./modules and let it be auto-imported. 3 - { inputs, ... }: 4 - { 5 - imports = [ 6 - # The _example directory contains CI tests for all den features. 7 - # use it as reference of usage, but not of best practices. 8 - (inputs.import-tree ./_example) 9 - ]; 10 - }
-9
templates/examples/modules/dendritic.nix
··· 1 - { inputs, lib, ... }: 2 - { 3 - flake-file.inputs.flake-file.url = lib.mkDefault "github:vic/flake-file"; 4 - flake-file.inputs.den.url = lib.mkDefault "github:vic/den"; 5 - imports = [ 6 - (inputs.flake-file.flakeModules.dendritic or { }) 7 - (inputs.den.flakeModules.dendritic or { }) 8 - ]; 9 - }
-12
templates/examples/provider/flake.nix
··· 1 - { 2 - outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 3 - 4 - inputs = { 5 - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 - flake-parts.url = "github:hercules-ci/flake-parts"; 7 - flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 8 - flake-aspects.url = "github:vic/flake-aspects"; 9 - import-tree.url = "github:vic/import-tree"; 10 - den.url = "github:vic/den"; 11 - }; 12 - }
-28
templates/examples/provider/modules/den.nix
··· 1 - { inputs, ... }: 2 - { 3 - systems = [ 4 - "x86_64-linux" 5 - "aarch64-darwin" 6 - ]; 7 - 8 - imports = [ 9 - inputs.den.flakeModule 10 - (inputs.den.namespace "provider" true) 11 - ]; 12 - 13 - provider.tools._.dev._.editors = { 14 - description = "Editor configurations from provider flake"; 15 - nixos.environment.variables.PROVIDER_EDITOR = "vim"; 16 - homeManager = 17 - { pkgs, ... }: 18 - { 19 - home.packages = [ pkgs.vim ]; 20 - }; 21 - }; 22 - 23 - provider.tools._.dev._.shells = { 24 - description = "Shell configurations from provider flake"; 25 - nixos.environment.variables.PROVIDER_SHELL = "fish"; 26 - nixos.environment.systemPackages = [ ]; 27 - }; 28 - }
-21
templates/minimal/flake.lock
··· 30 30 "type": "github" 31 31 } 32 32 }, 33 - "flake-parts": { 34 - "inputs": { 35 - "nixpkgs-lib": [ 36 - "nixpkgs" 37 - ] 38 - }, 39 - "locked": { 40 - "lastModified": 1762980239, 41 - "narHash": "sha256-8oNVE8TrD19ulHinjaqONf9QWCKK+w4url56cdStMpM=", 42 - "owner": "hercules-ci", 43 - "repo": "flake-parts", 44 - "rev": "52a2caecc898d0b46b2b905f058ccc5081f842da", 45 - "type": "github" 46 - }, 47 - "original": { 48 - "owner": "hercules-ci", 49 - "repo": "flake-parts", 50 - "type": "github" 51 - } 52 - }, 53 33 "import-tree": { 54 34 "locked": { 55 35 "lastModified": 1763263999, ··· 85 65 "inputs": { 86 66 "den": "den", 87 67 "flake-aspects": "flake-aspects", 88 - "flake-parts": "flake-parts", 89 68 "import-tree": "import-tree", 90 69 "nixpkgs": "nixpkgs" 91 70 }
+6 -4
templates/minimal/flake.nix
··· 1 1 { 2 - outputs = inputs: inputs.flake-parts.lib.mkFlake { inherit inputs; } (inputs.import-tree ./modules); 2 + outputs = 3 + inputs: 4 + (inputs.nixpkgs.lib.evalModules { 5 + modules = [ (inputs.import-tree ./modules) ]; 6 + specialArgs = { inherit inputs; }; 7 + }).config.flake; 3 8 4 9 inputs = { 5 10 nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 - flake-parts.url = "github:hercules-ci/flake-parts"; 7 - flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; 8 - 9 11 import-tree.url = "github:vic/import-tree"; 10 12 flake-aspects.url = "github:vic/flake-aspects"; 11 13 den.url = "github:vic/den";
+14 -2
templates/minimal/modules/den.nix
··· 1 1 { inputs, den, ... }: 2 2 { 3 - systems = builtins.attrNames den.hosts; 3 + # we can import this flakeModule even if we dont have flake-parts as input! 4 4 imports = [ inputs.den.flakeModule ]; 5 5 6 6 den.hosts.x86_64-linux.igloo.users.tux = { }; 7 - den.aspects.igloo = { }; 7 + 8 + den.aspects.igloo = { 9 + nixos = 10 + { pkgs, ... }: 11 + { 12 + environment.systemPackages = [ pkgs.hello ]; 13 + passthru = { }; 14 + }; 15 + }; 16 + 17 + den.aspects.tux = { 18 + includes = [ den.provides.primary-user ]; 19 + }; 8 20 }
+17
templates/noflake/README.md
··· 1 + # Den without flake-parts 2 + 3 + This template provides an example Den setup with stable Nix (no-flakes), no flake-parts and nix-maid instead of home-manager. 4 + 5 + It configures a NixOS host with one user. 6 + 7 + Try it with: 8 + 9 + ```shell 10 + nixos-rebuild build --file . -A nixosConfigurations.igloo 11 + ``` 12 + 13 + or 14 + 15 + ```shell 16 + nix-build -A nixosConfigurations.igloo.config.system.build.toplevel 17 + ```
+16
templates/noflake/default.nix
··· 1 + let 2 + 3 + outputs = 4 + inputs: 5 + (inputs.nixpkgs.lib.evalModules { 6 + modules = [ (inputs.import-tree ./modules) ]; 7 + specialArgs = { 8 + inherit inputs; 9 + inherit (inputs) self; 10 + }; 11 + }).config; 12 + 13 + in 14 + # Den outputs configurations at flake top-level attr 15 + # even when it does not depend on flakes or flake-parts. 16 + (import ./with-inputs.nix outputs).flake
+37
templates/noflake/modules/den.nix
··· 1 + { inputs, ... }: 2 + { 3 + # we can import this flakeModule even if we dont have flake-parts as input! 4 + imports = [ inputs.den.flakeModule ]; 5 + 6 + # tux user on igloo host. 7 + den.hosts.x86_64-linux.igloo.users.tux = { }; 8 + 9 + # host aspect 10 + den.aspects.igloo = { 11 + nixos = 12 + { pkgs, ... }: 13 + { 14 + passthru = { }; 15 + environment.systemPackages = [ 16 + pkgs.vim 17 + ]; 18 + }; 19 + }; 20 + 21 + # user aspect 22 + den.aspects.tux = { 23 + # user configures the host it lives in 24 + nixos = { 25 + # hipster-tux uses nix-maid instead of home-manager. 26 + imports = [ inputs.nix-maid.nixosModules.default ]; 27 + 28 + users.users.tux = { 29 + isNormalUser = true; 30 + maid.file.home.".gitconfig".text = '' 31 + [user] 32 + name=Tux 33 + ''; 34 + }; 35 + }; 36 + }; 37 + }
+249
templates/noflake/npins/default.nix
··· 1 + /* 2 + This file is provided under the MIT licence: 3 + 4 + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the โ€œSoftwareโ€), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 + 6 + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 + 8 + THE SOFTWARE IS PROVIDED โ€œAS ISโ€, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 + */ 10 + # Generated by npins. Do not modify; will be overwritten regularly 11 + let 12 + # Backwards-compatibly make something that previously didn't take any arguments take some 13 + # The function must return an attrset, and will unfortunately be eagerly evaluated 14 + # Same thing, but it catches eval errors on the default argument so that one may still call it with other arguments 15 + mkFunctor = 16 + fn: 17 + let 18 + e = builtins.tryEval (fn { }); 19 + in 20 + (if e.success then e.value else { error = fn { }; }) // { __functor = _self: fn; }; 21 + 22 + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295 23 + range = 24 + first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1); 25 + 26 + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257 27 + stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1)); 28 + 29 + # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269 30 + stringAsChars = f: s: concatStrings (map f (stringToCharacters s)); 31 + concatStrings = builtins.concatStringsSep ""; 32 + 33 + # If the environment variable NPINS_OVERRIDE_${name} is set, then use 34 + # the path directly as opposed to the fetched source. 35 + # (Taken from Niv for compatibility) 36 + mayOverride = 37 + name: path: 38 + let 39 + envVarName = "NPINS_OVERRIDE_${saneName}"; 40 + saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name; 41 + ersatz = builtins.getEnv envVarName; 42 + in 43 + if ersatz == "" then 44 + path 45 + else 46 + # this turns the string into an actual Nix path (for both absolute and 47 + # relative paths) 48 + builtins.trace "Overriding path of \"${name}\" with \"${ersatz}\" due to set \"${envVarName}\"" ( 49 + if builtins.substring 0 1 ersatz == "/" then 50 + /. + ersatz 51 + else 52 + /. + builtins.getEnv "PWD" + "/${ersatz}" 53 + ); 54 + 55 + mkSource = 56 + name: spec: 57 + { 58 + pkgs ? null, 59 + }: 60 + assert spec ? type; 61 + let 62 + # Unify across builtin and pkgs fetchers. 63 + # `fetchGit` requires a wrapper because of slight API differences. 64 + fetchers = 65 + if pkgs == null then 66 + { 67 + inherit (builtins) fetchTarball fetchurl; 68 + # For some fucking reason, fetchGit has a different signature than the other builtin fetchers โ€ฆ 69 + fetchGit = args: (builtins.fetchGit args).outPath; 70 + } 71 + else 72 + { 73 + fetchTarball = 74 + { 75 + url, 76 + sha256, 77 + }: 78 + pkgs.fetchzip { 79 + inherit url sha256; 80 + extension = "tar"; 81 + }; 82 + inherit (pkgs) fetchurl; 83 + fetchGit = 84 + { 85 + url, 86 + submodules, 87 + rev, 88 + name, 89 + narHash, 90 + }: 91 + pkgs.fetchgit { 92 + inherit url rev name; 93 + fetchSubmodules = submodules; 94 + hash = narHash; 95 + }; 96 + }; 97 + 98 + # Dispatch to the correct code path based on the type 99 + path = 100 + if spec.type == "Git" then 101 + mkGitSource fetchers spec 102 + else if spec.type == "GitRelease" then 103 + mkGitSource fetchers spec 104 + else if spec.type == "PyPi" then 105 + mkPyPiSource fetchers spec 106 + else if spec.type == "Channel" then 107 + mkChannelSource fetchers spec 108 + else if spec.type == "Tarball" then 109 + mkTarballSource fetchers spec 110 + else if spec.type == "Container" then 111 + mkContainerSource pkgs spec 112 + else 113 + builtins.throw "Unknown source type ${spec.type}"; 114 + in 115 + spec // { outPath = mayOverride name path; }; 116 + 117 + mkGitSource = 118 + { 119 + fetchTarball, 120 + fetchGit, 121 + ... 122 + }: 123 + { 124 + repository, 125 + revision, 126 + url ? null, 127 + submodules, 128 + hash, 129 + ... 130 + }: 131 + assert repository ? type; 132 + # At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository 133 + # In the latter case, there we will always be an url to the tarball 134 + if url != null && !submodules then 135 + fetchTarball { 136 + inherit url; 137 + sha256 = hash; 138 + } 139 + else 140 + let 141 + url = 142 + if repository.type == "Git" then 143 + repository.url 144 + else if repository.type == "GitHub" then 145 + "https://github.com/${repository.owner}/${repository.repo}.git" 146 + else if repository.type == "GitLab" then 147 + "${repository.server}/${repository.repo_path}.git" 148 + else if repository.type == "Forgejo" then 149 + "${repository.server}/${repository.owner}/${repository.repo}.git" 150 + else 151 + throw "Unrecognized repository type ${repository.type}"; 152 + urlToName = 153 + url: rev: 154 + let 155 + matched = builtins.match "^.*/([^/]*)(\\.git)?$" url; 156 + 157 + short = builtins.substring 0 7 rev; 158 + 159 + appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else ""; 160 + in 161 + "${if matched == null then "source" else builtins.head matched}${appendShort}"; 162 + name = urlToName url revision; 163 + in 164 + fetchGit { 165 + rev = revision; 166 + narHash = hash; 167 + 168 + inherit name submodules url; 169 + }; 170 + 171 + mkPyPiSource = 172 + { fetchurl, ... }: 173 + { 174 + url, 175 + hash, 176 + ... 177 + }: 178 + fetchurl { 179 + inherit url; 180 + sha256 = hash; 181 + }; 182 + 183 + mkChannelSource = 184 + { fetchTarball, ... }: 185 + { 186 + url, 187 + hash, 188 + ... 189 + }: 190 + fetchTarball { 191 + inherit url; 192 + sha256 = hash; 193 + }; 194 + 195 + mkTarballSource = 196 + { fetchTarball, ... }: 197 + { 198 + url, 199 + locked_url ? url, 200 + hash, 201 + ... 202 + }: 203 + fetchTarball { 204 + url = locked_url; 205 + sha256 = hash; 206 + }; 207 + 208 + mkContainerSource = 209 + pkgs: 210 + { 211 + image_name, 212 + image_tag, 213 + image_digest, 214 + ... 215 + }: 216 + if pkgs == null then 217 + builtins.throw "container sources require passing in a Nixpkgs value: https://github.com/andir/npins/blob/master/README.md#using-the-nixpkgs-fetchers" 218 + else 219 + pkgs.dockerTools.pullImage { 220 + imageName = image_name; 221 + imageDigest = image_digest; 222 + finalImageTag = image_tag; 223 + }; 224 + in 225 + mkFunctor ( 226 + { 227 + input ? ./sources.json, 228 + }: 229 + let 230 + data = 231 + if builtins.isPath input then 232 + # while `readFile` will throw an error anyways if the path doesn't exist, 233 + # we still need to check beforehand because *our* error can be caught but not the one from the builtin 234 + # *piegames sighs* 235 + if builtins.pathExists input then 236 + builtins.fromJSON (builtins.readFile input) 237 + else 238 + throw "Input path ${toString input} does not exist" 239 + else if builtins.isAttrs input then 240 + input 241 + else 242 + throw "Unsupported input type ${builtins.typeOf input}, must be a path or an attrset"; 243 + version = data.version; 244 + in 245 + if version == 7 then 246 + builtins.mapAttrs (name: spec: mkFunctor (mkSource name spec)) data.pins 247 + else 248 + throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`" 249 + )
+63
templates/noflake/npins/sources.json
··· 1 + { 2 + "pins": { 3 + "den": { 4 + "type": "Git", 5 + "repository": { 6 + "type": "GitHub", 7 + "owner": "vic", 8 + "repo": "den" 9 + }, 10 + "branch": "main", 11 + "submodules": false, 12 + "revision": "2dab67aab2214b84f5ac15adc4e900e90d07f17b", 13 + "url": "https://github.com/vic/den/archive/2dab67aab2214b84f5ac15adc4e900e90d07f17b.tar.gz", 14 + "hash": "sha256-LKTq2QTXzA5lR0xhrG/hkWtjvKnr91mvIabBof0/M4A=" 15 + }, 16 + "flake-aspects": { 17 + "type": "Git", 18 + "repository": { 19 + "type": "GitHub", 20 + "owner": "vic", 21 + "repo": "flake-aspects" 22 + }, 23 + "branch": "main", 24 + "submodules": false, 25 + "revision": "61524836788ef6991a82e7d34ebb0ccc05d374ed", 26 + "url": "https://github.com/vic/flake-aspects/archive/61524836788ef6991a82e7d34ebb0ccc05d374ed.tar.gz", 27 + "hash": "sha256-U15OaMr9AcJiB1wW2uCFzFO+DnQ3jJSvln+ZR/+Q0vE=" 28 + }, 29 + "import-tree": { 30 + "type": "Git", 31 + "repository": { 32 + "type": "GitHub", 33 + "owner": "vic", 34 + "repo": "import-tree" 35 + }, 36 + "branch": "main", 37 + "submodules": false, 38 + "revision": "3c23749d8013ec6daa1d7255057590e9ca726646", 39 + "url": "https://github.com/vic/import-tree/archive/3c23749d8013ec6daa1d7255057590e9ca726646.tar.gz", 40 + "hash": "sha256-ZvYKbFib3AEwiNMLsejb/CWs/OL/srFQ8AogkebEPF0=" 41 + }, 42 + "nix-maid": { 43 + "type": "Git", 44 + "repository": { 45 + "type": "GitHub", 46 + "owner": "viperml", 47 + "repo": "nix-maid" 48 + }, 49 + "branch": "master", 50 + "submodules": false, 51 + "revision": "303e67a8c67d1ba1416abd8842d206a57b46f832", 52 + "url": "https://github.com/viperml/nix-maid/archive/303e67a8c67d1ba1416abd8842d206a57b46f832.tar.gz", 53 + "hash": "sha256-nvybtwQnyGeUbAq/iOMnt0rOGrp8nkyjnZTW9XIsrfI=" 54 + }, 55 + "nixpkgs": { 56 + "type": "Channel", 57 + "name": "nixpkgs-unstable", 58 + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre937085.6308c3b21396/nixexprs.tar.xz", 59 + "hash": "sha256-RuGWBqXVEsZwwBvRGS/nRrA6PQyOQwVaAu139Z853Bk=" 60 + } 61 + }, 62 + "version": 7 63 + }
+42
templates/noflake/with-inputs.nix
··· 1 + # adapted from https://github.com/vic/dendritic-unflake 2 + let 3 + sources = (import ./npins) // { 4 + # uncomment for using local den checkout 5 + # den.outPath = ./../..; 6 + }; 7 + selfInputs = builtins.mapAttrs (name: value: mkInputs name value) sources; 8 + mkInputs = 9 + name: sourceInfo: 10 + let 11 + flakePath = sourceInfo.outPath + "/flake.nix"; 12 + isFlake = sources.${name}.flake or true; 13 + in 14 + if isFlake && builtins.pathExists flakePath then 15 + let 16 + flake = import (sourceInfo.outPath + "/flake.nix"); 17 + inputs = builtins.mapAttrs (name: _value: selfInputs.${name}) (flake.inputs or { }); 18 + outputs = flake.outputs (inputs // { inherit self; }); 19 + self = 20 + sourceInfo 21 + // outputs 22 + // { 23 + _type = "flake"; 24 + inherit inputs sourceInfo; 25 + }; 26 + in 27 + self 28 + else 29 + sourceInfo 30 + // { 31 + inherit sourceInfo; 32 + }; 33 + in 34 + selfInputs 35 + // { 36 + __functor = 37 + selfInputs: outputs: 38 + let 39 + self = outputs (selfInputs // { inherit self; }); 40 + in 41 + self; 42 + }