├── .github └── workflows │ └── update-flake-lock.yaml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── doc ├── 01-adr-iogx-interface.md ├── api.md └── nix-setup-guide.md ├── flake.lock ├── flake.nix ├── scripts ├── bump-iogx-everywhere.sh ├── find-repos-that-use-iogx.sh └── render-iogx-api-reference.sh ├── src ├── core │ ├── mkCombinedHaddock.nix │ ├── mkContainerFromCabalExe.nix │ ├── mkGitRevProjectOverlay.nix │ ├── mkHaskellProject.nix │ ├── mkHydraRequiredJob.nix │ ├── mkMergedShellProfiles.nix │ ├── mkReadTheDocsShellProfile.nix │ ├── mkReadTheDocsSite.nix │ ├── mkRenderedIogxApiReference.nix │ ├── mkShell.nix │ ├── mkShellUtilityScripts.nix │ └── mkShellWith.nix ├── ext │ ├── cabal-fmt.nix │ ├── cabal-install.nix │ ├── fourmolu.nix │ ├── haskell-language-server-project.nix │ ├── scriv.nix │ ├── sphinx-markdown-tables.nix │ ├── sphinx-toolchain.nix │ ├── sphinxcontrib-bibtex.nix │ ├── sphinxcontrib-haddock.nix │ └── sphinxemoji.nix ├── lib │ ├── modularise.nix │ └── utils.nix ├── mkFlake.nix └── options │ ├── default.nix │ ├── flake-dot-nix.nix │ ├── mkContainerFromCabalExe.nix │ ├── mkFlake.nix │ ├── mkGitRevOverlay.nix │ ├── mkHaskellProject.nix │ ├── mkHydraRequiredJob.nix │ └── mkShell.nix └── templates ├── haskell ├── cabal.project ├── flake.lock ├── flake.nix └── nix │ ├── outputs.nix │ ├── project.nix │ └── shell.nix └── vanilla ├── flake.lock ├── flake.nix └── nix ├── outputs.nix └── shell.nix /.github/workflows/update-flake-lock.yaml: -------------------------------------------------------------------------------- 1 | # This workflow automates the process of updating all flake inputs and generates 2 | # a pull request (PR) that auto-merges if all checks pass successfully. 3 | name: update-flake-lock 4 | 5 | on: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: '0 9 * * 1' # Runs weekly on Monday at 9:00am 9 | 10 | jobs: 11 | update-flake-lock: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout Repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Install Nix 18 | uses: DeterminateSystems/nix-installer-action@main 19 | 20 | - name: Update All flake.lock Files 21 | run: | 22 | nix flake update --accept-flake-config 23 | 24 | cd "$GITHUB_WORKSPACE/templates/haskell" 25 | nix flake update --accept-flake-config 26 | 27 | cd "$GITHUB_WORKSPACE/templates/vanilla" 28 | nix flake update --accept-flake-config 29 | 30 | cd "$GITHUB_WORKSPACE" 31 | git add . 32 | 33 | - name: Create Pull Request 34 | id: cpr 35 | uses: peter-evans/create-pull-request@v6.0.5 36 | with: 37 | title: Weekly Update of All flake.lock Files 38 | commit-message: Weekly Update of All flake.lock Files 39 | delete-branch: true 40 | token: ${{ secrets.GITHUB_TOKEN }} 41 | 42 | - name: Enable Pull Request Auto-Merge 43 | if: steps.cpr.outputs.pull-request-operation == 'created' 44 | uses: peter-evans/enable-pull-request-automerge@v3 45 | with: 46 | pull-request-number: ${{ steps.cpr.outputs.pull-request-number }} 47 | merge-method: squash 48 | token: ${{ secrets.GITHUB_TOKEN }} 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .trash 2 | .pre-commit-config.yaml 3 | result 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for IOGX 2 | 3 | ## 19 Nov 2024 4 | 5 | - Removed now-archived `nixpkgs-fmt` in favor of `nixfmt-classic`. 6 | 7 | To migrate, replace: 8 | ``` 9 | # shell.nix 10 | { repoRoot, inputs, pkgs, lib, system }: 11 | lib.iogx.mkShell { 12 | tools.nixpkgs-fmt.enable = true; 13 | } 14 | ``` 15 | With: 16 | ``` 17 | # shell.nix 18 | { repoRoot, inputs, pkgs, lib, system }: 19 | lib.iogx.mkShell { 20 | tools.nixfmt-classic.enable = true; 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IOGX — Flake Templates for Your Project 2 | 3 | - [1. Introduction](#1-introduction) 4 | - [2. Features](#2-features) 5 | - [3. API Reference](#3-api-reference) 6 | - [4. Future Work](#4-future-work) 7 | 8 | # 1. Introduction 9 | 10 | IOGX is a Nix library of functions and templates for structuring your Nix code and comes with a number of common DevX facilities to help develop your project. 11 | 12 | Make sure that you have [installed and configured](./doc/nix-setup-guide.md) nix on your system. 13 | 14 | To get started run: 15 | ```bash 16 | # For Haskell Projects 17 | nix flake init --template github:input-output-hk/iogx#haskell 18 | 19 | # For Other Projects 20 | nix flake init --template github:input-output-hk/iogx#vanilla 21 | ``` 22 | 23 | These will generates a `flake.nix` and a `nix` folder in your repository root. 24 | 25 | You may now move on to the [API Reference](./doc/api.md). 26 | 27 | # 2. Features 28 | 29 | ### GHC Build Matrices 30 | 31 | Define a set of GHC configurations for your Haskell project using `haskell.nix`'s flake variants, and for each variant you will get `devShells`, `packages`, `apps`, `checks` and `hydraJobs`. 32 | 33 | ### Extensible Development Shells 34 | 35 | `devShells` come with an optional and complete Haskell toolchain, and they can be easily extended with new packages, custom scripts, environment variables and hooks. 36 | 37 | ### Automatic Hydra Jobset 38 | 39 | By default your `hydraJobs` will include every component in your Haskell project, and your test suites will run in CI. 40 | 41 | ### Easy Code Formatting 42 | 43 | IOGX uses [`pre-commit-hooks`](https://github.com/cachix/pre-commit-hooks.nix) to format your source tree: hooks can be easily configured and are automatically run in CI. 44 | 45 | ### Read The Docs Support 46 | 47 | If your project needs a [Read The Docs](https://readthedocs.org) site then IOGX will include the necessary tools and scripts, and will add the relevant derivations to CI. 48 | 49 | # 3. API Reference 50 | 51 | The `flake.nix` file and all library functions are documented in the [API Reference](./doc/api.md). 52 | 53 | # 4. Future Work 54 | 55 | In the future we plan to develop the following features: 56 | 57 | - Hoogle Support 58 | - Automatic Test Coverage Reports 59 | - Automatic Benchmarking in CI 60 | - Broken Link Detection 61 | - Option to exclude specific jobs from the `required` aggregated job. 62 | 63 | -------------------------------------------------------------------------------- /doc/01-adr-iogx-interface.md: -------------------------------------------------------------------------------- 1 | # ADR 1 2 | 3 | # Motivation 4 | 5 | Nix is used in virtually all repositories at IOG to: 6 | - Provision a comprehensive development environment (including CI) 7 | - Deploy artifacts and manage cloud infrastructure 8 | 9 | IOGX (and this document) is presently only concerned with being a solution to the former. 10 | 11 | The repositories that have integrated IOGX so far have benefitted from a 35%-85% reduction in lines of nix code. 12 | 13 | We take that removing duplication to this degree is worth the effort, modulo the cost of maintaining IOGX. 14 | 15 | With this in mind, we not only want to make the nix code "less", but also simpler and easier to maintain for non nix-savvy devs, to the point that the DevX team can become obsolete to an extent (i.e. developers are self-serving w.r.t. their devenv). 16 | 17 | The way we want to achieve this is by creating an Internal Developer Platform. Our Internal Developer Platform provides the comprehensive development environment in the form of the IOGX flake. 18 | 19 | We hope that old and new repositories will use IOGX instead of copy-pasting thousands of lines of nix. 20 | 21 | 22 | # Current State 23 | 24 | What is common among all/most SC/IOG repos? 25 | 26 | - The main language is Haskell with a cabal.project setup 27 | - They use haskell.nix + CHaP 28 | - They want a complete haskell toolchain (HLS, cabal, ghc, hlint, fourmolu, etc...) 29 | - CI == hydraJobs 30 | - They want to build/test the project using multiple GHCs 31 | - They want to do cross compilation on Windows 32 | - They want other formatters/hooks in the shell (shellcheck / png-optimization) 33 | - They use read-the-docs for documentation 34 | 35 | All of the above are currently covered by IOGX out of the box. 36 | 37 | In the future we want to support: 38 | 39 | - Automatic haddock deployment to gh-pages 40 | - Haskell benchmarking with alerts and history 41 | - Test Coverage Reports 42 | - Arbitrary Haskell project configurations 43 | - Some form of standard/automated way to create/deploy docker containers. 44 | 45 | Currently IOGX exposes a single function called `mkFlake`, that takes some global config values, and then looks inside the `./nix` folder for specific files, imports those files, and uses those files to populate the flake outputs. 46 | 47 | The files inside the nix folder must evaluate to attribute sets with specific schemas. 48 | This is a file-based, declarative interface. 49 | 50 | While the "file-based" part might actually not add so much value (and in the proposal below is deprecated in favour of a fully attrset-based interface), the "declarative" part should probably be pursued, meaning: the user tells us what they want by defining attrsets of more-or-less simple data types, as opposed to by using a newly-crafted library of functions. 51 | 52 | While the interface for the currently-supported feature-set is clean enough, there is room for improvement when it comes to non-common/unpredictable use cases coming from power users. 53 | 54 | # Haskell Project Build Matrix 55 | 56 | We want to support multiple GHCs, and this adds complexity to the interface and the implementation. 57 | 58 | This is because for each GHC, we need a different HLS, which means a different hlint, stylish-haskell, etc. 59 | 60 | For each GHC, we might want to support profiling. And cross compiling (maybe to multiple targets). And static builds. And "selective" haddock compilation (see [here](https://github.com/input-output-hk/plutus-apps/blob/14ae5a40a147a4699c1cb7181ab471a40209c1eb/__std__/cells/plutus-apps/library/make-plutus-apps-project.nix#L5) -- btw, why?). 61 | 62 | Ultimately we want to support arbitrary project configurations (e.g. building some components with specific GHC flags). 63 | 64 | So what we have really is a matrix of haskell projects that we want expose in the interface, while maintaining a notion of a "default project". 65 | 66 | Even the `pre-commit-check` (which runs the formatters, including the haskell ones) becomes a matrix. Do we want to run `pre-commit-check` for each project in the matrix? For each GHC only? Just for the default project? Leave it to the user to decide? 67 | 68 | What about `read-the-docs`? 69 | 70 | This also means that we want a different `devShell` for each matrix element, or at least for each GHC + profiling? 71 | 72 | And each haskell component (that ends up in `inputs.self.packages|apps`) needs to have a unique attribute name across the entire matrix. 73 | 74 | This all extends to `hydraJobs`. 75 | 76 | We certainly want to hide this kind of complexity, and provide a way to make it easy for the user to configure and play with a matrix of projects, while making it trivial to select a default build matrix that fits most use-cases. 77 | 78 | # New Interface 79 | 80 | Below is what I think is a valid starting point. 81 | 82 | ```nix 83 | # Template flake.nix 84 | { 85 | description = ""; 86 | 87 | 88 | inputs = { 89 | 90 | iogx = { 91 | url = "github:input-output-hk/iogx"; 92 | inputs.hackage.follows = "hackage"; 93 | inputs.CHaP.follows = "CHaP"; 94 | }; 95 | 96 | hackage = { 97 | url = "github:input-output-hk/hackage.nix"; 98 | flake = false; 99 | }; 100 | 101 | CHaP = { 102 | url = "github:input-output-hk/cardano-haskell-packages?ref=repo"; 103 | flake = false; 104 | }; 105 | # ^^^^^ CHaP and Hackage inputs are now made explicit by default. 106 | # Should we do the same for nixpkgs? haskell.nix? 107 | }; 108 | 109 | 110 | outputs = inputs: inputs.iogx.lib.mkFlake { 111 | 112 | inherit inputs; 113 | 114 | repoRoot = ./.; 115 | 116 | systems = ["x86_64-linux" "x86_64-darwin"]; 117 | 118 | nixpkgsArgs = { 119 | config = {}; 120 | overlays = []; 121 | }; 122 | # ^^^^^ Internally this is fed to `import inputs.nixpkgs {}` 123 | 124 | 125 | makeOutputs = { inputs, repoRoot }: { 126 | 127 | projectMatrix = {}; 128 | 129 | makeProject = { matrix }: {}; 130 | 131 | extraOutputs = {}; 132 | }; 133 | }; 134 | 135 | 136 | nixConfig = { 137 | extra-substituters = [ 138 | "https://cache.iog.io" 139 | ]; 140 | extra-trusted-public-keys = [ 141 | "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" 142 | ]; 143 | allow-import-from-derivation = true; 144 | }; 145 | } 146 | ``` 147 | 148 | ## Description of `makeOutputs = { inputs, repoRoot }:` 149 | 150 | The `makeOutputs` function would be called by IOGX once for each system in `systems`. 151 | 152 | `inputs` is the de-systemized inputs, which is what you want 99.99% of the time. 153 | 154 | You only want to use the original inputs when you need to access the flake source (e.g. `import inputs.haskell-nix {}`) but even in that case, the de-systemized version works just as well. 155 | 156 | "de-systemizing" the inputs doesn't actually stop you from accessing per-system attributes. The following syntax is valid on any system: 157 | ```nix 158 | inputs.self.apps.foo 159 | # ^^^^^ Uses the current system 160 | 161 | inputs.self.apps.x86_64-darwin.foo 162 | # ^^^^^ This attribute is present on linux too, but obv. may fail to evaluate 163 | 164 | inputs.nixpkgs.hello 165 | inputs.nixpkgs.x86_64-darwin.hello 166 | ``` 167 | Turns out that there is no need to have both (`inputs`/`inputs'` or `inputs`/`perSystemInputs`). 168 | 169 | In the current version of IOGX we expose `pkgs`, `lib` and `system`. 170 | But we can just as well do this now: 171 | ```nix 172 | pkgs = inputs.nixpkgs; 173 | # ^^^^^ Already de-systemized and actually set to inputs.nixpkgs.legacyPackages.$system 174 | # Internally it is overlaid with iogx-nix and haskell.nix and nixpkgsArgs from mkFlake 175 | 176 | system = inputs.nixpkgs.stdenv.system; 177 | lib = pkgs.lib; 178 | ``` 179 | 180 | Now the idea for the `repoRoot` argument is taken straight from `std`. 181 | 182 | `repoRoot` is to IOGX what `cell(s)` is to `std`, except that we don't have a concept of a hierarchy of `blocks` of `cells`, we just have `repoRoot` to get to any file in the repo (e.g. `repoRoot."cabal.project"` or more commonly with nix files `repoRoot.nix.folder.file`) 183 | 184 | Note that the nix files that are wanted to be accessed by dot notation using `repoRoot` are expected to have this format: `{ inputs, repoRoot }: X` 185 | 186 | Or optionally just be `X` if neither `inputs` nor `repoRoot` are needed. 187 | 188 | This is just like in `std`, where you expect nix files to be like `{ inputs, cell }: X`. 189 | 190 | ## Description of `extraOutputs` 191 | 192 | ```nix 193 | extraOutputs = { 194 | 195 | packages.foo = repoRoot.nix.some-nix-file; 196 | 197 | devShells.shell1 = {..} 198 | devShells.shell2 = {..}; 199 | 200 | hydraJobs.nested.hello = inputs.self.packages.hello; 201 | 202 | non-standard-outputs = {..}; 203 | 204 | oci-images.example = inputs.self.cabalProjects.ghc8107.example; 205 | nomadTasks = {..}; 206 | }; 207 | ``` 208 | 209 | In here the user can put anything they want, they can add to the final outputs. 210 | 211 | The `inputs` arguments has the `self` argument, which means that the user has access to **our** outputs as well. 212 | 213 | In addition, it is guaranteed that they will want to reference the `haskell.nix:cabalProject'` project(s) directly. 214 | 215 | Because we are in charge of making the project matrix, we will add `inputs.self.cabalProjects` to the flake outputs (with a `inputs.self.cabalProjects.default`). 216 | 217 | Internally, we will recursively merge the `extraOutputs` attrset with the final flake outputs attrset produced by us, and print warnings/errors in case of name clashes. 218 | 219 | ## Description of `projectMatrix` 220 | 221 | ```nix 222 | projectMatrix = { 223 | 224 | ghc = ["ghc8107" "ghc928" "ghc962"]; 225 | targetHost = ["mingwW64" "musl" "native"]; 226 | enableProfiling = [ true false ]; 227 | enableHaddock = [ true false ]; 228 | # ^^^^^ builtins: we handle these 229 | 230 | customString = ["a" "b" "c"]; 231 | customInt = [1 2] 232 | # ^^^^^ user-defined 233 | } 234 | ``` 235 | 236 | The above provides a way to create an arbitrary matrix of Haskell projects. 237 | 238 | Internally we generate the matrix and call `makeProject` for each element. 239 | 240 | The example above would yield a matrix of 216 elements/projects. 241 | 242 | ## Description of `makeProject` 243 | 244 | ```nix 245 | 246 | makeProject = { matrix }: { 247 | 248 | projectTag = ""; 249 | 250 | isDefaultProject = false; 251 | 252 | addFlakeOutputs = true; 253 | 254 | addHydraJobs = true; 255 | 256 | tools = { 257 | cabalInstall = "default"; 258 | haskellLanguageServer = some-drv; 259 | fourmolu = if matrix.ghc == "ghc8107" then "default" else some-drv; 260 | # hlint 261 | # stylish-haskell 262 | # ghcid 263 | }; 264 | 265 | defaultChangelogPackages = []; 266 | 267 | combinedHaddock = { 268 | enable = false; 269 | projectPackages = []; 270 | prologue = ""; 271 | }; 272 | 273 | readTheDocsFolder = null; 274 | 275 | 276 | cabalProjectArgs = { config }: { 277 | cabalProjectLocal = ""; 278 | sha256map = {}; 279 | shell = {} 280 | modules = {}; 281 | overlays = []; 282 | }; 283 | 284 | shellFor = { project }: { 285 | 286 | prompt = ""; 287 | welcomeMessage = ""; 288 | packages = []; 289 | scripts = {}; 290 | env = {}; 291 | enterShell = ""; 292 | 293 | preCommitHooks = { 294 | nixpkgs-fmt.enable = true; 295 | nixpkgs-fmt.extraOptions = ""; 296 | # nixpkgs-fmt.package = "default"; 297 | 298 | hlint.enable = true; 299 | hlint.extraOptions = ""; 300 | # We don't offer hlint.package because this is set in `tools` above. 301 | 302 | # cabal-fmt 303 | # stylish-haskell 304 | # png-optimization 305 | # shellcheck 306 | }; 307 | }; 308 | } 309 | ``` 310 | 311 | This function is called for each row in the `projectMatrix`. 312 | 313 | ### The `projectTag` field 314 | 315 | Suppose you have two `ghc`s (`ghc8107` and `ghc928`) and we want to enable profiling. 316 | We need a way to name each project and each output. 317 | We might want to end up with: 318 | ```nix 319 | inputs.self.cabalProjects.ghc8107 320 | inputs.self.cabalProjects.ghc8107-profiled 321 | inputs.self.cabalProjects.ghc928 322 | inputs.self.cabalProjects.ghc928-profiled 323 | 324 | nix build .#package-exe-foo-ghc8107 325 | nix build .#package-exe-foo-ghc8107-profiled 326 | nix build .#package-exe-foo-ghc928 327 | nix build .#package-exe-foo-ghc928-profiled 328 | 329 | nix develop .#ghc8107 330 | nix develop .#ghc8107-profiled 331 | nix develop .#ghc928 332 | nix develop .#ghc928-profiled 333 | ``` 334 | 335 | With a more complex `projectMatrix` we want to let the user make the tag. 336 | 337 | Still the `projectTag` field can be omitted and we will generate a unique tag across the matrix. 338 | 339 | A warning will be generated if the same project tag is produced from different `matrix` args. 340 | 341 | ### The `isDefaultProject` field 342 | 343 | We need a concept of a `defaultProject` which is the one that will be selected when running `nix develop` and which will end up in `inputs.self.cabalProjects.default`. 344 | 345 | By default we can make the default project the one with the left-most GHC in `projectMatrix.ghc` (or add a `defaultGhc` to `mkFlake`), no profiling, no haddock, no custom user flags. 346 | 347 | A warning will be generated if more than one `matrix` yield a default project. 348 | 349 | ### The `addFlakeOutputs` field 350 | 351 | We might want to make a project but not include the actual flake outputs. 352 | 353 | This means that the project will end up in `inputs.self.cabalProjects` but not in `inputs.self.packages|apps|checks|devShells`. 354 | 355 | If this field is omitted then we just make outputs for the default project. 356 | 357 | ### The `tools` field 358 | 359 | Haskell-specific tools. 360 | 361 | If the `cabalInstall` field is omitted or set to `"default"` then we will select the right one w.r.t. `matrix.ghc`. 362 | 363 | Same for `haskellLanguageServer`. 364 | 365 | And same for `fourmolu`, `hlint`, `stylish-haskell` and `ghcid`: if set to `"default"` or omitted, we extract them from `haskellLanguageServer`. 366 | 367 | The entire `tools` field can be omitted and it will default to the latest version for each tool. 368 | 369 | ### The `defaultChangelogPackages` field 370 | 371 | Field for the scriv changelog scripts. 372 | 373 | ### The `combinedHaddock` field 374 | 375 | Fields for making the combined haddock. 376 | 377 | ### The `readTheDocsFolder` field 378 | 379 | This should probably be moved to `mkFlake` top-level and created only for the default project. 380 | 381 | ### The `addHydraJobs` field 382 | 383 | Similar to `addFlakeOutputs`, this field decides whether exes, libs and test-suites end up in Hydra for this project. Same default value as `addFlakeOutputs`. 384 | More control can be obtained in `extraOutputs` by working with `inputs.self.cabalProjects` directly. 385 | 386 | ### The `cabalProjectArgs` field 387 | 388 | Stuff that gets passed almost directly to `haskell.nix:cabalProject'` 389 | 390 | ### The `shellFor` field 391 | 392 | For each project we need a shell. 393 | 394 | At this stage the project has been created and so can be passed to `shellFor`. 395 | 396 | Each shell will be added to the flake outputs as `devShells.$TAG`. Where `$TAG` is the `projectTag` obtained in `makeProject`. 397 | 398 | This feels like a good place to place the `pre-commit-hooks` too (formerly `./nix/formatters.nix`) 399 | 400 | ----- 401 | 402 | # ADR (2) 403 | 404 | ## Modular Interface 405 | 406 | ```nix 407 | # The new template flake.nix 408 | { 409 | inputs = # Nothing new 410 | 411 | 412 | outputs = inputs: inputs.iogx.lib.mkFlake { 413 | 414 | inherit inputs; 415 | 416 | systems = ["x86_64-linux" "x86_64-darwin"]; 417 | 418 | modules = [ 419 | ({ inputs, root, ... }: { packages = ..; devShells = ..; hydraJobs = ..; }) 420 | ({ inputs, root, ... }: { packages = ..; devShells = ..; }) 421 | ({ inputs, root, ... }: { devShells = ..; }) 422 | ({ inputs, root, ... }: { operables = ..; oci-images = ..; }) 423 | ({ inputs, root, ... }: { nomadTasks = ..; }) 424 | ... 425 | ]; 426 | # ^^^^^ Just like the modules in haskell.nix 427 | # Each module is a function to a flake (packages, hydraJobs, devShells, oci-images, ...) 428 | # Each module is recursively merged top-to-bottom and a warning is raised on name clash 429 | }; 430 | 431 | 432 | nixConfig = # Nothing new 433 | } 434 | ``` 435 | 436 | Even if this were not a haskell project, we get: 437 | - `root` for attrset-based access to files and folders 438 | - A way to recursively merge flake outputs and get warnings on name clash 439 | 440 | Now we can put functions inside `inputs.iogx.lib` to make & play with modules. 441 | 442 | # `inputs.iogx.lib.makeCabalProjectsModule` 443 | 444 | ```nix 445 | inputs.iogx.lib.makeCabalProjectsModule { 446 | 447 | buildMatrix = { 448 | ghc = ["ghc8107" "ghc928" "ghc962"]; 449 | targetHost = ["mingwW64" "musl" "native"]; 450 | enableProfiling = [ true false ]; 451 | enableHaddock = [ true false ]; 452 | # ^^^^^ builtins 453 | customString = ["a" "b" "c"]; 454 | customInt = [1 2]; 455 | # ^^^^^ user-defined 456 | }; 457 | 458 | makeProject = config@{ ghc, targetHost, ..., customString, ... }: 459 | # ^^^^ Called for each row in buildMatrix 460 | 461 | id = "default"; # Must be unique across the matrix 462 | 463 | cabalProjectFile = ./cabal.project; 464 | 465 | defaultChangelogPackages = []; 466 | 467 | readTheDocsFolder = null; 468 | 469 | combinedHaddock = { 470 | enable = false; 471 | projectPackages = []; 472 | prologue = ""; 473 | }; 474 | 475 | tools = { 476 | cabalInstall = "default" or derviation; 477 | haskellLanguageServer = "default" or derviation; 478 | fourmolu = "default" or derviation; 479 | hlint = "default" or derviation; 480 | stylish-haskell = "default" or derviation; 481 | ghcid = "default" or derviation; 482 | }; 483 | 484 | haskellDotNixCabalProject = iogx.lib.makeHaskellDotNixCabalProject { 485 | # ^^^^^ where makeHaskellDotNixCabalProject is just a thin wrapper around haskell.nix:cabalProject' 486 | cabalProjectLocal = ""; 487 | sha256map = {}; 488 | shell = {} 489 | modules = {}; 490 | overlays = []; 491 | }; # ... Or you can call haskell.nix:cabalProject' yourself 492 | 493 | shell = { cabalProject }: { # The shell receives the made haskell.nix:cabalProject 494 | # ^^^^^ And it produces an augmented devShell with preCommitHooks 495 | 496 | prompt = ""; 497 | welcomeMessage = ""; 498 | packages = []; 499 | scripts = {}; 500 | env = {}; 501 | shellHook = ""; 502 | 503 | preCommitHooks = { 504 | nixpkgs-fmt.enable = true; 505 | nixpkgs-fmt.extraOptions = ""; 506 | hlint.enable = true; 507 | hlint.extraOptions = ""; 508 | # cabal-fmt, stylish-haskell, png-optimization, shellcheck, ... 509 | }; 510 | }; 511 | }; 512 | ``` 513 | 514 | The function above produces an attrset, where each attribute name is the `id` from `makeProject` and its value is an augmented cabal project as defined below: 515 | 516 | ```nix 517 | cabalProject = # return value of haskell.nix:cabalProject' 518 | 519 | # haskell.nix:cabalProject' already contains the `flake` attr, but we augment it: 520 | cabalProject.flake.devShells.default = # the augmented shell 521 | cabalProject.flake.hydraJobs.devShells.default = # the augmented shell 522 | 523 | iogx = { 524 | config = # same as the config passed to makeProject 525 | tools = # computed derivations for the tools 526 | id = # same as the id passed to makeProject 527 | read-the-docs-site = # derivation for the read-the-docs-site (optional) 528 | pre-commit-check = # derivation for the pre-commit-check 529 | } 530 | 531 | augmentedCabalProject = cabalProject // { inherit iogx; } 532 | ``` 533 | 534 | The final attrset is named `cabalProjects` (notice the `s`) and is added to the flake outputs. 535 | 536 | This means that it can be consumed by other modules: 537 | 538 | ```nix 539 | # flake.nix 540 | { 541 | ... 542 | outputs = inputs: inputs.iogx.lib.mkFlake { 543 | ... 544 | modules = [ 545 | ./nix/producer.nix 546 | ./nix/consumer.nix 547 | ]; 548 | }; 549 | } 550 | 551 | # ./nix/producer.nix 552 | { inputs, ... }: 553 | inputs.iogx.lib.makeCabalProjectsModule {} 554 | 555 | # ./nix/consumer.nix 556 | { inputs, lib, ... }: 557 | let 558 | projects = inputs.self.cabalProjects; 559 | in 560 | { 561 | packages = inputs.iogx.lib.recursiveUpdateManyWithWarning [ 562 | projects.ghc8107.flake.packages 563 | projects.ghc927.flake.packages 564 | projects.ghc927-profiled.flake.packages 565 | ]; 566 | 567 | checks = inputs.iogx.lib.recursiveUpdateManyWithWarning [ 568 | projects.ghc8107.checks.packages 569 | projects.ghc927.checks.packages 570 | projects.ghc927-profiled.checks.packages 571 | ]; 572 | 573 | devShells.default = devShells.ghc8017; 574 | devShells.ghc8017 = projects.ghc8107.flake.devShells.default; 575 | devShells.ghc8107-profiled = projects.ghc8107-profiled.flake.devShells.default; 576 | 577 | hydraJobs.ghc8017 = projects.ghc8107.flake.hydraJobs; 578 | hydraJobs.ghc927-profiled = projects.ghc927-profiled.flake.hydraJobs; 579 | 580 | hydraJobs.packages.read-the-docs-site = projects.default.iogx.read-the-docs-site; 581 | hydraJobs.packages.pre-commit-check = projects.default.iogx.pre-commit-check; 582 | } 583 | ``` 584 | -------------------------------------------------------------------------------- /doc/nix-setup-guide.md: -------------------------------------------------------------------------------- 1 | # Nix Setup Guide 2 | 3 | Here you will find instructions on how to install and configure nix to work with 4 | projects at IOG. 5 | 6 | ## Installing / Upgrading 7 | 8 | If you don't have nix installed, follow the instructions on the 9 | [official website](https://nixos.org/download), or simply use one of following 10 | commands in a terminal: 11 | 12 | - For multi-user installation (i.e., system-wide setup, **recommended**) 13 | ```bash 14 | sh <(curl -L https://nixos.org/nix/install) --daemon 15 | ``` 16 | - For single-user installation (i.e., local setup) 17 | ```bash 18 | sh <(curl -L https://nixos.org/nix/install) --no-daemon 19 | ``` 20 | 21 | We use nix `flakes` which require nix version `>= 2.4`, therefore if you already 22 | have nix installed, make sure to 23 | [upgrade](https://nixos.org/manual/nix/stable/installation/upgrading) to a 24 | recent version. 25 | 26 | ## Becoming a Truster User 27 | 28 | If you are on NixOS or have installed nix in multi-user mode, you **must** be 29 | a [trusted user](https://nixos.org/nix/manual/#ssec-multi-user), which is 30 | necessary to enable access to our binary cache. 31 | 32 | On non-NixOS systems, append the following line to the system-wide configuration 33 | `/etc/nix/nix.conf`: 34 | ```txt 35 | trusted-users = USER root 36 | ``` 37 | **Where `USER` is the result of running `whoami` in a terminal.** 38 | 39 | You can also use a group name by prefixing it with `@`. For instance, to add all 40 | members of the `wheel` group: 41 | ```txt 42 | trusted-users = @wheel root 43 | ``` 44 | 45 | On NixOS, add the user/group name to the list under 46 | [`nix.settings.trusted-users`](https://search.nixos.org/options?show=nix.settings.trusted-users). 47 | 48 | ## Setting Up IOG Binary Cache 49 | 50 | Adding the IOG binary cache to your nix configuration will speed up builds a 51 | lot, since many things will have been built already by our CI. 52 | 53 | On non-NixOS systems, edit one of the following two files (create them if they don't exist): 54 | - `/etc/nix/nix.conf` for system-wide settings on a multi-user installation 55 | - `~/.config/nix/nix.conf` for user-wide settings on a single-user installation 56 | 57 | By appending the following lines: 58 | ```txt 59 | experimental-features = nix-command flakes 60 | extra-substituters = https://cache.iog.io 61 | extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= 62 | ``` 63 | 64 | On NixOS systems, set the following NixOS options: 65 | ```nix 66 | nix.settings = { 67 | trusted-substituters = [ "https://cache.iog.io" ]; 68 | trusted-public-keys = [ "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" ]; 69 | }; 70 | ``` 71 | 72 | ## Notes for Apple Users 73 | 74 | Apple Silicon users can run any Intel binary out-of-the-box thanks to Rosetta 75 | emulation, but when working with nix flakes, the `aarch64-darwin` system will be 76 | selected by default. 77 | 78 | However, some projects at IOG cannot be built natively on `aarch64-darwin`. 79 | 80 | Therefore you must specify the `--system` explicitly to target `x86_64-darwin`. 81 | ```bash 82 | nix (develop|build|run|check) --system x86_64-darwin # Will use the x86_64-darwin derivation 83 | nix (develop|build|run|check) # Will use the aarch64-darwin derivation, if available 84 | ``` 85 | 86 | To enable this, you must append the following lines to your `/etc/nix/nix.conf` 87 | or `~/.config/nix/nix.conf`: 88 | ```txt 89 | extra-platforms = x86_64-darwin aarch64-darwin 90 | extra-sandbox-paths = /System/Library/Frameworks /System/Library/PrivateFrameworks /usr/lib /private/tmp /private/var/tmp /usr/bin/env 91 | ``` 92 | 93 | You may need to reload the nix daemon for changes to take effect: 94 | ```bash 95 | sudo launchctl stop org.nixos.nix-daemon 96 | sudo launchctl start org.nixos.nix-daemon 97 | ``` 98 | 99 | ## Development Shell and VSCode 100 | 101 | Now that you have nix installed and configured, you may enter the development 102 | shell: 103 | ``` 104 | nix develop 105 | ``` 106 | If you are on Apple Silicon and a native shell is not available, you will want 107 | to run this instead: 108 | ``` 109 | nix develop --system x86_64-darwin 110 | ``` 111 | 112 | If you are a `VSCode` user, you may also start your development session by 113 | executing the following command: 114 | ``` 115 | nix develop --command code 116 | ``` 117 | 118 | If you are running `nix develop` for the first time, please enter `y` at the 119 | following prompts to create the local nix settings file 120 | `~/.local/share/nix/trusted-settings.json`: 121 | ```txt 122 | > do you want to allow configuration setting 'accept-flake-config' to be set to 'true' (y/N)? 123 | > do you want to permanently mark this value as trusted (y/N)? 124 | ``` 125 | 126 | It is particularly important to accept the `extra-substituters` and 127 | `extra-trusted-public-keys` settings as these will grant access to our binary 128 | cache. 129 | 130 | When `nix develop` is run for the first time, a significant amount of 131 | dependencies will be downloaded, built and installed. This process may take a 132 | couple of hours but is expected to happen only once. However, if you ever 133 | witness that GHC is also being built from scratch, then it is likely that your 134 | binary cache has not been configured properly or is not being considered. 135 | Accepting the configuration settings as outlined above should be sufficient to 136 | avoid this. Nevertheless, if your caches are still broken, you'll want to review 137 | this document carefully to ensure that your nix installation is properly 138 | configured. 139 | 140 | You will know that your caches are broken if you see this message: 141 | ``` 142 | warning: ignoring untrusted substituter 'https://cache.iog.io', you are not a trusted user. 143 | ``` 144 | 145 | If is possible that your settings are correct but the nix daemon is not properly 146 | considering them. In this case, stop the `nix develop` process and restart the 147 | `nix-daemon` as follows: 148 | ```bash 149 | sudo systemctl stop nix-daemon.service 150 | ``` 151 | Once done, you can launch the `nix develop` process again. -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "CHaP": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1737590273, 7 | "narHash": "sha256-zzeaIeeKCVfTxeSLw3oTmguOwH46NJw5LZH8wyWbATQ=", 8 | "owner": "IntersectMBO", 9 | "repo": "cardano-haskell-packages", 10 | "rev": "be239ff9f54422603b3acf5c4d87b62a0c715196", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "IntersectMBO", 15 | "ref": "repo", 16 | "repo": "cardano-haskell-packages", 17 | "type": "github" 18 | } 19 | }, 20 | "HTTP": { 21 | "flake": false, 22 | "locked": { 23 | "lastModified": 1451647621, 24 | "narHash": "sha256-oHIyw3x0iKBexEo49YeUDV1k74ZtyYKGR2gNJXXRxts=", 25 | "owner": "phadej", 26 | "repo": "HTTP", 27 | "rev": "9bc0996d412fef1787449d841277ef663ad9a915", 28 | "type": "github" 29 | }, 30 | "original": { 31 | "owner": "phadej", 32 | "repo": "HTTP", 33 | "type": "github" 34 | } 35 | }, 36 | "blst": { 37 | "flake": false, 38 | "locked": { 39 | "lastModified": 1691598027, 40 | "narHash": "sha256-oqljy+ZXJAXEB/fJtmB8rlAr4UXM+Z2OkDa20gpILNA=", 41 | "owner": "supranational", 42 | "repo": "blst", 43 | "rev": "3dd0f804b1819e5d03fb22ca2e6fac105932043a", 44 | "type": "github" 45 | }, 46 | "original": { 47 | "owner": "supranational", 48 | "ref": "v0.3.11", 49 | "repo": "blst", 50 | "type": "github" 51 | } 52 | }, 53 | "cabal-32": { 54 | "flake": false, 55 | "locked": { 56 | "lastModified": 1603716527, 57 | "narHash": "sha256-X0TFfdD4KZpwl0Zr6x+PLxUt/VyKQfX7ylXHdmZIL+w=", 58 | "owner": "haskell", 59 | "repo": "cabal", 60 | "rev": "48bf10787e27364730dd37a42b603cee8d6af7ee", 61 | "type": "github" 62 | }, 63 | "original": { 64 | "owner": "haskell", 65 | "ref": "3.2", 66 | "repo": "cabal", 67 | "type": "github" 68 | } 69 | }, 70 | "cabal-34": { 71 | "flake": false, 72 | "locked": { 73 | "lastModified": 1645834128, 74 | "narHash": "sha256-wG3d+dOt14z8+ydz4SL7pwGfe7SiimxcD/LOuPCV6xM=", 75 | "owner": "haskell", 76 | "repo": "cabal", 77 | "rev": "5ff598c67f53f7c4f48e31d722ba37172230c462", 78 | "type": "github" 79 | }, 80 | "original": { 81 | "owner": "haskell", 82 | "ref": "3.4", 83 | "repo": "cabal", 84 | "type": "github" 85 | } 86 | }, 87 | "cabal-36": { 88 | "flake": false, 89 | "locked": { 90 | "lastModified": 1669081697, 91 | "narHash": "sha256-I5or+V7LZvMxfbYgZATU4awzkicBwwok4mVoje+sGmU=", 92 | "owner": "haskell", 93 | "repo": "cabal", 94 | "rev": "8fd619e33d34924a94e691c5fea2c42f0fc7f144", 95 | "type": "github" 96 | }, 97 | "original": { 98 | "owner": "haskell", 99 | "ref": "3.6", 100 | "repo": "cabal", 101 | "type": "github" 102 | } 103 | }, 104 | "cardano-shell": { 105 | "flake": false, 106 | "locked": { 107 | "lastModified": 1608537748, 108 | "narHash": "sha256-PulY1GfiMgKVnBci3ex4ptk2UNYMXqGjJOxcPy2KYT4=", 109 | "owner": "input-output-hk", 110 | "repo": "cardano-shell", 111 | "rev": "9392c75087cb9a3d453998f4230930dea3a95725", 112 | "type": "github" 113 | }, 114 | "original": { 115 | "owner": "input-output-hk", 116 | "repo": "cardano-shell", 117 | "type": "github" 118 | } 119 | }, 120 | "easy-purescript-nix": { 121 | "inputs": { 122 | "flake-utils": "flake-utils" 123 | }, 124 | "locked": { 125 | "lastModified": 1710161569, 126 | "narHash": "sha256-lcIRIOFCdIWEGyKyG/tB4KvxM9zoWuBRDxW+T+mvIb0=", 127 | "owner": "justinwoo", 128 | "repo": "easy-purescript-nix", 129 | "rev": "117fd96acb69d7d1727df95b6fde9d8715e031fc", 130 | "type": "github" 131 | }, 132 | "original": { 133 | "owner": "justinwoo", 134 | "repo": "easy-purescript-nix", 135 | "type": "github" 136 | } 137 | }, 138 | "flake-compat": { 139 | "locked": { 140 | "lastModified": 1733328505, 141 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", 142 | "owner": "edolstra", 143 | "repo": "flake-compat", 144 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", 145 | "type": "github" 146 | }, 147 | "original": { 148 | "owner": "edolstra", 149 | "repo": "flake-compat", 150 | "type": "github" 151 | } 152 | }, 153 | "flake-compat_2": { 154 | "flake": false, 155 | "locked": { 156 | "lastModified": 1672831974, 157 | "narHash": "sha256-z9k3MfslLjWQfnjBtEtJZdq3H7kyi2kQtUThfTgdRk0=", 158 | "owner": "input-output-hk", 159 | "repo": "flake-compat", 160 | "rev": "45f2638735f8cdc40fe302742b79f248d23eb368", 161 | "type": "github" 162 | }, 163 | "original": { 164 | "owner": "input-output-hk", 165 | "ref": "hkm/gitlab-fix", 166 | "repo": "flake-compat", 167 | "type": "github" 168 | } 169 | }, 170 | "flake-compat_3": { 171 | "flake": false, 172 | "locked": { 173 | "lastModified": 1696426674, 174 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", 175 | "owner": "edolstra", 176 | "repo": "flake-compat", 177 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", 178 | "type": "github" 179 | }, 180 | "original": { 181 | "owner": "edolstra", 182 | "repo": "flake-compat", 183 | "type": "github" 184 | } 185 | }, 186 | "flake-utils": { 187 | "inputs": { 188 | "systems": "systems" 189 | }, 190 | "locked": { 191 | "lastModified": 1685518550, 192 | "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", 193 | "owner": "numtide", 194 | "repo": "flake-utils", 195 | "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", 196 | "type": "github" 197 | }, 198 | "original": { 199 | "owner": "numtide", 200 | "repo": "flake-utils", 201 | "type": "github" 202 | } 203 | }, 204 | "flake-utils_2": { 205 | "inputs": { 206 | "systems": "systems_2" 207 | }, 208 | "locked": { 209 | "lastModified": 1731533236, 210 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 211 | "owner": "numtide", 212 | "repo": "flake-utils", 213 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 214 | "type": "github" 215 | }, 216 | "original": { 217 | "owner": "numtide", 218 | "repo": "flake-utils", 219 | "type": "github" 220 | } 221 | }, 222 | "flake-utils_3": { 223 | "inputs": { 224 | "systems": "systems_3" 225 | }, 226 | "locked": { 227 | "lastModified": 1710146030, 228 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 229 | "owner": "numtide", 230 | "repo": "flake-utils", 231 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 232 | "type": "github" 233 | }, 234 | "original": { 235 | "owner": "numtide", 236 | "repo": "flake-utils", 237 | "type": "github" 238 | } 239 | }, 240 | "ghc-8.6.5-iohk": { 241 | "flake": false, 242 | "locked": { 243 | "lastModified": 1600920045, 244 | "narHash": "sha256-DO6kxJz248djebZLpSzTGD6s8WRpNI9BTwUeOf5RwY8=", 245 | "owner": "input-output-hk", 246 | "repo": "ghc", 247 | "rev": "95713a6ecce4551240da7c96b6176f980af75cae", 248 | "type": "github" 249 | }, 250 | "original": { 251 | "owner": "input-output-hk", 252 | "ref": "release/8.6.5-iohk", 253 | "repo": "ghc", 254 | "type": "github" 255 | } 256 | }, 257 | "gitignore": { 258 | "inputs": { 259 | "nixpkgs": [ 260 | "pre-commit-hooks-nix", 261 | "nixpkgs" 262 | ] 263 | }, 264 | "locked": { 265 | "lastModified": 1709087332, 266 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", 267 | "owner": "hercules-ci", 268 | "repo": "gitignore.nix", 269 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", 270 | "type": "github" 271 | }, 272 | "original": { 273 | "owner": "hercules-ci", 274 | "repo": "gitignore.nix", 275 | "type": "github" 276 | } 277 | }, 278 | "hackage": { 279 | "flake": false, 280 | "locked": { 281 | "lastModified": 1737937741, 282 | "narHash": "sha256-IbfaZYGn4mDJVsM8/GsPyRBLsRP8saAHvST42IJuiBk=", 283 | "owner": "input-output-hk", 284 | "repo": "hackage.nix", 285 | "rev": "5f787e9eea8708dc08ae1224a06677c27707205f", 286 | "type": "github" 287 | }, 288 | "original": { 289 | "owner": "input-output-hk", 290 | "repo": "hackage.nix", 291 | "type": "github" 292 | } 293 | }, 294 | "haskell-nix": { 295 | "inputs": { 296 | "HTTP": "HTTP", 297 | "cabal-32": "cabal-32", 298 | "cabal-34": "cabal-34", 299 | "cabal-36": "cabal-36", 300 | "cardano-shell": "cardano-shell", 301 | "flake-compat": "flake-compat_2", 302 | "ghc-8.6.5-iohk": "ghc-8.6.5-iohk", 303 | "hackage": [ 304 | "hackage" 305 | ], 306 | "hls-1.10": "hls-1.10", 307 | "hls-2.0": "hls-2.0", 308 | "hls-2.2": "hls-2.2", 309 | "hls-2.3": "hls-2.3", 310 | "hls-2.4": "hls-2.4", 311 | "hls-2.5": "hls-2.5", 312 | "hls-2.6": "hls-2.6", 313 | "hls-2.7": "hls-2.7", 314 | "hls-2.8": "hls-2.8", 315 | "hls-2.9": "hls-2.9", 316 | "hpc-coveralls": "hpc-coveralls", 317 | "hydra": "hydra", 318 | "iserv-proxy": "iserv-proxy", 319 | "nixpkgs": [ 320 | "haskell-nix", 321 | "nixpkgs-unstable" 322 | ], 323 | "nixpkgs-2003": "nixpkgs-2003", 324 | "nixpkgs-2105": "nixpkgs-2105", 325 | "nixpkgs-2111": "nixpkgs-2111", 326 | "nixpkgs-2205": "nixpkgs-2205", 327 | "nixpkgs-2211": "nixpkgs-2211", 328 | "nixpkgs-2305": "nixpkgs-2305", 329 | "nixpkgs-2311": "nixpkgs-2311", 330 | "nixpkgs-2405": "nixpkgs-2405", 331 | "nixpkgs-unstable": "nixpkgs-unstable", 332 | "old-ghc-nix": "old-ghc-nix", 333 | "stackage": "stackage" 334 | }, 335 | "locked": { 336 | "lastModified": 1737939095, 337 | "narHash": "sha256-MrfPJMoE0PxeW0U9+emHV0rqqPXDOOyKZ5GpRRuJMZw=", 338 | "owner": "input-output-hk", 339 | "repo": "haskell.nix", 340 | "rev": "351c0e12fcb0d201a32b0e8dca15a815cc9be9dc", 341 | "type": "github" 342 | }, 343 | "original": { 344 | "owner": "input-output-hk", 345 | "repo": "haskell.nix", 346 | "type": "github" 347 | } 348 | }, 349 | "hls-1.10": { 350 | "flake": false, 351 | "locked": { 352 | "lastModified": 1680000865, 353 | "narHash": "sha256-rc7iiUAcrHxwRM/s0ErEsSPxOR3u8t7DvFeWlMycWgo=", 354 | "owner": "haskell", 355 | "repo": "haskell-language-server", 356 | "rev": "b08691db779f7a35ff322b71e72a12f6e3376fd9", 357 | "type": "github" 358 | }, 359 | "original": { 360 | "owner": "haskell", 361 | "ref": "1.10.0.0", 362 | "repo": "haskell-language-server", 363 | "type": "github" 364 | } 365 | }, 366 | "hls-2.0": { 367 | "flake": false, 368 | "locked": { 369 | "lastModified": 1687698105, 370 | "narHash": "sha256-OHXlgRzs/kuJH8q7Sxh507H+0Rb8b7VOiPAjcY9sM1k=", 371 | "owner": "haskell", 372 | "repo": "haskell-language-server", 373 | "rev": "783905f211ac63edf982dd1889c671653327e441", 374 | "type": "github" 375 | }, 376 | "original": { 377 | "owner": "haskell", 378 | "ref": "2.0.0.1", 379 | "repo": "haskell-language-server", 380 | "type": "github" 381 | } 382 | }, 383 | "hls-2.2": { 384 | "flake": false, 385 | "locked": { 386 | "lastModified": 1693064058, 387 | "narHash": "sha256-8DGIyz5GjuCFmohY6Fa79hHA/p1iIqubfJUTGQElbNk=", 388 | "owner": "haskell", 389 | "repo": "haskell-language-server", 390 | "rev": "b30f4b6cf5822f3112c35d14a0cba51f3fe23b85", 391 | "type": "github" 392 | }, 393 | "original": { 394 | "owner": "haskell", 395 | "ref": "2.2.0.0", 396 | "repo": "haskell-language-server", 397 | "type": "github" 398 | } 399 | }, 400 | "hls-2.3": { 401 | "flake": false, 402 | "locked": { 403 | "lastModified": 1695910642, 404 | "narHash": "sha256-tR58doOs3DncFehHwCLczJgntyG/zlsSd7DgDgMPOkI=", 405 | "owner": "haskell", 406 | "repo": "haskell-language-server", 407 | "rev": "458ccdb55c9ea22cd5d13ec3051aaefb295321be", 408 | "type": "github" 409 | }, 410 | "original": { 411 | "owner": "haskell", 412 | "ref": "2.3.0.0", 413 | "repo": "haskell-language-server", 414 | "type": "github" 415 | } 416 | }, 417 | "hls-2.4": { 418 | "flake": false, 419 | "locked": { 420 | "lastModified": 1699862708, 421 | "narHash": "sha256-YHXSkdz53zd0fYGIYOgLt6HrA0eaRJi9mXVqDgmvrjk=", 422 | "owner": "haskell", 423 | "repo": "haskell-language-server", 424 | "rev": "54507ef7e85fa8e9d0eb9a669832a3287ffccd57", 425 | "type": "github" 426 | }, 427 | "original": { 428 | "owner": "haskell", 429 | "ref": "2.4.0.1", 430 | "repo": "haskell-language-server", 431 | "type": "github" 432 | } 433 | }, 434 | "hls-2.5": { 435 | "flake": false, 436 | "locked": { 437 | "lastModified": 1701080174, 438 | "narHash": "sha256-fyiR9TaHGJIIR0UmcCb73Xv9TJq3ht2ioxQ2mT7kVdc=", 439 | "owner": "haskell", 440 | "repo": "haskell-language-server", 441 | "rev": "27f8c3d3892e38edaef5bea3870161815c4d014c", 442 | "type": "github" 443 | }, 444 | "original": { 445 | "owner": "haskell", 446 | "ref": "2.5.0.0", 447 | "repo": "haskell-language-server", 448 | "type": "github" 449 | } 450 | }, 451 | "hls-2.6": { 452 | "flake": false, 453 | "locked": { 454 | "lastModified": 1705325287, 455 | "narHash": "sha256-+P87oLdlPyMw8Mgoul7HMWdEvWP/fNlo8jyNtwME8E8=", 456 | "owner": "haskell", 457 | "repo": "haskell-language-server", 458 | "rev": "6e0b342fa0327e628610f2711f8c3e4eaaa08b1e", 459 | "type": "github" 460 | }, 461 | "original": { 462 | "owner": "haskell", 463 | "ref": "2.6.0.0", 464 | "repo": "haskell-language-server", 465 | "type": "github" 466 | } 467 | }, 468 | "hls-2.7": { 469 | "flake": false, 470 | "locked": { 471 | "lastModified": 1708965829, 472 | "narHash": "sha256-LfJ+TBcBFq/XKoiNI7pc4VoHg4WmuzsFxYJ3Fu+Jf+M=", 473 | "owner": "haskell", 474 | "repo": "haskell-language-server", 475 | "rev": "50322b0a4aefb27adc5ec42f5055aaa8f8e38001", 476 | "type": "github" 477 | }, 478 | "original": { 479 | "owner": "haskell", 480 | "ref": "2.7.0.0", 481 | "repo": "haskell-language-server", 482 | "type": "github" 483 | } 484 | }, 485 | "hls-2.8": { 486 | "flake": false, 487 | "locked": { 488 | "lastModified": 1715153580, 489 | "narHash": "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0=", 490 | "owner": "haskell", 491 | "repo": "haskell-language-server", 492 | "rev": "dd1be1beb16700de59e0d6801957290bcf956a0a", 493 | "type": "github" 494 | }, 495 | "original": { 496 | "owner": "haskell", 497 | "ref": "2.8.0.0", 498 | "repo": "haskell-language-server", 499 | "type": "github" 500 | } 501 | }, 502 | "hls-2.9": { 503 | "flake": false, 504 | "locked": { 505 | "lastModified": 1720003792, 506 | "narHash": "sha256-qnDx8Pk0UxtoPr7BimEsAZh9g2WuTuMB/kGqnmdryKs=", 507 | "owner": "haskell", 508 | "repo": "haskell-language-server", 509 | "rev": "0c1817cb2babef0765e4e72dd297c013e8e3d12b", 510 | "type": "github" 511 | }, 512 | "original": { 513 | "owner": "haskell", 514 | "ref": "2.9.0.1", 515 | "repo": "haskell-language-server", 516 | "type": "github" 517 | } 518 | }, 519 | "hpc-coveralls": { 520 | "flake": false, 521 | "locked": { 522 | "lastModified": 1607498076, 523 | "narHash": "sha256-8uqsEtivphgZWYeUo5RDUhp6bO9j2vaaProQxHBltQk=", 524 | "owner": "sevanspowell", 525 | "repo": "hpc-coveralls", 526 | "rev": "14df0f7d229f4cd2e79f8eabb1a740097fdfa430", 527 | "type": "github" 528 | }, 529 | "original": { 530 | "owner": "sevanspowell", 531 | "repo": "hpc-coveralls", 532 | "type": "github" 533 | } 534 | }, 535 | "hydra": { 536 | "inputs": { 537 | "libgit2": "libgit2", 538 | "nix": "nix", 539 | "nixpkgs": "nixpkgs" 540 | }, 541 | "locked": { 542 | "lastModified": 1671755331, 543 | "narHash": "sha256-hXsgJj0Cy0ZiCiYdW2OdBz5WmFyOMKuw4zyxKpgUKm4=", 544 | "owner": "NixOS", 545 | "repo": "hydra", 546 | "rev": "f48f00ee6d5727ae3e488cbf9ce157460853fea8", 547 | "type": "github" 548 | }, 549 | "original": { 550 | "id": "hydra", 551 | "type": "indirect" 552 | } 553 | }, 554 | "iohk-nix": { 555 | "inputs": { 556 | "blst": "blst", 557 | "nixpkgs": [ 558 | "nixpkgs" 559 | ], 560 | "secp256k1": "secp256k1", 561 | "sodium": "sodium" 562 | }, 563 | "locked": { 564 | "lastModified": 1734618971, 565 | "narHash": "sha256-5StB/VhWHOj3zlBxshqVFa6cwAE0Mk/wxRo3eEfcy74=", 566 | "owner": "input-output-hk", 567 | "repo": "iohk-nix", 568 | "rev": "dc900a3448e805243b0ed196017e8eb631e32240", 569 | "type": "github" 570 | }, 571 | "original": { 572 | "owner": "input-output-hk", 573 | "repo": "iohk-nix", 574 | "type": "github" 575 | } 576 | }, 577 | "iserv-proxy": { 578 | "flake": false, 579 | "locked": { 580 | "lastModified": 1717479972, 581 | "narHash": "sha256-7vE3RQycHI1YT9LHJ1/fUaeln2vIpYm6Mmn8FTpYeVo=", 582 | "owner": "stable-haskell", 583 | "repo": "iserv-proxy", 584 | "rev": "2ed34002247213fc435d0062350b91bab920626e", 585 | "type": "github" 586 | }, 587 | "original": { 588 | "owner": "stable-haskell", 589 | "ref": "iserv-syms", 590 | "repo": "iserv-proxy", 591 | "type": "github" 592 | } 593 | }, 594 | "libgit2": { 595 | "flake": false, 596 | "locked": { 597 | "lastModified": 1715853528, 598 | "narHash": "sha256-J2rCxTecyLbbDdsyBWn9w7r3pbKRMkI9E7RvRgAqBdY=", 599 | "owner": "libgit2", 600 | "repo": "libgit2", 601 | "rev": "36f7e21ad757a3dacc58cf7944329da6bc1d6e96", 602 | "type": "github" 603 | }, 604 | "original": { 605 | "owner": "libgit2", 606 | "ref": "v1.8.1", 607 | "repo": "libgit2", 608 | "type": "github" 609 | } 610 | }, 611 | "nix": { 612 | "inputs": { 613 | "flake-compat": [ 614 | "haskell-nix", 615 | "hydra" 616 | ], 617 | "flake-parts": [ 618 | "haskell-nix", 619 | "hydra" 620 | ], 621 | "git-hooks-nix": [ 622 | "haskell-nix", 623 | "hydra" 624 | ], 625 | "libgit2": [ 626 | "haskell-nix", 627 | "hydra", 628 | "libgit2" 629 | ], 630 | "nixpkgs": [ 631 | "haskell-nix", 632 | "hydra", 633 | "nixpkgs" 634 | ], 635 | "nixpkgs-23-11": [ 636 | "haskell-nix", 637 | "hydra" 638 | ], 639 | "nixpkgs-regression": [ 640 | "haskell-nix", 641 | "hydra" 642 | ] 643 | }, 644 | "locked": { 645 | "lastModified": 1737582558, 646 | "narHash": "sha256-nGb2MH9t/RI45BE8a08V6r9ORIUjX33rK26aSocON34=", 647 | "owner": "NixOS", 648 | "repo": "nix", 649 | "rev": "7616bae3427868ce351ccfd0c7e99af6ee068883", 650 | "type": "github" 651 | }, 652 | "original": { 653 | "owner": "NixOS", 654 | "ref": "2.24-maintenance", 655 | "repo": "nix", 656 | "type": "github" 657 | } 658 | }, 659 | "nix2container": { 660 | "inputs": { 661 | "flake-utils": "flake-utils_3", 662 | "nixpkgs": "nixpkgs_2" 663 | }, 664 | "locked": { 665 | "lastModified": 1730479402, 666 | "narHash": "sha256-79NLeNjpCa4mSasmFsE3QA6obURezF0TUO5Pm+1daog=", 667 | "owner": "nlewo", 668 | "repo": "nix2container", 669 | "rev": "5fb215a1564baa74ce04ad7f903d94ad6617e17a", 670 | "type": "github" 671 | }, 672 | "original": { 673 | "owner": "nlewo", 674 | "repo": "nix2container", 675 | "type": "github" 676 | } 677 | }, 678 | "nixpkgs": { 679 | "locked": { 680 | "lastModified": 1735651292, 681 | "narHash": "sha256-YLbzcBtYo1/FEzFsB3AnM16qFc6fWPMIoOuSoDwvg9g=", 682 | "owner": "NixOS", 683 | "repo": "nixpkgs", 684 | "rev": "0da3c44a9460a26d2025ec3ed2ec60a895eb1114", 685 | "type": "github" 686 | }, 687 | "original": { 688 | "owner": "NixOS", 689 | "ref": "nixos-24.05-small", 690 | "repo": "nixpkgs", 691 | "type": "github" 692 | } 693 | }, 694 | "nixpkgs-2003": { 695 | "locked": { 696 | "lastModified": 1620055814, 697 | "narHash": "sha256-8LEHoYSJiL901bTMVatq+rf8y7QtWuZhwwpKE2fyaRY=", 698 | "owner": "NixOS", 699 | "repo": "nixpkgs", 700 | "rev": "1db42b7fe3878f3f5f7a4f2dc210772fd080e205", 701 | "type": "github" 702 | }, 703 | "original": { 704 | "owner": "NixOS", 705 | "ref": "nixpkgs-20.03-darwin", 706 | "repo": "nixpkgs", 707 | "type": "github" 708 | } 709 | }, 710 | "nixpkgs-2105": { 711 | "locked": { 712 | "lastModified": 1659914493, 713 | "narHash": "sha256-lkA5X3VNMKirvA+SUzvEhfA7XquWLci+CGi505YFAIs=", 714 | "owner": "NixOS", 715 | "repo": "nixpkgs", 716 | "rev": "022caabb5f2265ad4006c1fa5b1ebe69fb0c3faf", 717 | "type": "github" 718 | }, 719 | "original": { 720 | "owner": "NixOS", 721 | "ref": "nixpkgs-21.05-darwin", 722 | "repo": "nixpkgs", 723 | "type": "github" 724 | } 725 | }, 726 | "nixpkgs-2111": { 727 | "locked": { 728 | "lastModified": 1659446231, 729 | "narHash": "sha256-hekabNdTdgR/iLsgce5TGWmfIDZ86qjPhxDg/8TlzhE=", 730 | "owner": "NixOS", 731 | "repo": "nixpkgs", 732 | "rev": "eabc38219184cc3e04a974fe31857d8e0eac098d", 733 | "type": "github" 734 | }, 735 | "original": { 736 | "owner": "NixOS", 737 | "ref": "nixpkgs-21.11-darwin", 738 | "repo": "nixpkgs", 739 | "type": "github" 740 | } 741 | }, 742 | "nixpkgs-2205": { 743 | "locked": { 744 | "lastModified": 1685573264, 745 | "narHash": "sha256-Zffu01pONhs/pqH07cjlF10NnMDLok8ix5Uk4rhOnZQ=", 746 | "owner": "NixOS", 747 | "repo": "nixpkgs", 748 | "rev": "380be19fbd2d9079f677978361792cb25e8a3635", 749 | "type": "github" 750 | }, 751 | "original": { 752 | "owner": "NixOS", 753 | "ref": "nixpkgs-22.05-darwin", 754 | "repo": "nixpkgs", 755 | "type": "github" 756 | } 757 | }, 758 | "nixpkgs-2211": { 759 | "locked": { 760 | "lastModified": 1688392541, 761 | "narHash": "sha256-lHrKvEkCPTUO+7tPfjIcb7Trk6k31rz18vkyqmkeJfY=", 762 | "owner": "NixOS", 763 | "repo": "nixpkgs", 764 | "rev": "ea4c80b39be4c09702b0cb3b42eab59e2ba4f24b", 765 | "type": "github" 766 | }, 767 | "original": { 768 | "owner": "NixOS", 769 | "ref": "nixpkgs-22.11-darwin", 770 | "repo": "nixpkgs", 771 | "type": "github" 772 | } 773 | }, 774 | "nixpkgs-2305": { 775 | "locked": { 776 | "lastModified": 1705033721, 777 | "narHash": "sha256-K5eJHmL1/kev6WuqyqqbS1cdNnSidIZ3jeqJ7GbrYnQ=", 778 | "owner": "NixOS", 779 | "repo": "nixpkgs", 780 | "rev": "a1982c92d8980a0114372973cbdfe0a307f1bdea", 781 | "type": "github" 782 | }, 783 | "original": { 784 | "owner": "NixOS", 785 | "ref": "nixpkgs-23.05-darwin", 786 | "repo": "nixpkgs", 787 | "type": "github" 788 | } 789 | }, 790 | "nixpkgs-2311": { 791 | "locked": { 792 | "lastModified": 1719957072, 793 | "narHash": "sha256-gvFhEf5nszouwLAkT9nWsDzocUTqLWHuL++dvNjMp9I=", 794 | "owner": "NixOS", 795 | "repo": "nixpkgs", 796 | "rev": "7144d6241f02d171d25fba3edeaf15e0f2592105", 797 | "type": "github" 798 | }, 799 | "original": { 800 | "owner": "NixOS", 801 | "ref": "nixpkgs-23.11-darwin", 802 | "repo": "nixpkgs", 803 | "type": "github" 804 | } 805 | }, 806 | "nixpkgs-2405": { 807 | "locked": { 808 | "lastModified": 1729242558, 809 | "narHash": "sha256-VgcLDu4igNT0eYua6OAl9pWCI0cYXhDbR+pWP44tte0=", 810 | "owner": "NixOS", 811 | "repo": "nixpkgs", 812 | "rev": "4a3f2d3195b60d07530574988df92e049372c10e", 813 | "type": "github" 814 | }, 815 | "original": { 816 | "owner": "NixOS", 817 | "ref": "nixpkgs-24.05-darwin", 818 | "repo": "nixpkgs", 819 | "type": "github" 820 | } 821 | }, 822 | "nixpkgs-stable": { 823 | "locked": { 824 | "lastModified": 1690680713, 825 | "narHash": "sha256-NXCWA8N+GfSQyoN7ZNiOgq/nDJKOp5/BHEpiZP8sUZw=", 826 | "owner": "NixOS", 827 | "repo": "nixpkgs", 828 | "rev": "b81af66deb21f73a70c67e5ea189568af53b1e8c", 829 | "type": "github" 830 | }, 831 | "original": { 832 | "owner": "NixOS", 833 | "repo": "nixpkgs", 834 | "rev": "b81af66deb21f73a70c67e5ea189568af53b1e8c", 835 | "type": "github" 836 | } 837 | }, 838 | "nixpkgs-unstable": { 839 | "locked": { 840 | "lastModified": 1729980323, 841 | "narHash": "sha256-eWPRZAlhf446bKSmzw6x7RWEE4IuZgAp8NW3eXZwRAY=", 842 | "owner": "NixOS", 843 | "repo": "nixpkgs", 844 | "rev": "86e78d3d2084ff87688da662cf78c2af085d8e73", 845 | "type": "github" 846 | }, 847 | "original": { 848 | "owner": "NixOS", 849 | "ref": "nixpkgs-unstable", 850 | "repo": "nixpkgs", 851 | "type": "github" 852 | } 853 | }, 854 | "nixpkgs_2": { 855 | "locked": { 856 | "lastModified": 1712920918, 857 | "narHash": "sha256-1yxFvUcJfUphK9V91KufIQom7gCsztza0H4Rz2VCWUU=", 858 | "owner": "NixOS", 859 | "repo": "nixpkgs", 860 | "rev": "92323443a56f4e9fc4e4b712e3119f66d0969297", 861 | "type": "github" 862 | }, 863 | "original": { 864 | "owner": "NixOS", 865 | "repo": "nixpkgs", 866 | "type": "github" 867 | } 868 | }, 869 | "nixpkgs_3": { 870 | "locked": { 871 | "lastModified": 1730768919, 872 | "narHash": "sha256-8AKquNnnSaJRXZxc5YmF/WfmxiHX6MMZZasRP6RRQkE=", 873 | "owner": "NixOS", 874 | "repo": "nixpkgs", 875 | "rev": "a04d33c0c3f1a59a2c1cb0c6e34cd24500e5a1dc", 876 | "type": "github" 877 | }, 878 | "original": { 879 | "owner": "NixOS", 880 | "ref": "nixpkgs-unstable", 881 | "repo": "nixpkgs", 882 | "type": "github" 883 | } 884 | }, 885 | "old-ghc-nix": { 886 | "flake": false, 887 | "locked": { 888 | "lastModified": 1631092763, 889 | "narHash": "sha256-sIKgO+z7tj4lw3u6oBZxqIhDrzSkvpHtv0Kki+lh9Fg=", 890 | "owner": "angerman", 891 | "repo": "old-ghc-nix", 892 | "rev": "af48a7a7353e418119b6dfe3cd1463a657f342b8", 893 | "type": "github" 894 | }, 895 | "original": { 896 | "owner": "angerman", 897 | "ref": "master", 898 | "repo": "old-ghc-nix", 899 | "type": "github" 900 | } 901 | }, 902 | "pre-commit-hooks-nix": { 903 | "inputs": { 904 | "flake-compat": "flake-compat_3", 905 | "gitignore": "gitignore", 906 | "nixpkgs": "nixpkgs_3" 907 | }, 908 | "locked": { 909 | "lastModified": 1737465171, 910 | "narHash": "sha256-R10v2hoJRLq8jcL4syVFag7nIGE7m13qO48wRIukWNg=", 911 | "owner": "cachix", 912 | "repo": "pre-commit-hooks.nix", 913 | "rev": "9364dc02281ce2d37a1f55b6e51f7c0f65a75f17", 914 | "type": "github" 915 | }, 916 | "original": { 917 | "owner": "cachix", 918 | "repo": "pre-commit-hooks.nix", 919 | "type": "github" 920 | } 921 | }, 922 | "root": { 923 | "inputs": { 924 | "CHaP": "CHaP", 925 | "easy-purescript-nix": "easy-purescript-nix", 926 | "flake-compat": "flake-compat", 927 | "flake-utils": "flake-utils_2", 928 | "hackage": "hackage", 929 | "haskell-nix": "haskell-nix", 930 | "iohk-nix": "iohk-nix", 931 | "nix2container": "nix2container", 932 | "nixpkgs": [ 933 | "haskell-nix", 934 | "nixpkgs" 935 | ], 936 | "nixpkgs-stable": "nixpkgs-stable", 937 | "pre-commit-hooks-nix": "pre-commit-hooks-nix", 938 | "sphinxcontrib-haddock": "sphinxcontrib-haddock" 939 | } 940 | }, 941 | "secp256k1": { 942 | "flake": false, 943 | "locked": { 944 | "lastModified": 1683999695, 945 | "narHash": "sha256-9nJJVENMXjXEJZzw8DHzin1DkFkF8h9m/c6PuM7Uk4s=", 946 | "owner": "bitcoin-core", 947 | "repo": "secp256k1", 948 | "rev": "acf5c55ae6a94e5ca847e07def40427547876101", 949 | "type": "github" 950 | }, 951 | "original": { 952 | "owner": "bitcoin-core", 953 | "ref": "v0.3.2", 954 | "repo": "secp256k1", 955 | "type": "github" 956 | } 957 | }, 958 | "sodium": { 959 | "flake": false, 960 | "locked": { 961 | "lastModified": 1675156279, 962 | "narHash": "sha256-0uRcN5gvMwO7MCXVYnoqG/OmeBFi8qRVnDWJLnBb9+Y=", 963 | "owner": "input-output-hk", 964 | "repo": "libsodium", 965 | "rev": "dbb48cce5429cb6585c9034f002568964f1ce567", 966 | "type": "github" 967 | }, 968 | "original": { 969 | "owner": "input-output-hk", 970 | "repo": "libsodium", 971 | "rev": "dbb48cce5429cb6585c9034f002568964f1ce567", 972 | "type": "github" 973 | } 974 | }, 975 | "sphinxcontrib-haddock": { 976 | "flake": false, 977 | "locked": { 978 | "lastModified": 1594136664, 979 | "narHash": "sha256-O9YT3iCUBHP3CEF88VDLLCO2HSP3HqkNA2q2939RnVY=", 980 | "owner": "michaelpj", 981 | "repo": "sphinxcontrib-haddock", 982 | "rev": "f3956b3256962b2d27d5a4e96edb7951acf5de34", 983 | "type": "github" 984 | }, 985 | "original": { 986 | "owner": "michaelpj", 987 | "repo": "sphinxcontrib-haddock", 988 | "type": "github" 989 | } 990 | }, 991 | "stackage": { 992 | "flake": false, 993 | "locked": { 994 | "lastModified": 1737850297, 995 | "narHash": "sha256-N6QJ+OfEWsgrE5OLVrHLyyXYlprG10JQifiPtb9vJkg=", 996 | "owner": "input-output-hk", 997 | "repo": "stackage.nix", 998 | "rev": "42381991e5202add671618c0ff93ad6577320995", 999 | "type": "github" 1000 | }, 1001 | "original": { 1002 | "owner": "input-output-hk", 1003 | "repo": "stackage.nix", 1004 | "type": "github" 1005 | } 1006 | }, 1007 | "systems": { 1008 | "locked": { 1009 | "lastModified": 1681028828, 1010 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 1011 | "owner": "nix-systems", 1012 | "repo": "default", 1013 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 1014 | "type": "github" 1015 | }, 1016 | "original": { 1017 | "owner": "nix-systems", 1018 | "repo": "default", 1019 | "type": "github" 1020 | } 1021 | }, 1022 | "systems_2": { 1023 | "locked": { 1024 | "lastModified": 1681028828, 1025 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 1026 | "owner": "nix-systems", 1027 | "repo": "default", 1028 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 1029 | "type": "github" 1030 | }, 1031 | "original": { 1032 | "owner": "nix-systems", 1033 | "repo": "default", 1034 | "type": "github" 1035 | } 1036 | }, 1037 | "systems_3": { 1038 | "locked": { 1039 | "lastModified": 1681028828, 1040 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 1041 | "owner": "nix-systems", 1042 | "repo": "default", 1043 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 1044 | "type": "github" 1045 | }, 1046 | "original": { 1047 | "owner": "nix-systems", 1048 | "repo": "default", 1049 | "type": "github" 1050 | } 1051 | } 1052 | }, 1053 | "root": "root", 1054 | "version": 7 1055 | } 1056 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Flake Templates for Projects at IOG"; 3 | 4 | inputs = { 5 | 6 | haskell-nix = { 7 | url = "github:input-output-hk/haskell.nix"; 8 | inputs.hackage.follows = "hackage"; 9 | }; 10 | 11 | nixpkgs.follows = "haskell-nix/nixpkgs"; 12 | 13 | # We use this to replace broken packages in haskell-nix/nixpkgs. 14 | nixpkgs-stable.url = 15 | "github:NixOS/nixpkgs/b81af66deb21f73a70c67e5ea189568af53b1e8c"; 16 | 17 | hackage = { 18 | url = "github:input-output-hk/hackage.nix"; 19 | flake = false; 20 | }; 21 | 22 | CHaP = { 23 | url = "github:IntersectMBO/cardano-haskell-packages?ref=repo"; 24 | flake = false; 25 | }; 26 | 27 | iohk-nix = { 28 | url = "github:input-output-hk/iohk-nix"; 29 | inputs.nixpkgs.follows = "nixpkgs"; 30 | }; 31 | 32 | sphinxcontrib-haddock = { 33 | url = "github:michaelpj/sphinxcontrib-haddock"; 34 | flake = false; 35 | }; 36 | 37 | pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; 38 | 39 | easy-purescript-nix = { 40 | url = "github:justinwoo/easy-purescript-nix"; 41 | flake = true; 42 | }; 43 | 44 | flake-utils.url = "github:numtide/flake-utils"; 45 | 46 | nix2container.url = "github:nlewo/nix2container"; 47 | 48 | flake-compat.url = "github:edolstra/flake-compat"; 49 | }; 50 | 51 | outputs = inputs: 52 | let 53 | mkFlake = import ./src/mkFlake.nix inputs; 54 | 55 | mkTestShell = lib: ghc: 56 | lib.iogx.mkShell { 57 | tools.haskellCompilerVersion = ghc; 58 | preCommit = { 59 | cabal-fmt.enable = true; 60 | stylish-haskell.enable = true; 61 | fourmolu.enable = true; 62 | hlint.enable = true; 63 | shellcheck.enable = true; 64 | prettier.enable = true; 65 | editorconfig-checker.enable = true; 66 | nixfmt-classic.enable = true; 67 | optipng.enable = true; 68 | purs-tidy.enable = true; 69 | rustfmt.enable = true; 70 | custom-hook = { 71 | enable = true; 72 | entry = "echo 'Running custom hook' ; exit 1"; 73 | pass_filenames = false; 74 | language = "fail"; 75 | }; 76 | }; 77 | }; 78 | 79 | mkTemplateOutputs = system: 80 | let 81 | vanilla = (import inputs.flake-compat { 82 | src = ./templates/vanilla; 83 | }).defaultNix; 84 | haskell = (import inputs.flake-compat { 85 | src = ./templates/haskell; 86 | }).defaultNix; 87 | in { 88 | vanilla = { devShells = vanilla.devShells.${system}; }; 89 | 90 | haskell = { 91 | devShells = haskell.devShells.${system}; 92 | packages = haskell.packages.${system}; 93 | checks = haskell.checks.${system}; 94 | hydraJobs = haskell.hydraJobs.${system}; 95 | }; 96 | }; 97 | 98 | in mkFlake rec { 99 | inherit inputs; 100 | 101 | repoRoot = ./.; 102 | 103 | systems = 104 | [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" "aarch64-linux" ]; 105 | 106 | flake = { repoRoot, inputs }: rec { 107 | 108 | templates.default = templates.vanilla; 109 | 110 | templates.haskell = { 111 | path = ./templates/haskell; 112 | description = "Flake Template for Haskell Projects"; 113 | welcomeText = '' 114 | # Flake Template for Haskell Projects 115 | Edit your cabal.project and run `nix develop` to enter the shell. 116 | ''; 117 | }; 118 | 119 | templates.vanilla = { 120 | path = ./templates/vanilla; 121 | description = "Flake Template for Blank Projects"; 122 | welcomeText = '' 123 | # Flake Template for Blank Projects 124 | Run `nix develop` to enter the shell. 125 | ''; 126 | }; 127 | 128 | lib = { 129 | inherit mkFlake; 130 | utils = import ./src/lib/utils.nix inputs; 131 | modularise = import ./src/lib/modularise.nix inputs; 132 | options = import ./src/options inputs; 133 | }; 134 | }; 135 | 136 | outputs = { repoRoot, inputs, pkgs, lib, system, ... }: [{ 137 | 138 | __debug = { inherit repoRoot pkgs; }; 139 | 140 | inherit repoRoot pkgs; # For debugging 141 | 142 | packages.rendered-iogx-api-reference = 143 | repoRoot.src.core.mkRenderedIogxApiReference; 144 | 145 | hydraJobs = { 146 | ghc810-shell = mkTestShell lib "ghc810"; 147 | ghc92-shell = mkTestShell lib "ghc92"; 148 | ghc96-shell = mkTestShell lib "ghc96"; 149 | ghc98-shell = mkTestShell lib "ghc98"; 150 | ghc910-shell = mkTestShell lib "ghc910"; 151 | rendered-iogx-api-reference = 152 | repoRoot.src.core.mkRenderedIogxApiReference; 153 | devShells.default = inputs.self.devShells.default; 154 | templates = mkTemplateOutputs system; 155 | required = lib.iogx.mkHydraRequiredJob { }; 156 | }; 157 | 158 | devShells.default = lib.iogx.mkShell { 159 | name = "iogx"; 160 | packages = 161 | [ pkgs.jq pkgs.github-cli pkgs.python39 pkgs.nix-prefetch-github ]; 162 | preCommit = { 163 | editorconfig-checker.enable = true; 164 | nixfmt-classic.enable = true; 165 | rustfmt.enable = true; 166 | }; 167 | scripts.render-iogx-api-reference = { 168 | group = "iogx"; 169 | description = "Generate ./doc/api.md"; 170 | exec = repoRoot.scripts."render-iogx-api-reference.sh"; 171 | }; 172 | scripts.find-repos-that-use-iogx = { 173 | group = "iogx"; 174 | description = "Find repos in input-output-hk that use iogx"; 175 | exec = repoRoot.scripts."find-repos-that-use-iogx.sh"; 176 | }; 177 | scripts.bump-iogx-everywhere = { 178 | group = "iogx"; 179 | description = "Create or update a PR to bump iogx in various repos"; 180 | exec = repoRoot.scripts."bump-iogx-everywhere.sh"; 181 | }; 182 | }; 183 | }]; 184 | }; 185 | 186 | nixConfig = { 187 | extra-substituters = [ "https://cache.iog.io" "https://cache.zw3rk.com" ]; 188 | extra-trusted-public-keys = [ 189 | "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" 190 | "loony-tools:pr9m4BkM/5/eSTZlkQyRt57Jz7OMBxNSUiMC4FkcNfk=" 191 | ]; 192 | allow-import-from-derivation = true; 193 | }; 194 | } 195 | -------------------------------------------------------------------------------- /scripts/bump-iogx-everywhere.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | create_draft_pr="no" 6 | custom_iogx_branch="custom-precommit-hooks" 7 | vbump_tag="2024-01-17" 8 | 9 | 10 | iogx_vbump_repo() { 11 | local repo_branch="$1" 12 | local repo_folder="$2" 13 | local add_no_changelog_required_label="$3" 14 | 15 | cd "../$repo_folder" || return 16 | git stash 17 | git checkout "$repo_branch" 18 | find . -name "*.DS_Store" -delete 19 | git pull 20 | git checkout -b "iogx-bump-$vbump_tag" || git checkout "iogx-bump-$vbump_tag" 21 | git pull --rebase origin "$repo_branch" 22 | if [ "$custom_iogx_branch" != "" ]; then 23 | if ! grep -q "github:input-output-hk/iogx?ref=$custom_iogx_branch" flake.nix; then 24 | sed -i "s|github:input-output-hk/iogx|github:input-output-hk/iogx?ref=$custom_iogx_branch|" flake.nix 25 | fi 26 | fi 27 | 28 | nix flake lock --update-input iogx 29 | git add . 30 | git commit -m "Bump IOGX $vbump_tag" --no-verify || true 31 | git push --force 32 | 33 | if [ "$create_draft_pr" == "yes" ]; then 34 | if [ "$add_no_changelog_required_label" == "yes" ]; then 35 | gh pr create --title "Bump IOGX $vbump_tag" --body "Ordinary version bump" --draft --label "No Changelog Required" 36 | else 37 | gh pr create --title "Bump IOGX $vbump_tag" --body "Ordinary version bump" --draft 38 | fi 39 | fi 40 | } 41 | 42 | 43 | iogx_vbump_plutus() { iogx_vbump_repo master plutus yes; } 44 | iogx_vbump_plutus-apps() { iogx_vbump_repo main plutus-apps yes; } 45 | iogx_vbump_marlowe-ts-sdk() { iogx_vbump_repo main marlowe-ts-sdk no; } 46 | iogx_vbump_marlowe-agda() { iogx_vbump_repo main marlowe-agda no; } 47 | iogx_vbump_marconi() { iogx_vbump_repo main marconi no; } 48 | iogx_vbump_dapps-certification() { iogx_vbump_repo master dapps-certification no; } 49 | iogx_vbump_marconi-sidechain-node() { iogx_vbump_repo main marconi-sidechain-node no; } 50 | iogx_vbump_marlowe() { iogx_vbump_repo master marlowe no; } 51 | iogx_vbump_quickcheck-dynamic() { iogx_vbump_repo main quickcheck-dynamic no; } 52 | iogx_vbump_marlowe-token-plans() { iogx_vbump_repo main marlowe-token-plans no; } 53 | iogx_vbump_marlowe-runner() { iogx_vbump_repo main marlowe-runner no; } 54 | iogx_vbump_marlowe-plutus() { iogx_vbump_repo main marlowe-plutus no; } 55 | iogx_vbump_marlowe-payouts() { iogx_vbump_repo main marlowe-payouts no; } 56 | iogx_vbump_marlowe-cardano() { iogx_vbump_repo main marlowe-cardano no; } 57 | iogx_vbump_stablecoin-plutus() { iogx_vbump_repo main stablecoin-plutus no; } 58 | iogx_vbump_quickcheck-contractmodel() { iogx_vbump_repo master quickcheck-contractmodel no; } 59 | iogx_vbump_antaeus() { iogx_vbump_repo main antaeus no; } 60 | iogx_vbump_marlowe-playground() { iogx_vbump_repo main marlowe-playground no; } 61 | 62 | 63 | iogx_vbump_everywhere() { 64 | iogx_vbump_plutus 65 | iogx_vbump_plutus-apps 66 | iogx_vbump_marlowe-ts-sdk 67 | iogx_vbump_marlowe-agda 68 | iogx_vbump_marconi 69 | iogx_vbump_dapps-certification 70 | iogx_vbump_marconi-sidechain-node 71 | iogx_vbump_marlowe 72 | iogx_vbump_quickcheck-dynamic 73 | iogx_vbump_marlowe-token-plans 74 | iogx_vbump_marlowe-runner 75 | iogx_vbump_marlowe-plutus 76 | iogx_vbump_marlowe-payouts 77 | iogx_vbump_marlowe-cardano 78 | iogx_vbump_stablecoin-plutus 79 | iogx_vbump_quickcheck-contractmodel 80 | iogx_vbump_antaeus 81 | iogx_vbump_marlowe-playground 82 | } 83 | 84 | 85 | iogx_vbump_$1 -------------------------------------------------------------------------------- /scripts/find-repos-that-use-iogx.sh: -------------------------------------------------------------------------------- 1 | # Scans all repositories in the input-output-hk GitHub organization and prints 2 | # those that use iogx flake as a direct or indirect dependency. 3 | 4 | if [ -z "$1" ]; then 5 | echo "usage: find-repos-that-use-iogx GITHUB_TOKEN" 6 | exit 1 7 | fi 8 | 9 | GITHUB_TOKEN="$1" 10 | 11 | check_one_repo() { 12 | local org_name="$1" 13 | local repo_obj="$2" 14 | local repo_name="$(echo "$repo_obj" | jq -r .name)" 15 | 16 | local flake_lock="$(mktemp)" 17 | local flake_nix="$(mktemp)" 18 | 19 | curl \ 20 | --max-time 5 \ 21 | -s \ 22 | -H "Authorization: token $GITHUB_TOKEN" \ 23 | -H 'Accept: application/vnd.github.v3.raw' \ 24 | -L "https://api.github.com/repos/$org_name/$repo_name/contents/flake.lock" \ 25 | > "$flake_lock" 26 | 27 | curl \ 28 | --max-time 5 \ 29 | -s \ 30 | -H "Authorization: token $GITHUB_TOKEN" \ 31 | -H 'Accept: application/vnd.github.v3.raw' \ 32 | -L "https://api.github.com/repos/$org_name/$repo_name/contents/flake.nix" \ 33 | > "$flake_nix" 34 | 35 | local iogx_flake_hash="$(jq -r '.nodes.iogx.locked.rev' "$flake_lock")" 36 | 37 | if [[ "$iogx_flake_hash" != "null" && "$iogx_flake_hash" != "" ]]; then 38 | local direct_dep="$(grep -q "iogx" "$flake_nix" && echo "*" || echo "indirect")" 39 | local timestamp="$(jq -r '.nodes.iogx.locked.lastModified' $flake_lock)" 40 | local datetime=$(date -d "@$timestamp" "+%Y-%m-%d") 41 | printf \ 42 | "%-64s %s %s %s\n" \ 43 | "https://www.github.com/$org_name/$repo_name" \ 44 | "$iogx_flake_hash" \ 45 | "$datetime" \ 46 | "$direct_dep" 47 | fi 48 | } 49 | 50 | 51 | check_all_repos() { 52 | local org_name="$1" 53 | local repos=$(gh repo list "$org_name" --json name --source --limit 1000 | jq -c '.[]') 54 | 55 | for repo in $repos; do 56 | check_one_repo "$org_name" "$repo" & 57 | done 58 | } 59 | 60 | 61 | check_all_organizations() { 62 | check_all_repos "input-output-hk" 63 | check_all_repos "IntersectMBO" 64 | } 65 | 66 | 67 | printf "%-64s %-40s %-10s %s\n" "repo" "iogx gitrev" "iogx time" "depend" 68 | check_all_organizations | sort -rk3 69 | 70 | wait -------------------------------------------------------------------------------- /scripts/render-iogx-api-reference.sh: -------------------------------------------------------------------------------- 1 | # A simple script to render the iogx API reference to a markdown file. 2 | # This script is available inside the iogx `nix develop` shell. 3 | # Remember to run this script before committing changes to GitHub. 4 | # TODO: make a git pre-commit hook that runs this. 5 | 6 | set -e 7 | nix build .#rendered-iogx-api-reference --show-trace "$@" 8 | cp result doc/api.md -------------------------------------------------------------------------------- /src/core/mkCombinedHaddock.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, iogx-inputs, pkgs, lib, ... }: 2 | 3 | # The haskell.nix cabalProject (or one of its flake variants) 4 | cabalProject: 5 | 6 | # The combinedHaddock submodule in mkHaskellProject-IN 7 | combinedHaddock: 8 | 9 | let 10 | 11 | # Haskell packages to make documentation for. Only those with a "doc" output will be used. 12 | # Note: we do not provide arbitrary additional Haddock options, as these would not be 13 | # applied consistently, since we're reusing the already built Haddock for the packages. 14 | hsPkgs = lib.attrValues 15 | (pkgs.haskell-nix.haskellLib.collectComponents' "library" 16 | (pkgs.haskell-nix.haskellLib.selectProjectPackages cabalProject.hsPkgs 17 | // (lib.filterAttrs (name: _: lib.elem name combinedHaddock.packages) 18 | cabalProject.hsPkgs))); 19 | 20 | # Optionally, a file to be used for the Haddock "--prologue" option. 21 | prologue = pkgs.writeTextFile { 22 | name = "prologue"; 23 | text = combinedHaddock.prologue; 24 | }; 25 | 26 | hsPkgs-docs = map (x: x.doc) (lib.filter (x: x ? doc) hsPkgs); 27 | 28 | the-mighty-command = '' 29 | hsdocsRec="$(cat graph* | grep -F /nix/store | sort | uniq)" 30 | 31 | # Merge all the docs from the packages and their doc dependencies. 32 | # We don't use symlinkJoin because: 33 | # - We are going to want to redistribute this, so we don't want any symlinks. 34 | # - We want to be selective about what we copy (we don't need the hydra 35 | # tarballs from the other packages, for example. 36 | mkdir -p "$out/share/doc" 37 | 38 | for pkg in $hsdocsRec; do 39 | if [ -d "$pkg/share/doc/ghc" ]; then 40 | echo "Skipping GHC :'(" 41 | elif [ -d "$pkg/share/doc" ]; then 42 | cp -r "$pkg/share/doc/." "$out/share/doc" 43 | fi 44 | done 45 | 46 | # We're going to sed all the files so they'd better be writable! 47 | chmod -R +w $out/share/doc 48 | 49 | # We're now going to rewrite all the pre-generated Haddock HTML output 50 | # so that links point to the appropriate place within our combined output, 51 | # rather than into the store. 52 | root=$out/share/doc 53 | for f in $(find $out -name "*.html"); do 54 | # Replace all links to the docs we're processing with relative links 55 | # to the root of the doc directory we're creating - the rest of the link is 56 | # the same. 57 | # Also, it's not a a file:// link now because it's a relative URL instead 58 | # of an absolute one. 59 | relpath=$(realpath --relative-to=$(dirname $f) --no-symlinks $root) 60 | pkgsRegex="${"file:///nix/store/[^/]*/share/doc"}" 61 | sed -i -r "s,$pkgsRegex,$relpath,g" "$f" 62 | done 63 | 64 | # Move to the docdir. We do this so that we can give relative docpaths to 65 | # Haddock so it will generate relative (relocatable) links in the index. 66 | cd $out/share/doc 67 | # Non-recursively collect all the interface files and their docpaths 68 | # (in this case we can just use the enclosing directory). 69 | interfaceOpts=() 70 | for pkg in ${lib.concatStringsSep " " hsPkgs-docs}; do 71 | pushd $pkg/share/doc 72 | for interfaceFile in $(find . -name "*.haddock"); do 73 | # this is '$PACKAGE/html' 74 | docdir=$(dirname $interfaceFile) 75 | interfaceOpts+=("--read-interface=$docdir,$interfaceFile") 76 | # Jam this in here for now 77 | pushd $out/share/doc 78 | ${repoRoot.src.ext.sphinxcontrib-haddock.sphinxcontrib-haddock}/bin/haddock_inventory $docdir 79 | popd 80 | done 81 | popd 82 | done 83 | 84 | # Generate the contents and index 85 | ${cabalProject.pkg-set.config.ghc.package}/bin/haddock \ 86 | --gen-contents \ 87 | --gen-index \ 88 | --quickjump \ 89 | ${lib.optionalString (prologue != null) "--prologue ${prologue}"} \ 90 | "''${interfaceOpts[@]}" 91 | 92 | # TODO: remove patch when haddock > 2.24.0 93 | # patch quick-jump.css to fix scrolling in search for chromium 94 | for f in $(find $out -name "quick-jump.css"); do 95 | sed -i -r "s,^\#search-results \{,\#search-results \{ max-height:80%;overflow-y:scroll;," "$f" 96 | done 97 | 98 | # Following: https://github.com/input-output-hk/ouroboros-network/blob/2068d091bc7dcd3f4538fb76f1b598f219d1e0c8/scripts/haddocs.sh#L87 # editorconfig-checker-disable-line 99 | # Assemble a toplevel `doc-index.json` from package level ones. 100 | shopt -s globstar 101 | echo "[]" > "doc-index.json" 102 | for file in $(ls **/doc-index.json); do 103 | project=$(dirname $file); 104 | ${pkgs.jq}/bin/jq -s ".[0] + [.[1][] | (. + {link: (\"$project/\" + .link)}) ]" \ 105 | "doc-index.json" "$file" > doc-index.tmp.json 106 | mv doc-index.tmp.json "doc-index.json" 107 | done 108 | 109 | echo "Done Combining Haddock" 110 | ''; 111 | 112 | command-args = { 113 | buildInputs = [ hsPkgs-docs ]; 114 | 115 | # For each package in hsdocs, this will create a file `graph-N` (where N is the index in the list) 116 | # which contains information about which nix paths are referenced by the package. This will allow 117 | # us to resolve hyperlinks to haddocks elsewhere in the store. 118 | # 119 | # See also https://nixos.org/manual/nix/stable/expressions/advanced-attributes.html#adv-attr-exportReferencesGraph # editorconfig-checker-disable-line 120 | exportReferencesGraph = lib.concatLists 121 | (lib.imap0 (i: pkg: [ "graph-${toString i}" pkg ]) hsPkgs-docs); 122 | }; 123 | 124 | combined-haddock = 125 | pkgs.runCommand "combine-haddock" command-args the-mighty-command; 126 | 127 | dummy-combined-haddock = pkgs.runCommand "dummy-combined-haddock" { } '' 128 | mkdir -p $out/share/doc 129 | echo "No packages to combine." > $out/share/doc/index.html 130 | ''; 131 | 132 | in if combinedHaddock.enable then combined-haddock else dummy-combined-haddock 133 | -------------------------------------------------------------------------------- /src/core/mkContainerFromCabalExe.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, iogx-inputs, user-inputs, pkgs, lib, system, ... }: 2 | 3 | mkContainerFromCabalExe-IN: 4 | 5 | let 6 | inherit (iogx-inputs.nix2container.packages) nix2container; 7 | 8 | evaluated-modules = lib.evalModules { 9 | modules = [{ 10 | options = lib.iogx.options; 11 | config."mkContainerFromCabalExe." = mkContainerFromCabalExe-IN; 12 | }]; 13 | }; 14 | 15 | userConfig = evaluated-modules.config."mkContainerFromCabalExe."; 16 | 17 | name = if lib.isNull userConfig.name then 18 | userConfig.exe.exeName 19 | else 20 | userConfig.name; 21 | 22 | license = let ls = userConfig.exe.meta.license; 23 | in if (lib.isAttrs ls) then 24 | ls.spdxId 25 | else if (lib.isList ls && [ ] != ls) then 26 | let l = (lib.head ls); in if lib.isAttrs l then l.spdxId else null 27 | else 28 | null; 29 | 30 | labels = lib.pipe { 31 | inherit license; 32 | inherit (userConfig) description; 33 | source = userConfig.sourceUrl; 34 | } [ 35 | (lib.filterAttrs (k: v: v != null)) 36 | (lib.mapAttrs' (k: v: lib.nameValuePair "org.opencontainers.image.${k}" v)) 37 | ]; 38 | 39 | rootPackages = [ 40 | # Provide some tools for users who want to enter a shell in the running 41 | # container. 42 | (pkgs.buildEnv { 43 | name = "base"; 44 | paths = [ pkgs.bashInteractive pkgs.coreutils ]; 45 | pathsToLink = [ "/bin" ]; 46 | }) 47 | 48 | # Some networked applications need cacerts on the machine 49 | pkgs.cacert 50 | 51 | # Fixes networking on some platforms 52 | pkgs.fakeNss 53 | ] ++ lib.optional 54 | (!lib.isNull userConfig.packages && userConfig.packages != [ ]) 55 | (pkgs.buildEnv { 56 | name = "user"; 57 | paths = userConfig.packages; 58 | pathsToLink = [ "/bin" ]; 59 | }); 60 | in nix2container.buildImage ({ 61 | inherit name; 62 | 63 | config = { 64 | entryPoint = lib.singleton (lib.getExe userConfig.exe); 65 | } // lib.optionalAttrs (labels != { }) { Labels = labels; }; 66 | 67 | copyToRoot = rootPackages; 68 | }) 69 | -------------------------------------------------------------------------------- /src/core/mkGitRevProjectOverlay.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, iogx-inputs, user-inputs, pkgs, lib, system, ... }: 2 | 3 | mkGitRevProjectOverlay-IN: 4 | 5 | let 6 | 7 | evaluated-modules = lib.evalModules { 8 | modules = [{ 9 | options = lib.iogx.options; 10 | config."mkGitRevProjectOverlay." = mkGitRevProjectOverlay-IN; 11 | }]; 12 | }; 13 | 14 | args = evaluated-modules.config."mkGitRevProjectOverlay."; 15 | 16 | overlay = _: prev: { 17 | hsPkgs = 18 | prev.pkgs.pkgsHostTarget.setGitRevForPaths prev.pkgs.gitrev args.exePaths 19 | prev.hsPkgs; 20 | }; 21 | 22 | in args.project.appendOverlays [ overlay ] 23 | -------------------------------------------------------------------------------- /src/core/mkHaskellProject.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, iogx-inputs, user-inputs, pkgs, lib, system, ... }: 2 | 3 | # The project config provided by the user. 4 | mkHaskellProject-IN: 5 | 6 | let 7 | 8 | utils = lib.iogx.utils; 9 | 10 | evaluated-modules = lib.evalModules { 11 | modules = [{ 12 | options = lib.iogx.options; 13 | config."mkHaskellProject." = mkHaskellProject-IN; 14 | }]; 15 | }; 16 | 17 | haskellProject = evaluated-modules.config."mkHaskellProject."; 18 | 19 | readTheDocs = lib.recursiveUpdate haskellProject.readTheDocs { 20 | 21 | sphinxToolchain = if haskellProject.readTheDocs.sphinxToolchain == null then 22 | repoRoot.src.ext.sphinx-toolchain 23 | else 24 | haskellProject.readTheDocs.sphinxToolchain; 25 | }; 26 | 27 | mkAliasedOutputs = flake: 28 | let 29 | makeAliasesForGroup = group: 30 | let 31 | mkPair = name: lib.nameValuePair (makeAliasForComp name); 32 | isExe = lib.strings.hasInfix ":exe"; 33 | isTest = lib.strings.hasInfix ":test"; 34 | isBench = lib.strings.hasInfix ":bench"; 35 | isRunnable = name: isExe name || isTest name || isBench name; 36 | runnables = lib.filterAttrs (name: _: isRunnable name) group; 37 | in lib.mapAttrs' mkPair runnables; 38 | 39 | makeAliasForComp = name: lib.last (lib.splitString ":" name); 40 | 41 | findDuplicateComps = group: 42 | let names = lib.attrNames (makeAliasesForGroup group); 43 | in utils.findDuplicates names; 44 | 45 | makeAliasesForGroupIfNoDuplicates = group: 46 | let duplicates = findDuplicateComps group; 47 | in if lib.length duplicates == 0 then 48 | makeAliasesForGroup group 49 | else 50 | errorDuplicateComponentNames duplicates { }; 51 | 52 | errorDuplicateComponentNames = duplicates: 53 | utils.iogxThrow '' 54 | There are multiple components with the same name across your cabal files: 55 | 56 | ${lib.concatStringsSep " " duplicates} 57 | 58 | Therefore I cannot create unique flake outputs for those. 59 | Rename the components across your cabal files so that they are unique.''; 60 | 61 | outputs = { 62 | apps = makeAliasesForGroupIfNoDuplicates flake.apps; 63 | checks = makeAliasesForGroupIfNoDuplicates flake.checks; 64 | packages = makeAliasesForGroupIfNoDuplicates flake.packages; 65 | }; 66 | in outputs; 67 | 68 | mkProjectShellProfile = project: 69 | let 70 | devshell = pkgs.haskell-nix.haskellLib.devshellFor project.shell; 71 | packages = devshell.packages; 72 | env = lib.listToAttrs devshell.env; 73 | in { inherit packages env; }; 74 | 75 | mkProjectDevShell = project: 76 | let 77 | read-the-docs-profile = 78 | repoRoot.src.core.mkReadTheDocsShellProfile readTheDocs; 79 | cabal-project-profile = mkProjectShellProfile project; 80 | shell-profiles = [ cabal-project-profile read-the-docs-profile ]; 81 | 82 | tools-args = { 83 | tools.haskellCompilerVersion = project.args.compiler-nix-name; 84 | }; 85 | project-args = haskellProject.shellArgs project; 86 | shell-args = lib.recursiveUpdate tools-args project-args; 87 | in repoRoot.src.core.mkShellWith shell-args shell-profiles; 88 | 89 | mkProjectDevShell2 = project: 90 | let 91 | read-the-docs-profile = 92 | repoRoot.src.core.mkReadTheDocsShellProfile readTheDocs; 93 | tools-args = { 94 | tools.haskellCompilerVersion = project.args.compiler-nix-name; 95 | }; 96 | project-args = haskellProject.shellArgs project; 97 | shell-args = lib.recursiveUpdate tools-args project-args; 98 | iogx-shell = 99 | repoRoot.src.core.mkShellWith shell-args [ read-the-docs-profile ]; 100 | final-shell = project.shell.overrideAttrs (attrs: 101 | attrs // { 102 | buildInputs = attrs.buildInputs ++ iogx-shell.buildInputs; 103 | shellHook = '' 104 | ${attrs.shellHook} 105 | ${iogx-shell.shellHook}''; 106 | }); 107 | in final-shell // { inherit (iogx-shell) pre-commit-check tools; }; 108 | 109 | mkProjectVariantOutputs = project: 110 | let 111 | # Replace mkProjectDevShell2 with mkProjectDevShell when this is merged: 112 | # https://github.com/input-output-hk/haskell.nix/pull/2163 113 | # Or keep mkProjectDevShell2 forever since it seems to be working fine. 114 | devShell = mkProjectDevShell2 project; 115 | devShells.default = devShell; 116 | 117 | originalFlake = 118 | pkgs.haskell-nix.haskellLib.mkFlake project { inherit devShell; }; 119 | 120 | inherit (mkAliasedOutputs originalFlake) apps checks packages; 121 | 122 | inherit (originalFlake) hydraJobs; 123 | 124 | combined-haddock = repoRoot.src.core.mkCombinedHaddock project 125 | haskellProject.combinedHaddock; 126 | read-the-docs-site = 127 | repoRoot.src.core.mkReadTheDocsSite readTheDocs combined-haddock; 128 | pre-commit-check = devShell.pre-commit-check; 129 | in { 130 | cabalProject = project; 131 | inherit apps checks packages devShells devShell hydraJobs; 132 | inherit combined-haddock read-the-docs-site pre-commit-check; 133 | }; 134 | 135 | mkCrossVariantOutputs = project: 136 | let 137 | flake = pkgs.haskell-nix.haskellLib.mkFlake project { }; 138 | hydraJobs = removeAttrs flake.hydraJobs [ "devShell" "devShells" ]; 139 | in { 140 | cabalProject = project; 141 | inherit hydraJobs; 142 | }; 143 | 144 | mkDefaultFlake = project: 145 | let 146 | extra-packages = { 147 | combined-haddock = project.combined-haddock; 148 | read-the-docs-site = project.read-the-docs-site; 149 | pre-commit-check = project.pre-commit-check; 150 | }; 151 | 152 | mingwW64-jobs = lib.optionalAttrs 153 | (system == "x86_64-linux" && haskellProject.includeMingwW64HydraJobs) { 154 | mingwW64 = project.cross.mingwW64.hydraJobs; 155 | }; 156 | 157 | variants-jobs = let 158 | all = utils.mapAttrValues (project: project.hydraJobs) project.variants; 159 | in if haskellProject.includeProfiledHydraJobs then 160 | all 161 | else 162 | removeAttrs all [ "profiled" ]; 163 | 164 | required-job = { required = repoRoot.src.core.mkHydraRequiredJob { }; }; 165 | in { 166 | devShell = project.devShell; 167 | devShells = project.devShells; 168 | apps = project.apps; 169 | checks = project.checks; 170 | cabalProject = project.cabalProject; 171 | 172 | packages = project.packages // extra-packages; 173 | 174 | hydraJobs = required-job // project.hydraJobs // extra-packages 175 | // variants-jobs // mingwW64-jobs; 176 | }; 177 | 178 | iogx-project = let 179 | base = haskellProject.cabalProject; 180 | 181 | mkProjectVariant = project: 182 | (mkProjectVariantOutputs project) // { 183 | cross = utils.mapAttrValues mkCrossVariantOutputs project.projectCross; 184 | }; 185 | 186 | # { cabalProject, cross, variants 187 | # , combined-haddock, read-the-docs-site pre-commit-check 188 | # , packages/checks/apps/devShells/hydraJobs } 189 | project = (mkProjectVariant base) // { 190 | variants = utils.mapAttrValues mkProjectVariant base.projectVariants; 191 | }; 192 | 193 | flake = mkDefaultFlake project; 194 | in project // { inherit flake; }; 195 | 196 | in iogx-project 197 | -------------------------------------------------------------------------------- /src/core/mkHydraRequiredJob.nix: -------------------------------------------------------------------------------- 1 | { user-inputs, pkgs, lib, system, ... }: 2 | 3 | # The required job is a special job that succeeds when all other hydraJobs succeed. 4 | 5 | { ... }: 6 | 7 | let 8 | 9 | clean-jobs = 10 | lib.filterAttrsRecursive (name: _: name != "recurseForDerivations") 11 | (removeAttrs user-inputs.self.hydraJobs.${system} [ "required" ]); 12 | 13 | required-job = pkgs.releaseTools.aggregate { 14 | name = "required"; 15 | meta.description = "All jobs required to pass CI"; 16 | constituents = lib.collect lib.isDerivation clean-jobs; 17 | }; 18 | 19 | in required-job 20 | -------------------------------------------------------------------------------- /src/core/mkMergedShellProfiles.nix: -------------------------------------------------------------------------------- 1 | { lib, pkgs, ... }: 2 | 3 | # A simple utility to merge two shell profiles together. 4 | 5 | let 6 | utils = lib.iogx.utils; 7 | 8 | mergeTwoShellProfiles = p1: p2: { 9 | 10 | packages = utils.getAttrWithDefault "packages" [ ] p1 11 | ++ utils.getAttrWithDefault "packages" [ ] p2; 12 | 13 | scripts = let 14 | scripts1 = utils.getAttrWithDefault "scripts" { } p1; 15 | scripts2 = utils.getAttrWithDefault "scripts" { } p2; 16 | # TODO check clashes 17 | in scripts1 // scripts2; 18 | 19 | env = let 20 | env1 = utils.getAttrWithDefault "env" { } p1; 21 | env2 = utils.getAttrWithDefault "env" { } p2; 22 | # TODO check clashes 23 | in env1 // env2; 24 | 25 | shellHook = lib.concatStringsSep "\n" [ 26 | (utils.getAttrWithDefault "shellHook" "" p1) 27 | (utils.getAttrWithDefault "shellHook" "" p2) 28 | ]; 29 | 30 | tools = let 31 | tools1 = utils.getAttrWithDefault "tools" { } p1; 32 | tools2 = utils.getAttrWithDefault "tools" { } p2; 33 | # TODO check clashes 34 | in tools1 // tools2; 35 | 36 | preCommit = let 37 | pre1 = utils.getAttrWithDefault "preCommit" { } p1; 38 | pre2 = utils.getAttrWithDefault "preCommit" { } p2; 39 | # TODO check clashes 40 | in pre1 // pre2; 41 | }; 42 | 43 | mkMergedShellProfiles = lib.foldl' mergeTwoShellProfiles { }; 44 | 45 | in mkMergedShellProfiles 46 | -------------------------------------------------------------------------------- /src/core/mkReadTheDocsShellProfile.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, pkgs, ... }: 2 | 3 | # Return an attrset containing packages and scripts for developing a RTD site. 4 | 5 | # The readTheDocs submodule in mkHaskellProject-IN 6 | readTheDocs: 7 | 8 | let 9 | 10 | shell-profile = { 11 | 12 | packages = [ repoRoot.src.ext.sphinx-toolchain pkgs.python3 ]; 13 | 14 | scripts.develop-rtd-site = { 15 | description = "Develop your site live in ${readTheDocs.siteFolder}"; 16 | group = "read-the-docs"; 17 | exec = '' 18 | repo_root="$(git rev-parse --show-toplevel)" 19 | doc="$repo_root/${readTheDocs.siteFolder}" 20 | sphinx-autobuild -j 4 -n "$doc" "$doc/_build" 21 | ''; 22 | }; 23 | 24 | scripts.build-rtd-site = { 25 | description = "Build your site in ${readTheDocs.siteFolder}"; 26 | group = "read-the-docs"; 27 | exec = '' 28 | repo_root="$(git rev-parse --show-toplevel)" 29 | doc="$repo_root/${readTheDocs.siteFolder}" 30 | sphinx-build -j 4 -n "$doc" "$doc/_build" 31 | ''; 32 | }; 33 | 34 | scripts.serve-rtd-site = { 35 | description = "Build with nix and then serve your site at localhost:8002"; 36 | group = "read-the-docs"; 37 | exec = '' 38 | nix build .#read-the-docs-site --out-link result "$@" 39 | (cd result && python -m http.server 8002) 40 | ''; 41 | }; 42 | }; 43 | 44 | in if readTheDocs.enable then shell-profile else { } 45 | 46 | -------------------------------------------------------------------------------- /src/core/mkReadTheDocsSite.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, pkgs, lib, user-inputs, ... }: 2 | 3 | # Derivation for a read-the-docs site. 4 | 5 | # The readTheDocs submodule in mkHaskellProject-IN 6 | readTheDocs: 7 | 8 | # Derivation comptuted by mkCombinedHaddock.nix 9 | combined-haddock: 10 | 11 | let 12 | 13 | site = pkgs.stdenv.mkDerivation { 14 | 15 | name = "read-the-docs-site"; 16 | 17 | src = lib.sourceFilesBySuffices 18 | (user-inputs.self + "/${readTheDocs.siteFolder}") [ 19 | ".py" 20 | ".rst" 21 | ".md" 22 | ".hs" 23 | ".png" 24 | ".svg" 25 | ".bib" 26 | ".csv" 27 | ".css" 28 | ".html" 29 | ".txt" 30 | ".json" 31 | ]; 32 | 33 | buildInputs = [ 34 | readTheDocs.sphinxToolchain 35 | # We need this here in order to get the `plantuml` executable in PATH. 36 | # Unfortunately `python3.withPackages` (used by sphinxToolchain above) 37 | # won't do it automatically. 38 | readTheDocs.sphinxToolchain.pkgs.sphinxcontrib_plantuml 39 | ]; 40 | 41 | dontInstall = true; 42 | 43 | buildPhase = '' 44 | cp -aR ${combined-haddock}/share/doc haddock 45 | # -n gives warnings on missing link targets, -W makes warnings into errors 46 | SPHINX_HADDOCK_DIR=haddock sphinx-build -W -n . $out 47 | cp -aR haddock $out 48 | ''; 49 | }; 50 | 51 | dummy-site = pkgs.runCommand "dummy-read-the-docs-site" { } '' 52 | mkdir -p $out 53 | echo "This is a dummy read-the-docs site." > $out/index.html 54 | ''; 55 | 56 | in if readTheDocs.enable then site else dummy-site 57 | -------------------------------------------------------------------------------- /src/core/mkRenderedIogxApiReference.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, inputs, ... }: 2 | 3 | # Generate custom markdown documentation for the options of iogx, using the 4 | # nixosOptionsDoc function from nix. 5 | 6 | let 7 | 8 | evaluated-modules = lib.evalModules { 9 | modules = [{ 10 | options = { 11 | inherit (inputs.self.lib.options) 12 | # Note: the `.` submodules are rendered implicitly. 13 | "flake.nix" 14 | mkFlake 15 | mkHaskellProject 16 | mkShell 17 | mkContainerFromCabalExe; 18 | }; 19 | }]; 20 | }; 21 | 22 | 23 | options-doc = pkgs.nixosOptionsDoc { 24 | options = evaluated-modules.options; 25 | transformOptions = opt: opt; 26 | }; 27 | 28 | 29 | options-doc-nix = options-doc.optionsNix; 30 | 31 | 32 | options-doc-markdown = 33 | lib.concatStringsSep "\n" 34 | (lib.mapAttrsToList mkMarkdownForOption options-doc-nix); 35 | 36 | 37 | cleanupName = name: 38 | lib.replaceStrings 39 | [ "\"\"" "\"\"" "" ] 40 | [ "" "" "" ] 41 | name; 42 | 43 | 44 | prettyPrintValue = value: 45 | if value._type == "literalExpression" && lib.hasInfix "\n" value.text then 46 | "\n```nix\n${value.text}\n```" 47 | else if value._type == "literalExpression" && !lib.hasInfix "\n" value.text then 48 | "`${value.text}`" 49 | else if value._type != "literalExpression" && lib.hasInfix "\n" value.text then 50 | "\n```nix\n${lib.toJSON value.text}\n```" 51 | else if value._type != "literalExpression" && !lib.hasInfix "\n" value.text then 52 | "`${lib.toJSON value.text}`" 53 | else 54 | ""; 55 | 56 | 57 | mkMarkdownForOption = name: value: '' 58 | --- 59 | 60 | ### `${cleanupName name}` 61 | 62 | **Type**: ${value.type} 63 | 64 | ${ 65 | if lib.hasAttr "default" value then 66 | '' 67 | **Default**: ${prettyPrintValue value.default} 68 | '' 69 | else 70 | "" 71 | } 72 | ${ 73 | if value.readOnly then "**Read Only**" else "" 74 | } 75 | ${ 76 | if lib.hasAttr "example" value then 77 | '' 78 | **Example**: ${prettyPrintValue value.example} 79 | '' 80 | else 81 | "" 82 | } 83 | 84 | ${value.description} 85 | ''; 86 | 87 | in 88 | 89 | pkgs.writeText "api.md" '' 90 | 91 | # API Reference 92 | 93 | 1. ${lib.iogx.utils.headerToMarkDownLink "flake.nix" "flake.nix"} 94 | - Top-level `flake.nix` file. 95 | 2. ${lib.iogx.utils.headerToMarkDownLink "inputs.iogx.lib.mkFlake" "mkFlake"} 96 | - Makes your flake outputs. 97 | 3. ${lib.iogx.utils.headerToMarkDownLink "pkgs.lib.iogx.mkHaskellProject" "mkHaskellProject"} 98 | - Makes a [`haskell.nix`](https://github.com/input-output-hk/haskell.nix) project. 99 | 4. ${lib.iogx.utils.headerToMarkDownLink "pkgs.lib.iogx.mkShell" "mkShell"} 100 | - Makes a `devShell` with `pre-commit-check` and tools. 101 | 5. ${lib.iogx.utils.headerToMarkDownLink "pkgs.lib.iogx.mkContainerFromCabalExe" "mkContainerFromCabalExe"} 102 | - Makes a OCI compliant container using an exe defined with cabal. 103 | 104 | ${options-doc-markdown} 105 | '' 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/core/mkShell.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, ... }: 2 | 3 | # Create an actual nix devShell with a bunch of tools and utilities. 4 | 5 | # The shell config provided by the user. 6 | mkShell-IN: 7 | 8 | repoRoot.src.core.mkShellWith mkShell-IN [ ] 9 | -------------------------------------------------------------------------------- /src/core/mkShellUtilityScripts.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, user-inputs, system, ... }: 2 | 3 | # Create a bunch of general-purpose scripts that can be added to the shell. 4 | 5 | # A valid shell. 6 | mkShell-IN: 7 | 8 | let 9 | 10 | utils = lib.iogx.utils; 11 | 12 | partitionScriptsByGroup = scripts: 13 | let 14 | getGroup = script: 15 | utils.getAttrWithDefault "group" "ungrouped" script.value; 16 | nameValToScript = script: { "${script.name}" = script.value; }; 17 | groupToScripts = _: namevals: 18 | utils.recursiveUpdateMany (map nameValToScript namevals); 19 | pairs = lib.mapAttrsToList lib.nameValuePair scripts; 20 | groups = lib.groupBy getGroup pairs; 21 | partitioned = lib.mapAttrs groupToScripts groups; 22 | in partitioned; 23 | 24 | list-flake-outputs = let 25 | formatGroup = group: command: 26 | if lib.hasAttr group user-inputs.self && user-inputs.self.${group} 27 | != { } then 28 | let 29 | mkCommand = name: _: "nix ${command} .#${utils.ansiBold name}"; 30 | commands = 31 | lib.mapAttrsToList mkCommand user-inputs.self.${group}.${system}; 32 | in '' 33 | ${utils.ansiColor group "purple" "bold"} 34 | 35 | ${lib.concatStringsSep "\n" commands}'' 36 | else 37 | ""; 38 | 39 | formatted-outputs = lib.concatStringsSep "\n\n" (utils.filterEmptyStrings [ 40 | (formatGroup "packages" "build") 41 | (formatGroup "apps" "run") 42 | (formatGroup "checks" "run") 43 | (formatGroup "devShells" "develop") 44 | ]); 45 | 46 | script = { 47 | group = "general"; 48 | description = "List the flake outputs buildable by nix"; 49 | exec = '' 50 | echo 51 | printf "${formatted-outputs}" 52 | echo 53 | ''; 54 | }; 55 | in script; 56 | 57 | info = let 58 | all-scripts = let 59 | filterDisabled = lib.filterAttrs (_: { enable ? true, ... }: enable); 60 | shell-scripts = 61 | filterDisabled (utils.getAttrWithDefault "scripts" { } mkShell-IN); 62 | extra-scripts = { inherit info list-flake-outputs; }; 63 | in shell-scripts // extra-scripts; 64 | 65 | formatGroup = group: scripts: 66 | let 67 | formatScript = name: script: '' 68 | — ${utils.ansiBold name} ∷ ${script.description or ""} 69 | ''; 70 | formatted-group = 71 | lib.concatStrings (lib.mapAttrsToList formatScript scripts); 72 | in '' 73 | ${utils.ansiColor "λ ${group}" "purple" "bold"} 74 | ${formatted-group} 75 | ''; 76 | 77 | formatted-script-groups = let groups = partitionScriptsByGroup all-scripts; 78 | in lib.concatStrings (lib.mapAttrsToList formatGroup groups); 79 | 80 | formatted-env = let 81 | internal-vars = 82 | [ "NIX_GHC_LIBDIR" "PKG_CONFIG_PATH" "CABAL_CONFIG" "LOCALE_ARCHIVE" ]; 83 | shell-env = utils.getAttrWithDefault "env" { } mkShell-IN; 84 | final-env = removeAttrs shell-env internal-vars; 85 | formatVar = var: val: '' 86 | — ${utils.ansiBold var} ∷ ${val} 87 | ''; 88 | body = lib.concatStrings (lib.mapAttrsToList formatVar final-env); 89 | content = if body == "" then 90 | "" 91 | else '' 92 | ${utils.ansiColor "λ environment" "purple" "bold"} 93 | ${body} 94 | ''; 95 | in content; 96 | 97 | content = lib.optionalString (formatted-env != "") formatted-env 98 | + "${formatted-script-groups}"; 99 | 100 | script = { 101 | group = "general"; 102 | description = "Print this message"; 103 | exec = '' 104 | echo 105 | printf "${content}"''; 106 | }; 107 | in script; 108 | 109 | utility-scripts = { inherit info list-flake-outputs; }; 110 | 111 | in utility-scripts 112 | -------------------------------------------------------------------------------- /src/core/mkShellWith.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, iogx-inputs, user-inputs, pkgs, lib, system, ... }: 2 | 3 | # Create an actual nix devShell with a bunch of tools and utilities. 4 | 5 | # The shell config provided by the user. 6 | mkShell-IN: 7 | 8 | # Extra profiles. Internally these will be read-the-docs and haskell-nix. 9 | extra-shell-profiles: 10 | 11 | let 12 | 13 | utils = lib.iogx.utils; 14 | 15 | evaluated-shell-module = lib.evalModules { 16 | modules = [{ 17 | options = lib.iogx.options; 18 | config."mkShell." = mkShell-IN; 19 | }]; 20 | }; 21 | 22 | shell' = evaluated-shell-module.config."mkShell."; 23 | 24 | shell = lib.recursiveUpdate shell' { 25 | prompt = if shell'.prompt == null then '' 26 | 27 | \[\033[1;32m\][${shell'.name}:\w]\$\[\033[0m\] '' else 28 | shell'.prompt; 29 | 30 | welcomeMessage = if shell'.welcomeMessage == null then 31 | "🤟 \\033[1;31mWelcome to ${shell'.name}\\033[0m 🤟" 32 | else 33 | shell'.welcomeMessage; 34 | }; 35 | 36 | ghc = if shell.tools.haskellCompilerVersion == null then 37 | "ghc96" 38 | else 39 | shell.tools.haskellCompilerVersion; 40 | 41 | hls = repoRoot.src.ext.haskell-language-server-project ghc; 42 | 43 | purescript = pkgs.callPackage iogx-inputs.easy-purescript-nix { }; 44 | 45 | default-tools = { 46 | cabal-install = repoRoot.src.ext.cabal-install ghc; 47 | haskell-language-server = 48 | hls.hsPkgs.haskell-language-server.components.exes.haskell-language-server; 49 | haskell-language-server-wrapper = 50 | hls.hsPkgs.haskell-language-server.components.exes.haskell-language-server-wrapper; 51 | 52 | # When using mkHaskellProject, this will be overriden by the ghcWithPackages provided by haskell.nix's shell. 53 | ghc = hls.pkg-set.config.ghc.package; 54 | 55 | stylish-haskell = 56 | hls.hsPkgs.stylish-haskell.components.exes.stylish-haskell; 57 | hlint = hls.hsPkgs.hlint.components.exes.hlint; 58 | cabal-fmt = repoRoot.src.ext.cabal-fmt; 59 | fourmolu = repoRoot.src.ext.fourmolu; 60 | 61 | shellcheck = pkgs.shellcheck; 62 | prettier = pkgs.nodePackages.prettier; 63 | editorconfig-checker = pkgs.editorconfig-checker; 64 | nixfmt-classic = pkgs.nixfmt-classic; 65 | optipng = pkgs.optipng; 66 | purs-tidy = purescript.purs-tidy; 67 | rustfmt = pkgs.rustfmt; 68 | }; 69 | 70 | getTool = name: 71 | if utils.getAttrWithDefault name null shell.tools == null then 72 | default-tools.${name} 73 | else 74 | shell.tools.${name}; 75 | 76 | shell-tools = { 77 | ghc = getTool "ghc"; 78 | cabal-install = getTool "cabal-install"; 79 | cabal-fmt = getTool "cabal-fmt"; 80 | stylish-haskell = getTool "stylish-haskell"; 81 | fourmolu = getTool "fourmolu"; 82 | hlint = getTool "hlint"; 83 | shellcheck = getTool "shellcheck"; 84 | prettier = getTool "prettier"; 85 | editorconfig-checker = getTool "editorconfig-checker"; 86 | nixfmt-classic = getTool "nixfmt-classic"; 87 | optipng = getTool "optipng"; 88 | purs-tidy = getTool "purs-tidy"; 89 | rustfmt = getTool "rustfmt"; 90 | haskell-language-server = getTool "haskell-language-server"; 91 | haskell-language-server-wrapper = getTool "haskell-language-server-wrapper"; 92 | }; 93 | 94 | mkBuiltinPreCommitHook = name: hook: rec { 95 | package = shell-tools.${name}; 96 | 97 | pass_filenames = true; 98 | 99 | files = if hook ? include then 100 | "\\.(${lib.concatStringsSep "|" hook.include})$" 101 | else 102 | ""; 103 | 104 | # TODO once nixfmt-classic has resolved naming conflicts, we can remove this `if` expression. 105 | entry = 106 | lib.getExe' package (if name == "nixfmt-classic" then "nixfmt" else name) 107 | + " " + utils.getAttrWithDefault "options" "" hook; 108 | }; 109 | 110 | builtin-pre-commit-hooks = lib.mapAttrs mkBuiltinPreCommitHook { 111 | cabal-fmt = { 112 | options = "--inplace"; 113 | include = [ "cabal" ]; 114 | }; 115 | 116 | stylish-haskell = { 117 | options = "--inplace --config .stylish-haskell.yaml"; 118 | include = [ "hs" "lhs" ]; 119 | }; 120 | 121 | fourmolu = { 122 | options = "--mode inplace"; 123 | include = [ "hs" "lhs" ]; 124 | }; 125 | 126 | hlint = { 127 | options = "--hint=.hlint.yaml"; 128 | include = [ "hs" "lhs" ]; 129 | }; 130 | 131 | shellcheck = { include = [ "sh" ]; }; 132 | 133 | prettier = { include = [ "ts" "js" "css" "html" ]; }; 134 | 135 | editorconfig-checker = { options = "-config .editorconfig"; }; 136 | 137 | nixfmt-classic = { include = [ "nix" ]; }; 138 | 139 | optipng = { include = [ "png" ]; }; 140 | 141 | purs-tidy = { 142 | options = "format-in-place"; 143 | include = [ "purs" ]; 144 | }; 145 | 146 | rustfmt = { include = [ "rs" ]; }; 147 | }; 148 | 149 | # Note that this is a bit of a hack, this attrses is a valid pre-commit-hook 150 | # minus extra fields "extraOptions", "package", which 151 | # are only used internally by this module, however `extraOptions` can also be 152 | # provided by the user. 153 | mkAugmentedPreCommitHook = name: hook: { 154 | package = utils.getAttrWithDefault "package" null hook; 155 | 156 | entry = utils.getAttrWithDefault "entry" "" hook + " " 157 | + utils.getAttrWithDefault "extraOptions" "" hook; 158 | 159 | enable = utils.getAttrWithDefault "enable" false hook; 160 | name = utils.getAttrWithDefault "name" name hook; 161 | types = utils.getAttrWithDefault "types" [ "file" ] hook; 162 | files = utils.getAttrWithDefault "files" "" hook; 163 | excludes = utils.getAttrWithDefault "excludes" [ ] hook; 164 | language = utils.getAttrWithDefault "language" "system" hook; 165 | pass_filenames = utils.getAttrWithDefault "pass_filenames" false hook; 166 | }; 167 | 168 | augmented-pre-commit-hooks = lib.mapAttrs mkAugmentedPreCommitHook 169 | (lib.recursiveUpdate builtin-pre-commit-hooks shell.preCommit); 170 | 171 | toolchain-profile = let 172 | should-include-haskell-tools = shell.tools.haskellCompilerVersion != null 173 | || augmented-pre-commit-hooks.cabal-fmt.enable 174 | || augmented-pre-commit-hooks.stylish-haskell.enable 175 | || augmented-pre-commit-hooks.fourmolu.enable 176 | || augmented-pre-commit-hooks.hlint.enable; 177 | 178 | haskell-tools = [ 179 | shell-tools.haskell-language-server 180 | shell-tools.haskell-language-server-wrapper 181 | shell-tools.cabal-install 182 | shell-tools.cabal-fmt 183 | shell-tools.stylish-haskell 184 | shell-tools.fourmolu 185 | shell-tools.hlint 186 | shell-tools.ghc 187 | ]; 188 | 189 | pre-commit-packages = 190 | let getPkg = _: hook: if hook.enable then hook.package else null; 191 | in lib.mapAttrsToList getPkg augmented-pre-commit-hooks; 192 | 193 | packages = pre-commit-packages 194 | ++ lib.optional should-include-haskell-tools haskell-tools; 195 | in { inherit packages; }; 196 | 197 | toValidPreCommitHook = name: hook: 198 | lib.mkForce (removeAttrs hook [ "extraOptions" "package" ]); 199 | 200 | pre-commit-check = iogx-inputs.pre-commit-hooks-nix.lib.${system}.run { 201 | src = lib.cleanSource user-inputs.self; 202 | hooks = lib.mapAttrs toValidPreCommitHook augmented-pre-commit-hooks; 203 | # options = {}; # TODO users might want to set this... 204 | }; 205 | 206 | pre-commit-profile = { 207 | packages = [ pkgs.pre-commit ]; 208 | shellHook = pre-commit-check.shellHook; 209 | }; 210 | 211 | shell-as-shell-profile = 212 | removeAttrs shell [ "name" "prompt" "welcomeMessage" ]; 213 | 214 | name-and-welcome-message-profile = { 215 | shellHook = '' 216 | export PS1="${shell.prompt}" 217 | echo 218 | printf "${shell.welcomeMessage}" 219 | echo 220 | echo 221 | echo "Type 'info' to see what's inside this shell." 222 | ''; 223 | }; 224 | 225 | local-archive-profile.env.LOCALE_ARCHIVE = 226 | lib.optionalString (pkgs.stdenv.hostPlatform.libc == "glibc") 227 | ("${pkgs.glibcLocales}/lib/locale/locale-archive"); 228 | 229 | base-profile = repoRoot.src.core.mkMergedShellProfiles (extra-shell-profiles 230 | ++ [ 231 | pre-commit-profile 232 | shell-as-shell-profile 233 | local-archive-profile 234 | toolchain-profile 235 | name-and-welcome-message-profile 236 | ]); 237 | 238 | utility-scripts-profile = { 239 | scripts = repoRoot.src.core.mkShellUtilityScripts base-profile; 240 | }; 241 | 242 | final-profile = repoRoot.src.core.mkMergedShellProfiles [ 243 | base-profile 244 | utility-scripts-profile 245 | ]; 246 | 247 | final-scripts-as-packages = let 248 | removeDisabled = lib.filterAttrs (_: { enable ? true, ... }: enable); 249 | enabled-scripts = removeDisabled final-profile.scripts; 250 | scriptToPackage = name: script: 251 | pkgs.writeShellScriptBin name "${script.exec}"; 252 | in lib.mapAttrsToList scriptToPackage enabled-scripts; 253 | 254 | final-env-as-bash = 255 | let exportVar = key: val: ''export ${key}="${toString val}"''; 256 | in lib.concatStringsSep "\n" 257 | (lib.mapAttrsToList exportVar final-profile.env); 258 | 259 | devShell' = pkgs.mkShell { 260 | name = shell.name; 261 | buildInputs = final-profile.packages ++ final-scripts-as-packages; 262 | shellHook = '' 263 | ${final-profile.shellHook} 264 | ${final-env-as-bash} 265 | ''; 266 | }; 267 | 268 | devShell = devShell' // { 269 | tools = shell-tools; 270 | inherit pre-commit-check; 271 | }; 272 | 273 | in devShell 274 | -------------------------------------------------------------------------------- /src/ext/cabal-fmt.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | 5 | project = pkgs.haskell-nix.hackage-project { 6 | name = "cabal-fmt"; 7 | 8 | version = "0.1.12"; 9 | 10 | compiler-nix-name = "ghc96"; 11 | 12 | # Cabal is a lib library, so haskell.nix would normally use the one coming 13 | # from the compiler-nix-name (currently 3.2). However cabal-fmt depends on 14 | # Cabal library version 3.6, hence we add this line. 15 | modules = [{ 16 | reinstallableLibGhc = true; 17 | 18 | packages.cabal-fmt.components.exes.cabal-fmt.dontStrip = false; 19 | }]; 20 | }; 21 | 22 | in project.hsPkgs.cabal-fmt.components.exes.cabal-fmt 23 | -------------------------------------------------------------------------------- /src/ext/cabal-install.nix: -------------------------------------------------------------------------------- 1 | { pkgs, lib, ... }: 2 | 3 | ghc: 4 | 5 | pkgs.haskell-nix.tool ghc "cabal-install" "latest" 6 | -------------------------------------------------------------------------------- /src/ext/fourmolu.nix: -------------------------------------------------------------------------------- 1 | # Normally we would want to get fourmolu from HLS, so as to have the same version 2 | # both in HLS and in pre-commit-check. However the fourmolu provided by HLS is 3 | # too old for our needs, and so we build it from source. 4 | { pkgs, ... }: 5 | 6 | let 7 | project = pkgs.haskell-nix.hackage-project { 8 | name = "fourmolu"; 9 | version = "0.16.2.0"; 10 | compiler-nix-name = "ghc98"; 11 | 12 | modules = 13 | [{ packages.fourmolu.components.exes.fourmolu.dontStrip = false; }]; 14 | }; 15 | 16 | in project.hsPkgs.fourmolu.components.exes.fourmolu 17 | -------------------------------------------------------------------------------- /src/ext/haskell-language-server-project.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, lib, pkgs, ... }: 2 | 3 | # Builds haskell-language-server using the given ghc version. 4 | 5 | ghc: 6 | 7 | let 8 | 9 | config = if lib.hasInfix "ghc810" ghc then { 10 | rev = "855a88238279b795634fa6144a4c0e8acc7e9644"; # 1.8.0.0 11 | sha256 = "sha256-El5wZDn0br/My7cxstRzUyO7VUf1q5V44T55NEQONnI="; 12 | cabalProjectLocal = "constraints: stylish-haskell==0.13.0.0, hlint==3.2.8"; 13 | } else if lib.hasInfix "ghc92" ghc then { 14 | rev = "1916b5782d9f3204d25a1d8f94da4cfd83ae2607"; # 1.9.0.0 15 | sha256 = "sha256-j3XRQTWa7jsVlimaxFZNnlE9IzWII9Prj1/+otks5FQ="; 16 | cabalProjectLocal = "constraints: stylish-haskell==0.14.2.0, hlint==3.4.1"; 17 | } else if lib.hasInfix "ghc96" ghc then { 18 | rev = "2.8.0.0"; 19 | sha256 = "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0="; 20 | cabalProjectLocal = "constraints: stylish-haskell ^>= 0.14, hlint ^>= 3.8"; 21 | configureArgs = "--disable-benchmarks"; 22 | } 23 | # TODO replace with ghc98 when HLS supports ghc983 24 | else if lib.hasInfix "ghc982" ghc then { 25 | rev = "2.8.0.0"; 26 | sha256 = "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0="; 27 | cabalProjectLocal = "constraints: stylish-haskell ^>= 0.14, hlint ^>= 3.8"; 28 | configureArgs = "--disable-benchmarks"; 29 | } else if lib.hasInfix "ghc910" ghc then { 30 | rev = "2.9.0.0"; 31 | sha256 = "sha256-THXSz+iwB1yQQsr/PY151+2GvtoJnTIB2pIQ4OzfjD4="; 32 | configureArgs = "--disable-benchmarks"; 33 | # TODO remove this once HLS fully supports ghc910 with hlint and stylish-haskell 34 | hls-fallback = repoRoot.src.ext.haskell-language-server-project "ghc98"; 35 | } else 36 | lib.trace '' 37 | Unsupported GHC version ${ghc}, defaulting to ghc982 and haskell-language-server v2.8.0.0 38 | '' { 39 | ghc = "ghc982"; 40 | rev = "2.8.0.0"; 41 | sha256 = "sha256-Vi/iUt2pWyUJlo9VrYgTcbRviWE0cFO6rmGi9rmALw0="; 42 | cabalProjectLocal = 43 | "constraints: stylish-haskell ^>= 0.14, hlint ^>= 3.8"; 44 | configureArgs = "--disable-benchmarks"; 45 | }; 46 | 47 | project = pkgs.haskell-nix.cabalProject' { 48 | # See https://github.com/haskell/haskell-language-server/issues/411. 49 | # We want to use stylish-haskell, hlint, and implicit-hie as standalone tools 50 | # *and* through HLS. But we need to have consistent versions in both cases, 51 | # otherwise e.g. you could format the code in HLS and then have the CI 52 | # complain that it's wrong 53 | # 54 | # The solution we use here is to: 55 | # a) Where we care (mostly just formatters), constrain the versions of 56 | # tools which HLS uses explicitly 57 | # b) Pull out the tools themselves from the HLS project so we can use 58 | # them elsewhere 59 | cabalProjectLocal = config.cabalProjectLocal or ""; 60 | 61 | configureArgs = config.configureArgs or ""; 62 | 63 | src = pkgs.fetchFromGitHub { 64 | owner = "haskell"; 65 | repo = "haskell-language-server"; 66 | inherit (config) rev sha256; 67 | }; 68 | 69 | compiler-nix-name = config.ghc or ghc; 70 | 71 | sha256map = { 72 | "https://github.com/pepeiborra/ekg-json"."7a0af7a8fd38045fd15fb13445bdcc7085325460" = 73 | "sha256-fVwKxGgM0S4Kv/4egVAAiAjV7QB5PBqMVMCfsv7otIQ="; # editorconfig-checker-disable-line 74 | }; 75 | 76 | modules = [{ 77 | # See https://github.com/haskell/haskell-language-server/pull/1382#issuecomment-780472005 78 | packages.ghcide.flags.ghc-patched-unboxed-bytecode = true; 79 | 80 | dontStrip = false; 81 | }]; 82 | }; 83 | 84 | project' = let hls-fallback = config.hls-fallback or project; 85 | in lib.recursiveUpdate project { 86 | hsPkgs.hlint = hls-fallback.hsPkgs.hlint; 87 | hsPkgs.stylish-haskell = hls-fallback.hsPkgs.stylish-haskell; 88 | }; 89 | 90 | in project' 91 | -------------------------------------------------------------------------------- /src/ext/scriv.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | 5 | package = { lib, buildPythonPackage, fetchPypi, attrs, click, click-log 6 | , requests, jinja2 }: 7 | 8 | buildPythonPackage rec { 9 | 10 | pname = "scriv"; 11 | 12 | version = "0.17.0"; 13 | 14 | src = fetchPypi { 15 | inherit pname version; 16 | sha256 = "sha256-jyOIPvg9/FDwn3au8I/zBz8nUsclXbFdJM2L/swyN5w="; 17 | }; 18 | 19 | propagatedBuildInputs = [ attrs click click-log jinja2 requests ]; 20 | 21 | doCheck = false; 22 | 23 | meta = with lib; { 24 | homepage = "https://github.com/nedbat/scriv"; 25 | description = "Maintain useful changelogs."; 26 | maintainers = with maintainers; [ michaelpj ]; 27 | }; 28 | }; 29 | 30 | in pkgs.python3Packages.callPackage package { } 31 | 32 | -------------------------------------------------------------------------------- /src/ext/sphinx-markdown-tables.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | 5 | package = { lib, buildPythonPackage, fetchPypi, markdown }: 6 | 7 | buildPythonPackage rec { 8 | 9 | pname = "sphinx-markdown-tables"; 10 | 11 | version = "0.0.17"; 12 | 13 | src = fetchPypi { 14 | inherit pname version; 15 | sha256 = "a8bT1ADqzP7r0ohEa8CN2DCDNnxYuF1A/mwS1371kvE="; 16 | }; 17 | 18 | propagatedBuildInputs = [ markdown ]; 19 | 20 | doCheck = false; 21 | 22 | meta = with lib; { 23 | homepage = "https://github.com/ryanfox/sphinx-markdown-tables"; 24 | description = ""; 25 | maintainers = with maintainers; [ michaelpj ]; 26 | }; 27 | }; 28 | 29 | in pkgs.python3Packages.callPackage package { } 30 | -------------------------------------------------------------------------------- /src/ext/sphinx-toolchain.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, pkgs, ... }: 2 | 3 | let sphinxcontrib-haddock = repoRoot.src.ext.sphinxcontrib-haddock; 4 | 5 | in pkgs.python3.withPackages (py: [ 6 | 7 | repoRoot.src.ext.sphinxcontrib-bibtex 8 | repoRoot.src.ext.sphinx-markdown-tables 9 | repoRoot.src.ext.sphinxemoji 10 | 11 | sphinxcontrib-haddock.sphinxcontrib-haddock 12 | sphinxcontrib-haddock.sphinxcontrib-domaintools 13 | 14 | py.sphinxcontrib_plantuml 15 | py.sphinx-autobuild 16 | py.sphinx 17 | py.sphinx_rtd_theme 18 | py.recommonmark 19 | ]) 20 | -------------------------------------------------------------------------------- /src/ext/sphinxcontrib-bibtex.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | pkgs.python3Packages.sphinxcontrib-bibtex.overrideAttrs (oldAttrs: rec { 4 | 5 | version = "2.2.0"; 6 | 7 | src = pkgs.python3Packages.fetchPypi { 8 | inherit (oldAttrs) pname; 9 | inherit version; 10 | sha256 = "1cp3dj5bbl122d64i3vbqhjhfplnh1rwm9dw4cy9hxjd2lz8803m"; 11 | }; 12 | }) 13 | -------------------------------------------------------------------------------- /src/ext/sphinxcontrib-haddock.nix: -------------------------------------------------------------------------------- 1 | { iogx-inputs, pkgs, ... }: 2 | 3 | pkgs.callPackage iogx-inputs.sphinxcontrib-haddock { 4 | pythonPackages = pkgs.python3Packages; 5 | } 6 | -------------------------------------------------------------------------------- /src/ext/sphinxemoji.nix: -------------------------------------------------------------------------------- 1 | { pkgs, ... }: 2 | 3 | let 4 | 5 | package = { lib, buildPythonPackage, fetchPypi, sphinx }: 6 | buildPythonPackage rec { 7 | pname = "sphinxemoji"; 8 | 9 | version = "0.1.6"; 10 | 11 | src = fetchPypi { 12 | inherit pname version; 13 | sha256 = "1s2w8hn9kfcg371l9msn8vnmdjmhih9pc1mhr9i4l0j54xsrgrwf"; 14 | }; 15 | 16 | propagatedBuildInputs = [ sphinx ]; 17 | 18 | doCheck = false; 19 | 20 | meta = with lib; { 21 | homepage = "https://github.com/sphinx-contrib/emojicodes"; 22 | description = ""; 23 | maintainers = with maintainers; [ michaelpj ]; 24 | }; 25 | }; 26 | 27 | in pkgs.python3Packages.callPackage package { } 28 | 29 | -------------------------------------------------------------------------------- /src/lib/modularise.nix: -------------------------------------------------------------------------------- 1 | # This file provides a function to import a directory and its contents as a nix 2 | # attribute set. The iogx flake exports it in `inputs.iogx.lib.modularise`. 3 | 4 | iogx-inputs: 5 | 6 | { 7 | # The path to the folder to be imported (e.g. inputs.self or ../my/folder) 8 | root 9 | # The string to use as the name of the folder (e.g. "root" or "src") 10 | , module 11 | # An attrset of arguments to pass to each nix files. 12 | # The final attrset will also have an attribute with a name equal to the value 13 | # of `module` 14 | , args 15 | # If true, debug information will be traced as files are read and evaluated 16 | , debug ? false }: 17 | 18 | # Often in nix code the same variables are passed around to different nix files. 19 | # Hance why most nix files are functions from an attrset (containing some common 20 | # variables) to some another nix value. 21 | # Ordinarily one would use the `import` keyword to import nix files, followed by 22 | # the common arguments. 23 | # modularise.nix provides a way to avoid this boilerplate by allowing you to 24 | # reference a folder and its contents as a nix attrset, assuming that the nix 25 | # files inside that folder are all functions taking a known attrset. 26 | 27 | let 28 | 29 | usage-example = let 30 | # The contents of ./some/existing/folder: 31 | # - cabal.project 32 | # - main.nix 33 | # * src 34 | # - Main.hs 35 | # * nix 36 | # - outputs.nix 37 | # - alpha.nix 38 | # * bravo 39 | # - charlie.nix 40 | # - india.nix 41 | # - hotel.json 42 | # * delta 43 | # - echo.nix 44 | # - golf.txt 45 | # 46 | # The conents of nix/alpha.nix: 47 | # { my-folder, my, common, vars, ... }: 48 | # "result" 49 | # 50 | # The conents of nix/bravo/charlie.nix: 51 | # { my-folder, my, common, vars, ... }: 52 | # my-folder."cabal.project" 53 | # 54 | example-module = import ./modularise.nix { 55 | root = ./some/existing/folder; 56 | module = "my-folder"; 57 | args = { 58 | my = "my-value"; 59 | common = "common-value"; 60 | vars = [ ]; 61 | }; 62 | }; 63 | 64 | # NOTE: nix files do not need the ".nix" suffix, while files with any other 65 | # extension (e.g. `golf.txt`) must include the full name to be referenced. 66 | # In the case of non-nix files, internally modularise.nix calls 67 | # `builtins.readFile` to read the contents of that file. 68 | some-result = let 69 | a = example-module.nix.alpha; 70 | c = example-module.nix.bravo.charlie; 71 | e = example-module.nix.bravo.delta.echo "arg1" { }; 72 | f = example-module.nix.bravo.delta."golf.txt"; 73 | g = example-module.src."Main.hs"; 74 | in 42; 75 | in some-result; 76 | 77 | l = builtins // iogx-inputs.nixpkgs.lib; 78 | 79 | fileToModule = dir: path: 80 | if !l.pathExists "${dir}/${path}" then 81 | l.throw 82 | ("[modularise.nix] there is no file/folder named ${path} in directory ${dir}") 83 | else if l.hasSuffix ".nix" path then 84 | let 85 | name = l.removeSuffix ".nix" path; 86 | # TODO check that import "${dir}/${path}" is a function and warn otherwise 87 | value = import "${dir}/${path}" (args // { ${module} = __module__; }); 88 | trace = l.trace ("[modularise.nix] importing ${dir}/${path}"); 89 | value' = if debug then trace value else value; 90 | in l.nameValuePair name value 91 | else # non-Nix file 92 | l.nameValuePair path (l.readFile "${dir}/${path}"); 93 | 94 | dirToModule = dir: path: 95 | let 96 | name = path; 97 | value = l.mapAttrs' (pathToModule "${dir}/${path}") 98 | (l.readDir "${dir}/${path}"); 99 | in l.nameValuePair name value; 100 | 101 | pathToModule = dir: path: type: 102 | if type == "directory" then 103 | dirToModule dir path 104 | else if type == "regular" then 105 | fileToModule dir path 106 | else 107 | l.throw "[modularise.nix] unexpected file ${dir}/${path} of type ${type}"; 108 | 109 | mkModule = path: 110 | if !l.pathExists path then 111 | l.throw "[modularise.nix] path ${path} does not exist" 112 | else 113 | (dirToModule "" path).value; 114 | 115 | __module__ = mkModule (l.toPath root); 116 | 117 | in __module__ 118 | -------------------------------------------------------------------------------- /src/lib/utils.nix: -------------------------------------------------------------------------------- 1 | # This file contains utility functions that are used by the iogx codebase. 2 | # The iogx flake exports it in `inputs.iogx.lib.utils`. 3 | 4 | iogx-inputs: 5 | 6 | let 7 | 8 | l = builtins // iogx-inputs.nixpkgs.lib; 9 | 10 | utils = rec { 11 | 12 | # When rendered, each header in and .md file gets its own url. 13 | # The url is constructed by sanitizing the header's text. 14 | # This function takes the desired link's display text and the header's text 15 | # and returns a valid markdown link. 16 | headerToMarkDownLink = tag: text: 17 | let 18 | text' = l.replaceStrings [ "<" ">" "." " " ] [ "" "" "" "-" ] text; 19 | text-lower = l.strings.toLower text'; 20 | in "[`${tag}`](#${text-lower})"; 21 | 22 | recursiveUpdateMany = l.foldl' l.recursiveUpdate { }; 23 | 24 | ptrace = x: y: l.trace (valueToString x) y; 25 | ptraceAttrNames = s: l.trace (l.attrNames s) s; 26 | ptraceShow = x: ptrace x x; 27 | 28 | valueToString = x: 29 | if x == null then 30 | "null" 31 | else if l.typeOf x == "set" then 32 | "{${ 33 | l.concatStringsSep " " 34 | (l.mapAttrsToList (k: v: "${k}=${valueToString v};") x) 35 | }}" 36 | else if l.typeOf x == "list" then 37 | "[${l.concatStringsSep " " (map valueToString x)}]" 38 | else if l.typeOf x == "string" then 39 | ''"${x}"'' 40 | else if l.typeOf x == "bool" then 41 | if x then "true" else "false" 42 | else if l.typeOf x == "lambda" then 43 | "" 44 | else 45 | toString x; 46 | 47 | # Is this not in the stdlib? 48 | getAttrWithDefault = name: def: set: 49 | if l.hasAttr name set then l.getAttr name set else def; 50 | 51 | ansiColor = text: fg: style: 52 | let 53 | colors = { 54 | black = ";30"; 55 | red = ";31"; 56 | green = ";32"; 57 | yellow = ";33"; 58 | blue = ";34"; 59 | purple = ";35"; 60 | cyan = ";36"; 61 | white = ";37"; 62 | }; 63 | 64 | colorBit = getAttrWithDefault fg "" colors; 65 | 66 | boldBit = if style == "bold" then "1" else "0"; 67 | in "\\033[${boldBit}${colorBit}m${text}\\033[0m"; 68 | 69 | ansiBold = text: ansiColor text "" "bold"; 70 | 71 | mapAttrValues = f: l.mapAttrs (_: f); 72 | 73 | iogxThrow = errmsg: 74 | l.throw '' 75 | 76 | ------------------------------------ IOGX -------------------------------------- 77 | ${errmsg} 78 | -------------------------------------------------------------------------------- 79 | ''; 80 | 81 | iogxTrace = errmsg: 82 | l.trace '' 83 | 84 | ------------------------------------ IOGX -------------------------------------- 85 | ${errmsg} 86 | -------------------------------------------------------------------------------- 87 | ''; 88 | 89 | # Stolen from https://github.com/divnix/nosys 90 | deSystemize = let 91 | iteration = cutoff: system: fragment: 92 | if !(l.isAttrs fragment) || cutoff == 0 then 93 | fragment 94 | else 95 | let recursed = l.mapAttrs (_: iteration (cutoff - 1) system) fragment; 96 | in if l.hasAttr "${system}" fragment then 97 | if l.isFunction fragment.${system} then 98 | recursed // { __functor = _: fragment.${system}; } 99 | else 100 | recursed // fragment.${system} 101 | else 102 | recursed; 103 | in iteration 3; 104 | 105 | filterEmptyStrings = l.filter (x: x != ""); 106 | 107 | findDuplicates = list: 108 | map l.head (l.attrValues 109 | (l.filterAttrs (k: v: l.length v > 1) (l.groupBy toString list))); 110 | 111 | mkApiFuncOptionType = type-in: type-out: 112 | l.mkOptionType { 113 | name = "core-API-function"; 114 | description = "Core API Function"; 115 | getSubOptions = prefix: 116 | type-in.getSubOptions (prefix ++ [ "" ]) 117 | // type-out.getSubOptions (prefix ++ [ "" ]); 118 | }; 119 | }; 120 | 121 | in utils 122 | -------------------------------------------------------------------------------- /src/mkFlake.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: 2 | 3 | let 4 | 5 | utils = import ./lib/utils.nix iogx-inputs; 6 | modularise = import ./lib/modularise.nix iogx-inputs; 7 | options = import ./options iogx-inputs; 8 | 9 | # prefetch-npm-deps is broken (hangs indefinitely) in the current version of 10 | # nixpkgs (which is nixpkgs-unstable coming from haskell.nix), so we need this 11 | # hack. Same for dockerTools (fails to add layers with an out-of-disk error on 12 | # /tmp). TODO when we bump haskell-nix, check if this is still needed. 13 | mkCustomNixpkgsOverlay = user-inputs: prev: _: 14 | let 15 | stable-pkgs = 16 | import iogx-inputs.nixpkgs-stable { inherit (prev) system; }; 17 | in { 18 | prefetch-npm-deps = stable-pkgs.prefetch-npm-deps; 19 | dockerTools = stable-pkgs.dockerTools; 20 | 21 | # NOTE: `self.rev` is only defined when the git tree is not dirty. 22 | # `gitref` is useful when wanting to embed the current git commit hash in 23 | # your binary or other build outputs. 24 | gitrev = 25 | user-inputs.self.rev or "0000000000000000000000000000000000000000"; 26 | }; 27 | 28 | mkNixpkgs = user-inputs: system: args: 29 | import iogx-inputs.nixpkgs { 30 | inherit system; 31 | config = iogx-inputs.haskell-nix.config // (args.config or { }); 32 | overlays = [ 33 | iogx-inputs.iohk-nix.overlays.crypto 34 | iogx-inputs.iohk-nix.overlays.cardano-lib 35 | iogx-inputs.haskell-nix.overlay 36 | iogx-inputs.iohk-nix.overlays.haskell-nix-crypto 37 | # WARNING: The order of these is crucial 38 | # The iohk-nix.overlays.haskell-nix-crypto depends on both the 39 | # iohk-nix.overlays.crypto and the haskell-nix.overlay overlays 40 | # and so must be after them in the list of overlays to nixpkgs. 41 | # TODO check if this constraint still applies in the latest haskell.nix. 42 | iogx-inputs.iohk-nix.overlays.haskell-nix-extra 43 | (mkCustomNixpkgsOverlay user-inputs) 44 | ] ++ (args.overlays or [ ]); 45 | }; 46 | 47 | # This creates the IOGX lib and will become available as lib.iogx.* to the user. 48 | mkIogxLib = desystemized-user-inputs: pkgs: 49 | let 50 | iogx = { inherit utils modularise options; }; 51 | 52 | repoRoot = modularise { 53 | root = ../.; 54 | module = "repoRoot"; 55 | args = { 56 | user-inputs = desystemized-user-inputs; 57 | iogx-inputs = utils.deSystemize pkgs.stdenv.system iogx-inputs; 58 | lib = builtins // pkgs.lib // { inherit iogx; }; 59 | system = pkgs.stdenv.system; 60 | inherit pkgs; 61 | }; 62 | }; 63 | in { 64 | mkShell = repoRoot.src.core.mkShell; 65 | mkHaskellProject = repoRoot.src.core.mkHaskellProject; 66 | mkHydraRequiredJob = repoRoot.src.core.mkHydraRequiredJob; 67 | mkContainerFromCabalExe = repoRoot.src.core.mkContainerFromCabalExe; 68 | inherit utils modularise options; 69 | }; 70 | 71 | mkFlake = args': 72 | let 73 | evaluated-modules = iogx-inputs.nixpkgs.lib.evalModules { 74 | modules = [{ 75 | options = options; 76 | config."mkFlake." = args'; 77 | }]; 78 | }; 79 | 80 | args = evaluated-modules.config."mkFlake."; 81 | 82 | mkPerSystemFlake = system: 83 | let 84 | user-inputs = args.inputs; 85 | 86 | desystemized-user-inputs = utils.deSystemize system user-inputs; 87 | 88 | pkgs = mkNixpkgs user-inputs system args.nixpkgsArgs; 89 | 90 | lib = builtins // pkgs.lib // { 91 | iogx = mkIogxLib desystemized-user-inputs pkgs; 92 | }; 93 | 94 | modularised-user-repo-root = modularise { 95 | debug = args.debug; 96 | root = args.repoRoot; 97 | module = "repoRoot"; 98 | args = { 99 | inputs = desystemized-user-inputs; 100 | inherit pkgs lib system; 101 | }; 102 | }; 103 | 104 | args-for-user-outputs = { 105 | repoRoot = modularised-user-repo-root; 106 | inputs = desystemized-user-inputs; 107 | inherit pkgs lib system; 108 | }; 109 | 110 | evaluated-outputs = args.outputs args-for-user-outputs; 111 | 112 | flake = 113 | utils.recursiveUpdateMany (lib.concatLists [ evaluated-outputs ]); 114 | 115 | flake-without-hydraJobs = flake // { hydraJobs = { }; }; 116 | 117 | # We don't support hydraJobs on aarch64-linux 118 | flake' = if system == "aarch64-linux" then 119 | flake-without-hydraJobs 120 | else 121 | flake; 122 | in flake'; 123 | 124 | systemIndependentFlake = let 125 | modularised-user-repo-root = modularise { 126 | debug = args.debug; 127 | root = args.repoRoot; 128 | module = "repoRoot"; 129 | args = { inputs = args.inputs; }; 130 | }; 131 | 132 | args-for-user-flake = { 133 | repoRoot = modularised-user-repo-root; 134 | inputs = args.inputs; 135 | }; 136 | 137 | flake = args.flake args-for-user-flake; 138 | in flake; 139 | 140 | perSystemFlake = 141 | iogx-inputs.flake-utils.lib.eachSystem args.systems mkPerSystemFlake; 142 | 143 | flake = iogx-inputs.nixpkgs.lib.recursiveUpdate systemIndependentFlake 144 | perSystemFlake; 145 | in flake; 146 | 147 | in mkFlake 148 | -------------------------------------------------------------------------------- /src/options/default.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: 2 | 3 | import ./flake-dot-nix.nix iogx-inputs // import ./mkFlake.nix iogx-inputs 4 | // import ./mkGitRevOverlay.nix iogx-inputs 5 | // import ./mkHaskellProject.nix iogx-inputs 6 | // import ./mkHydraRequiredJob.nix iogx-inputs 7 | // import ./mkShell.nix iogx-inputs 8 | // import ./mkContainerFromCabalExe.nix iogx-inputs 9 | -------------------------------------------------------------------------------- /src/options/flake-dot-nix.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: 2 | 3 | let 4 | 5 | l = builtins // iogx-inputs.nixpkgs.lib; 6 | 7 | utils = import ../lib/utils.nix iogx-inputs; 8 | 9 | link = x: utils.headerToMarkDownLink x x; 10 | 11 | flake-dot-nix-submodule = l.types.submodule { 12 | options = { 13 | description = l.mkOption { 14 | type = l.types.str; 15 | description = '' 16 | Arbitrary description for the flake. 17 | 18 | This string is displayed when running `nix flake info` and other flake 19 | commands. 20 | 21 | It can be a short title for your project. 22 | ''; 23 | example = l.literalExpression '' 24 | # flake.nix 25 | { 26 | description = "My Haskell Project"; 27 | } 28 | ''; 29 | }; 30 | 31 | inputs = l.mkOption { 32 | type = l.types.attrs; 33 | description = '' 34 | Your flake *must* define `iogx` among its inputs. 35 | 36 | In turn, IOGX manages the following inputs for you: 37 | [CHaP](https://github.com/input-output-hk/cardano-haskell-packages), 38 | [haskell.nix](https://github.com/input-output-hk/haskell.nix), 39 | [nixpkgs](https://github.com/NixOS/nixpkgs), 40 | [hackage.nix](https://github.com/input-output-hk/hackage.nix), 41 | [iohk-nix](https://github.com/input-output-hk/iohk-nix), 42 | [sphinxcontrib-haddock](https://github.com/michaelpj/sphinxcontrib-haddock), 43 | [pre-commit-hooks-nix](https://github.com/cachix/pre-commit-hooks.nix), 44 | [haskell-language-server](https://github.com/haskell/haskell-language-server), 45 | [easy-purescript-nix](https://github.com/justinwoo/easy-purescript-nix). 46 | 47 | If you find that you want to use a different version of some of the 48 | implicit inputs listed above, for instance because IOGX has not been 49 | updated, or because you need to test against a specific branch, you 50 | can use the `follows` syntax like in the example above. 51 | 52 | Note that the Haskell template `flake.nix` does this by default with 53 | `CHaP`, `hackage.nix` and `haskell.nix`. 54 | 55 | It is of course possible to add other inputs (not already managed by 56 | IOGX) in the normal way. 57 | 58 | For example, to add `nix2container` and `cardano-world`: 59 | 60 | ```nix 61 | inputs = { 62 | iogx.url = "github:inputs-output-hk/iogx"; 63 | n2c.url = "github:nlewo/nix2container"; 64 | cardano-world.url = "github:input-output-hk/cardano-world"; 65 | }; 66 | ``` 67 | 68 | If you need to reference the inputs managed by IOGX in your flake, you 69 | may use this syntax: 70 | 71 | ```nix 72 | { inputs, ... }: 73 | { 74 | nixpkgs = inputs.iogx.inputs.nixpkgs; 75 | CHaP = inputs.iogx.inputs.CHaP; 76 | haskellNix = inputs.iogx.inputs.haskell-nix; 77 | } 78 | ``` 79 | 80 | If you are using the `follows` syntax for some inputs, you can avoid 81 | one level of indirection when referencing those inputs: 82 | ```nix 83 | { inputs, ... }: 84 | { 85 | nixpkgs = inputs.nixpkgs; 86 | CHaP = inputs.CHaP; 87 | haskellNix = inputs.haskell-nix; 88 | } 89 | ``` 90 | 91 | If you need to update IOGX (or any other input) you can do it the 92 | normal way: 93 | 94 | ```bash 95 | nix flake lock --update-input iogx 96 | nix flake lock --update-input haskell-nix 97 | nix flake lock --update-input hackage 98 | nix flake lock --update-input CHaP 99 | ``` 100 | ''; 101 | example = l.literalExpression '' 102 | # flake.nix inputs for Haskell Projects 103 | { 104 | inputs = { 105 | iogx = { 106 | url = "github:input-output-hk/iogx"; 107 | inputs.hackage.follows = "hackage"; 108 | inputs.CHaP.follows = "CHaP"; 109 | inputs.haskell-nix.follows = "haskell-nix"; 110 | inputs.nixpkgs.follows = "haskell-nix/nixpkgs-2305"; 111 | }; 112 | 113 | hackage = { 114 | url = "github:input-output-hk/hackage.nix"; 115 | flake = false; 116 | }; 117 | 118 | CHaP = { 119 | url = "github:input-output-hk/cardano-haskell-packages?ref=repo"; 120 | flake = false; 121 | }; 122 | 123 | haskell-nix = { 124 | url = "github:input-output-hk/haskell.nix"; 125 | inputs.hackage.follows = "hackage"; 126 | }; 127 | }; 128 | } 129 | 130 | # flake.nix inputs for Vanilla Projects 131 | { 132 | inputs = { 133 | iogx.url = "github:input-output-hk/iogx"; 134 | }; 135 | } 136 | ''; 137 | }; 138 | 139 | outputs = l.mkOption { 140 | type = l.types.functionTo l.types.attrs; 141 | description = '' 142 | Your flake `outputs` are produced using ${link "mkFlake"}. 143 | ''; 144 | example = l.literalExpression '' 145 | # flake.nix 146 | { 147 | outputs = inputs: inputs.iogx.lib.mkFlake { 148 | 149 | inherit inputs; 150 | 151 | repoRoot = ./.; 152 | 153 | outputs = import ./nix/outputs.nix; 154 | 155 | # systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" "aarch64-linux" ]; 156 | 157 | # debug = false; 158 | 159 | # nixpkgsArgs = { 160 | # config = {}; 161 | # overlays = []; 162 | # }; 163 | 164 | # flake = {}; 165 | }; 166 | } 167 | ''; 168 | }; 169 | 170 | nixConfig = l.mkOption { 171 | type = l.types.attrs; 172 | description = '' 173 | Unless you know what you are doing, you should not change `nixConfig`. 174 | 175 | You could always add new `extra-substituters` and `extra-trusted-public-keys`, but do not delete the existing ones, or you won't have access to IOG caches. 176 | 177 | For the caches to work properly, it is sufficient that the following two lines be included in your `/etc/nix/nix.conf`: 178 | ```txt 179 | trusted-users = USER 180 | experimental-features = nix-command flakes 181 | ``` 182 | Replace `USER` with the result of running `whoami`. 183 | 184 | You may need to reload the nix daemon on Darwin for changes to `/etc/nix/nix.conf` to take effect: 185 | ```bash 186 | sudo launchctl stop org.nixos.nix-daemon 187 | sudo launchctl start org.nixos.nix-daemon 188 | ``` 189 | Leave `allow-import-from-derivation` set to `true` for `haskell.nix` for work correctly. 190 | 191 | If Nix starts building `GHC` or other large artifacts that means that your caches have not been configured properly. 192 | ''; 193 | example = l.literalExpression '' 194 | # flake.nix 195 | { 196 | nixConfig = { 197 | extra-substituters = [ 198 | "https://cache.iog.io" 199 | ]; 200 | extra-trusted-public-keys = [ 201 | "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" 202 | ]; 203 | allow-import-from-derivation = true; 204 | }; 205 | } 206 | ''; 207 | }; 208 | }; 209 | }; 210 | 211 | flake-dot-nix = l.mkOption { 212 | type = flake-dot-nix-submodule; 213 | description = '' 214 | The `flake.nix` file for your project. 215 | 216 | For [Haskell Projects](../templates/haskell/flake.nix): 217 | ```bash 218 | nix flake init --template github:input-output-hk/iogx#haskell 219 | ``` 220 | 221 | For [Other Projects](../templates/vanilla/flake.nix): 222 | ```bash 223 | nix flake init --template github:input-output-hk/iogx#vanilla 224 | ``` 225 | 226 | Below is a description of each of its attributes. 227 | ''; 228 | }; 229 | 230 | in { "flake.nix" = flake-dot-nix; } 231 | -------------------------------------------------------------------------------- /src/options/mkContainerFromCabalExe.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: 2 | 3 | let 4 | l = builtins // iogx-inputs.nixpkgs.lib; 5 | 6 | utils = import ../lib/utils.nix iogx-inputs; 7 | 8 | mkContainerFromCabalExe-IN-submodule = l.types.submodule { 9 | options = { 10 | exe = l.mkOption { 11 | type = l.types.package; 12 | description = '' 13 | The exe produced by haskell.nix that you want to wrap in a container. 14 | ''; 15 | example = l.literalExpression '' 16 | project.packages.fooExe 17 | ''; 18 | }; 19 | 20 | name = l.mkOption { 21 | type = l.types.nullOr l.types.str; 22 | default = null; 23 | defaultText = l.literalExpression "exe.exeName"; 24 | description = '' 25 | Name of the container produced. 26 | ''; 27 | }; 28 | 29 | description = l.mkOption { 30 | type = l.types.nullOr l.types.str; 31 | default = null; 32 | description = '' 33 | Sets the `org.opencontainers.image.description` annotate key in the container. 34 | See https://github.com/opencontainers/image-spec/blob/main/annotations.md 35 | ''; 36 | }; 37 | 38 | packages = l.mkOption { 39 | type = l.types.nullOr (l.types.listOf l.types.package); 40 | default = null; 41 | description = '' 42 | Packages to add to the container's filesystem. 43 | > Note: Only the `/bin` directly will be linked from packages into the containers root filesystem. 44 | ''; 45 | }; 46 | 47 | sourceUrl = l.mkOption { 48 | type = l.types.nullOr l.types.str; 49 | default = null; 50 | description = '' 51 | Sets the `org.opencontainers.image.source` annotate key in the container. 52 | See https://github.com/opencontainers/image-spec/blob/main/annotations.md 53 | ''; 54 | }; 55 | }; 56 | }; 57 | 58 | mkContainerFromCabalExe-OUT-submodule = l.types.submodule { options = { }; }; 59 | 60 | mkContainerFromCabalExe-IN = l.mkOption { 61 | type = mkContainerFromCabalExe-IN-submodule; 62 | description = '' 63 | # Not Rendered In Docs 64 | ''; 65 | }; 66 | 67 | mkContainerFromCabalExe-OUT = l.mkOption { 68 | type = mkContainerFromCabalExe-OUT-submodule; 69 | description = '' 70 | # Not Rendered In Docs 71 | ''; 72 | }; 73 | 74 | mkContainerFromCabalExe = l.mkOption { 75 | description = '' 76 | The `lib.iogx.mkContainerFromCabalExe` function builds a portable container for use with docker and similar tools. 77 | 78 | It outputs the results from running nix2container's buildImage function. 79 | 80 | See. https://github.com/nlewo/nix2container 81 | 82 | In this document: 83 | - Options for the input attrset are prefixed by `mkContainerFromCabalExe.`. 84 | ''; 85 | type = utils.mkApiFuncOptionType mkContainerFromCabalExe-IN.type 86 | mkContainerFromCabalExe-OUT.type; 87 | example = l.literalExpression '' 88 | # nix/containers.nix 89 | { repoRoot, inputs, pkgs, lib, system }: 90 | { 91 | fooContainer = lib.iogx.mkContainerFromCabalExe { 92 | exe = inputs.self.packages.fooExe; 93 | }; 94 | 95 | barContainer = lib.iogx.mkContainerFromCabalExe { 96 | exe = inputs.self.packages.barExe; 97 | name = "bizz"; 98 | description = "Test container"; 99 | packages = [ pkgs.jq ]; 100 | sourceUrl = "https://github.com/input-output-hk/example"; 101 | }; 102 | } 103 | 104 | # nix/outputs.nix 105 | { repoRoot, inputs, pkgs, lib, system }: 106 | let 107 | containers = repoRoot.nix.containers; 108 | in 109 | [ 110 | { 111 | inherit containers; 112 | } 113 | ] 114 | ''; 115 | }; 116 | in { 117 | inherit mkContainerFromCabalExe; 118 | "mkContainerFromCabalExe." = mkContainerFromCabalExe-IN; 119 | } 120 | -------------------------------------------------------------------------------- /src/options/mkFlake.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: 2 | 3 | let 4 | 5 | l = builtins // iogx-inputs.nixpkgs.lib; 6 | 7 | utils = import ../lib/utils.nix iogx-inputs; 8 | 9 | link = x: utils.headerToMarkDownLink x x; 10 | 11 | mkFlake-IN-submodule = l.types.submodule { 12 | options = { 13 | 14 | inputs = l.mkOption { 15 | type = l.types.attrs; 16 | description = '' 17 | Your flake inputs. 18 | 19 | You almost certainly want to do `inherit inputs;` here (see the example in ${ 20 | link "mkFlake" 21 | }) 22 | ''; 23 | }; 24 | 25 | repoRoot = l.mkOption { 26 | type = l.types.path; 27 | description = '' 28 | The root of your repository (most likely `./.`). 29 | ''; 30 | example = l.literalExpression "./alternative/flake.nix"; 31 | }; 32 | 33 | systems = l.mkOption { 34 | type = l.types.listOf (l.types.enum [ 35 | "x86_64-linux" 36 | "x86_64-darwin" 37 | "aarch64-darwin" 38 | "aarch64-linux" 39 | ]); 40 | description = '' 41 | The systems you want to build for. 42 | 43 | The ${ 44 | link "mkFlake..outputs" 45 | } function will be called once for each system. 46 | ''; 47 | default = 48 | [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" "aarch64-linux" ]; 49 | defaultText = l.literalExpression '' 50 | [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" "aarch64-linux" ]''; 51 | }; 52 | 53 | outputs = l.mkOption { 54 | type = l.types.functionTo (l.types.listOf l.types.attrs); 55 | example = l.literalExpression '' 56 | # flake.nix 57 | { 58 | outputs = inputs: inputs.iogx.lib.mkFlake { 59 | outputs = import ./outputs.nix; 60 | }; 61 | } 62 | 63 | # outputs.nix 64 | { repoRoot, inputs, pkgs, lib, system }: 65 | [ 66 | { 67 | project = lib.iogx.mkHaskellProject {}; 68 | } 69 | { 70 | packages.foo = repoRoot.nix.foo; 71 | devShells.foo = lib.iogx.mkShell {}; 72 | } 73 | { 74 | hydraJobs.ghc928 = inputs.self.project.variants.ghc928.hydraJobs; 75 | } 76 | ] 77 | ''; 78 | description = '' 79 | A function that is called once for each system in ${ 80 | link "mkFlake..systems" 81 | }. 82 | 83 | This is the most important option as it will determine your flake outputs. 84 | 85 | `outputs` receives an attrset and must return a list of attrsets. 86 | 87 | The returned attrsets are recursively merged top-to-bottom. 88 | 89 | Each of the input attributes to the `outputs` function is documented below. 90 | 91 | #### `repoRoot` 92 | 93 | Ordinarily you would use the `import` keyword to import nix files, but you can use the `repoRoot` variable instead. 94 | 95 | `repoRoot` is an attrset that can be used to reference the contents of your repository folder instead of using the `import` keyword. 96 | 97 | Its value is set to the path of ${link "mkFlake..repoRoot"}. 98 | 99 | For example, if this is your top-level repository folder: 100 | ``` 101 | * src 102 | - Main.hs 103 | - cabal.project 104 | * nix 105 | - outputs.nix 106 | - alpha.nix 107 | * bravo 108 | - charlie.nix 109 | - india.nix 110 | - hotel.json 111 | * delta 112 | - echo.nix 113 | - golf.txt 114 | ``` 115 | 116 | Then this is how you can use the `repoRoot` attrset: 117 | ```nix 118 | # ./nix/alpha.nix 119 | { repoRoot, ... }: 120 | repoRoot."cabal.project" 121 | 122 | # ./nix/bravo/charlie.nix 123 | { repoRoot, ... }: 124 | repoRoot.nix.bravo."hotel.json" 125 | 126 | # ./nix/bravo/india.nix 127 | { pkgs, ... }: 128 | pkgs.hello 129 | 130 | # ./nix/bravo/delta/echo.nix 131 | { repoRoot, lib, ... }: 132 | arg1: 133 | { arg2 ? null }: 134 | lib.someFunction arg1 arg2 repoRoot.nix.bravo.delta."golf.txt" 135 | 136 | # ./nix/per-system-outputs.nix 137 | { repoRoot, inputs, pkgs, system, lib, ... }: 138 | { 139 | packages.example = 140 | let 141 | a = repoRoot.nix.alpha; 142 | c = repoRoot.nix.bravo.charlie; 143 | e = repoRoot.nix.bravo.delta.echo "arg1" {}; 144 | f = repoRoot.nix.bravo.delta."golf.txt"; 145 | g = repoRoot.src."Main.hs"; 146 | in 147 | 42; 148 | } 149 | ``` 150 | 151 | Note that the Nix files do not need the `".nix"` suffix, while files with any other extension (e.g. `golf.txt`) must include the full name to be referenced. 152 | 153 | In the case of non-Nix files, internally IOGX calls `builtins.readFile` to read the contents of that file. 154 | 155 | > **_NOTE:_** Any nix file that is referenced this way will also receive the attrset `{ repoRoot, inputs, pkgs, system, lib }`, just like ${ 156 | link "mkFlake..outputs" 157 | }. 158 | 159 | Using the `repoRoot` argument is optional, but it has the advantage of not having to thread the standard arguments (especially `pkgs` and `inputs`) all over the place. 160 | 161 | ### `inputs` 162 | 163 | Your flake inputs as defined in ${link "mkFlake..inputs"}. 164 | 165 | Note that these `inputs` have been de-systemized against the current system. 166 | 167 | This means that you can use the following syntax: 168 | ```nix 169 | inputs.n2c.packages.nix2container 170 | inputs.self.packages.foo 171 | ``` 172 | 173 | In addition to the usual syntax which mentions `system` explicitely. 174 | ```nix 175 | inputs.n2c.packages.x86_64-linux.nix2container 176 | inputs.self.packages.x86_64-darwin.foo 177 | ``` 178 | 179 | #### `pkgs` 180 | 181 | A `nixpkgs` instantiated against the current system (as found in `pkgs.stdenv.system`), for each of your ${ 182 | link "mkFlake..systems" 183 | }, and overlaid with goodies from `haskell.nix` and `iohk-nix`. 184 | 185 | A `nixpkgs` is also available at `inputs.nixpkgs.legacyPackages` but that should *not* be used because it doesn't have the required overlays. 186 | 187 | You may reference `pkgs` freely to get to the legacy packages. 188 | 189 | #### `system` 190 | 191 | This is just `pkgs.stdenv.system`, which is likely to be used often. 192 | 193 | #### `lib` 194 | 195 | This is just `pkgs.lib` plus the `iogx` attrset, which contains library functions and utilities. 196 | 197 | In here you will find the following: 198 | ```nix 199 | lib.iogx.mkShell {} 200 | lib.iogx.mkHaskellProject {} 201 | lib.iogx.mkHydraRequiredJob {} 202 | lib.iogx.mkGitRevProjectOverlay {} 203 | ``` 204 | ''; 205 | }; 206 | 207 | flake = l.mkOption { 208 | type = l.types.functionTo l.types.attrs; 209 | default = _: { }; 210 | description = '' 211 | A function that returns a flake-like attrset. 212 | 213 | You can place additional flake outputs here, which will be recursively updated with the attrset from ${ 214 | link "mkFlake..outputs" 215 | }. 216 | 217 | This is a good place to put system-independent values like a `lib` attrset or pure Nix values. 218 | 219 | Like ${ 220 | link "mkFlake..outputs" 221 | }, this function takes an attrset as argument, containing both `repoRoot` and the original (non de-systemized) `inputs`. 222 | 223 | Note that if you use `repoRoot` to reference nix files in this context, the nix files must also be functions from an `{ repoRoot, inputs }` attrset. 224 | ''; 225 | example = l.literalExpression '' 226 | { repoRoot, inputs }: 227 | { 228 | lib.bar = _: null; 229 | 230 | packages.x86_64-linux.foo = null; 231 | devShells.x86_64-darwin.bar = null; 232 | 233 | networks = { 234 | prod = { }; 235 | dev = { }; 236 | }; 237 | } 238 | ''; 239 | }; 240 | 241 | nixpkgsArgs = l.mkOption { 242 | type = l.types.attrs; 243 | description = '' 244 | Internally, IOGX calls `import inputs.nixpkgs {}` for each of your ${ 245 | link "mkFlake..systems" 246 | }. 247 | 248 | Using `nixpkgsArgs` you can provide an additional `config` attrset and a list of `overlays` to be appended to nixpkgs. 249 | ''; 250 | default = { 251 | config = { }; 252 | overlays = [ ]; 253 | }; 254 | example = l.literalExpression '' 255 | # flake.nix 256 | { 257 | outputs = inputs: inputs.iogx.lib.mkFlake { 258 | nixpkgsArgs.overlays = [(self: super: { 259 | acme = super.callPackage ./nix/acme.nix { }; 260 | })]; 261 | nixpkgsArgs.config.permittedInsecurePackages [ 262 | "python-2.7.18.6" 263 | ]; 264 | }; 265 | } 266 | ''; 267 | defaultText = l.literalExpression '' 268 | { 269 | config = { }; 270 | overlays = [ ]; 271 | } 272 | ''; 273 | }; 274 | 275 | debug = l.mkOption { 276 | type = l.types.bool; 277 | default = false; 278 | description = '' 279 | If enabled, IOGX will trace debugging info to standard output. 280 | ''; 281 | }; 282 | }; 283 | }; 284 | 285 | mkFlake-IN = l.mkOption { 286 | type = mkFlake-IN-submodule; 287 | description = '' 288 | # Not Rendered In Docs 289 | ''; 290 | }; 291 | 292 | mkFlake-OUT = l.mkOption { 293 | type = l.types.raw; 294 | description = '' 295 | # Not Rendered In Docs 296 | ''; 297 | }; 298 | 299 | mkFlake = l.mkOption { 300 | type = utils.mkApiFuncOptionType mkFlake-IN.type mkFlake-OUT.type; 301 | description = '' 302 | The `inputs.iogx.lib.mkFlake` function takes an attrset of options and returns an attrset of flake outputs. 303 | 304 | In this document, options for the input attrset are prefixed by `mkFlake.`. 305 | ''; 306 | example = l.literalExpression '' 307 | # flake.nix 308 | { 309 | outputs = inputs: inputs.iogx.lib.mkFlake { 310 | inherit inputs; 311 | repoRoot = ./.; 312 | debug = false; 313 | nixpkgsArgs = {}; 314 | systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" "aarch64-linux" ]; 315 | outputs = { repoRoot, inputs, pkgs, lib, system }: []; 316 | }; 317 | } 318 | ''; 319 | }; 320 | 321 | in { 322 | inherit mkFlake; 323 | "mkFlake." = mkFlake-IN; 324 | } 325 | -------------------------------------------------------------------------------- /src/options/mkGitRevOverlay.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: { } 2 | -------------------------------------------------------------------------------- /src/options/mkHaskellProject.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: 2 | 3 | let 4 | 5 | l = builtins // iogx-inputs.nixpkgs.lib; 6 | 7 | utils = import ../lib/utils.nix iogx-inputs; 8 | 9 | link = x: utils.headerToMarkDownLink x x; 10 | 11 | combined-haddock-submodule = l.types.submodule { 12 | options = { 13 | enable = l.mkOption { 14 | type = l.types.bool; 15 | default = false; 16 | description = '' 17 | Whether to enable combined haddock for your project. 18 | ''; 19 | }; 20 | 21 | packages = l.mkOption { 22 | type = l.types.listOf l.types.str; 23 | default = [ ]; 24 | description = '' 25 | The list of cabal package names to include in the combined Haddock. 26 | ''; 27 | }; 28 | 29 | prologue = l.mkOption { 30 | type = l.types.str; 31 | default = ""; 32 | description = '' 33 | A string acting as prologue for the combined Haddock. 34 | ''; 35 | }; 36 | }; 37 | }; 38 | 39 | default-combined-haddock = { 40 | enable = false; 41 | packages = [ ]; 42 | prologue = ""; 43 | }; 44 | 45 | read-the-docs-submodule = l.types.submodule { 46 | options = { 47 | enable = l.mkOption { 48 | type = l.types.bool; 49 | default = false; 50 | description = '' 51 | Whether to enable support for a Read The Docs site. 52 | ''; 53 | }; 54 | 55 | siteFolder = l.mkOption { 56 | type = l.types.str; 57 | description = '' 58 | A Nix string representing a path, relative to the repository root, to 59 | your site folder containing the `conf.py` file. 60 | ''; 61 | example = l.literalExpression '' 62 | # project.nix 63 | { repoRoot, inputs, pkgs, lib, system }: 64 | 65 | lib.iogx.mkHaskellProject { 66 | readTheDocs.siteFolder = "./doc/read-the-docs-site"; 67 | } 68 | ''; 69 | }; 70 | 71 | sphinxToolchain = l.mkOption { 72 | type = l.types.nullOr l.types.package; 73 | default = null; 74 | description = '' 75 | A python environment with the required packages to build your site 76 | using sphinx. 77 | 78 | Normally you don't need to override this. 79 | ''; 80 | example = l.literalExpression '' 81 | # project.nix 82 | { repoRoot, inputs, pkgs, lib, system }: 83 | 84 | lib.iogx.mkHaskellProject { 85 | readTheDocs = { 86 | enable = true; 87 | siteFolder = "./doc/read-the-docs-site"; 88 | sphinxToolchain = pkgs.python3.withPackages (py: [ 89 | repoRoot.nix.ext.sphinxcontrib-bibtex 90 | repoRoot.nix.ext.sphinx-markdown-tables 91 | repoRoot.nix.ext.sphinxemoji 92 | repoRoot.nix.ext.sphinxcontrib-haddock 93 | repoRoot.nix.ext.sphinxcontrib-domaintools 94 | py.sphinxcontrib_plantuml 95 | py.sphinx-autobuild 96 | py.sphinx 97 | py.sphinx_rtd_theme 98 | py.recommonmark 99 | ]); 100 | }; 101 | } 102 | ''; 103 | }; 104 | }; 105 | }; 106 | 107 | default-read-the-docs = { 108 | enable = false; 109 | siteFolder = null; 110 | sphinxToolchain = null; 111 | }; 112 | 113 | mkHaskellProject-IN-submodule = l.types.submodule { 114 | options = { 115 | cabalProject = l.mkOption { 116 | type = l.types.attrs; 117 | default = { }; 118 | description = '' 119 | The original `cabalProject`. 120 | 121 | You most likely want to get one using 122 | [`haskell.nix:cabalProject'`](https://input-output-hk.github.io/haskell.nix/reference/library.html?highlight=cabalProjec#cabalproject) 123 | like in the example above. 124 | 125 | You should use `flake.variants` to provide support for profiling, different GHC versions, and any other additional configuration. 126 | 127 | The variants will be available in ${ 128 | link "mkHaskellProject..variants" 129 | }. 130 | ''; 131 | example = l.literalExpression '' 132 | # nix/project.nix 133 | { repoRoot, inputs, lib, system, ... }: 134 | 135 | lib.iogx.mkHaskellProject { 136 | cabalProject = pkgs.haskell-nix.cabalProject' ({ pkgs, config, ...) { 137 | name = "my-project"; 138 | src = ./.; # Must contain the cabal.project file 139 | inputMap = { 140 | "https://input-output-hk.github.io/cardano-haskell-packages" = inputs.CHaP; 141 | }; 142 | compiler-nix-name = "ghc8107"; 143 | flake.variants.profiled = { 144 | modules = [{ 145 | enableProfiling = true; 146 | enableLibraryProfiling = true; 147 | }]; 148 | }; 149 | flake.variants.ghc928 = { 150 | compiler-nix-name = "ghc928"; 151 | }; 152 | modules = []; 153 | cabalProjectLocal = ""; 154 | }); 155 | }; 156 | ''; 157 | }; 158 | 159 | shellArgs = l.mkOption { 160 | type = l.types.functionTo l.types.attrs; 161 | default = cabalProject: { 162 | tools.haskellCompilerVersion = cabalProject.args.compiler-nix-name; 163 | name = cabalProject.args.name; 164 | }; 165 | description = '' 166 | Arguments for ${link "mkShell"}. 167 | 168 | This is a function that is called once with the original 169 | ${ 170 | link "mkHaskellProject..cabalProject" 171 | } (coming from `haskell.nix`), 172 | and then once for each project variant. 173 | 174 | Internally these `shellArgs` are passed to ${link "mkShell"}. 175 | 176 | The shells will be available in: 177 | - ${link "mkHaskellProject..devShell"}. 178 | - ${link "mkHaskellProject..variants..devShell"}. 179 | ''; 180 | }; 181 | 182 | includeProfiledHydraJobs = l.mkOption { 183 | type = l.types.bool; 184 | default = false; 185 | example = l.literalExpression '' 186 | # outputs.nix 187 | { repoRoot, inputs, pkgs, lib, system }: 188 | let 189 | project = lib.iogx.mkHaskellProject { 190 | includeProfiledHydraJobs = true; 191 | }; 192 | in 193 | [ 194 | ( 195 | project.flake 196 | # ^^^^^ Includes: hydraJobs.profiled = project.variants.profiled.hydraJobs; 197 | ) 198 | ] 199 | ''; 200 | description = '' 201 | When set to `true` then ${ 202 | link "mkHaskellProject..flake" 203 | } will include: 204 | ```nix 205 | hydraJobs.profiled = project.variants.profiled.hydraJobs; 206 | ``` 207 | 208 | This is just a convenience option, you can always reference the jobs directly: 209 | ```nix 210 | # outputs.nix 211 | { repoRoot, inputs, pkgs, lib, system }: 212 | let 213 | project = lib.iogx.mkHaskellProject { 214 | includeProfiledHydraJobs = false; 215 | }; 216 | in 217 | [ 218 | { 219 | hydraJobs.profiled = project.variants.profiled.hydraJobs; 220 | } 221 | ] 222 | ``` 223 | 224 | This option assumes that you have defined a flake variant called `profiled` in your 225 | haskell.nix `cabalProject` (see the example above). 226 | ''; 227 | }; 228 | 229 | includeMingwW64HydraJobs = l.mkOption { 230 | type = l.types.bool; 231 | default = false; 232 | example = l.literalExpression '' 233 | # outputs.nix 234 | { repoRoot, inputs, pkgs, lib, system }: 235 | let 236 | project = lib.iogx.mkHaskellProject { 237 | includeMingwW64HydraJobs = true; 238 | }; 239 | in 240 | [ 241 | ( 242 | project.flake 243 | # ^^^^^ Includes: hydraJobs.mingwW64 = project.cross.mingwW64.hydraJobs; 244 | ) 245 | ] 246 | ``` 247 | ''; 248 | description = '' 249 | When set to `true` then ${ 250 | link "mkHaskellProject..flake" 251 | } will include: 252 | ```nix 253 | hydraJobs.mingwW66 = project.cross.mingwW64.hydraJobs 254 | ``` 255 | 256 | This is just a convenience option, you can always reference the jobs directly: 257 | ```nix 258 | # outputs.nix 259 | { repoRoot, inputs, pkgs, lib, system }: 260 | let 261 | project = lib.iogx.mkHaskellProject { 262 | includeMingwW64HydraJobs = false; 263 | }; 264 | in 265 | [ 266 | { 267 | hydraJobs.mingwW64 = project.cross.mingwW64.hydraJobs; 268 | } 269 | ] 270 | ``` 271 | ''; 272 | }; 273 | 274 | combinedHaddock = l.mkOption { 275 | type = combined-haddock-submodule; 276 | default = default-combined-haddock; 277 | description = '' 278 | Configuration for a combined Haddock. 279 | 280 | When enabled, your ${ 281 | link "mkHaskellProject..readTheDocs" 282 | } site will have access to Haddock symbols for your Haskell packages. 283 | 284 | Combining Haddock artifacts takes a significant amount of time and may slow down CI. 285 | 286 | The combined Haddock(s) will be available in: 287 | - ${link "mkHaskellProject..combined-haddock"} 288 | - ${link "mkHaskellProject..variants..combined-haddock"} 289 | ''; 290 | example = l.literalExpression '' 291 | # outputs.nix 292 | { repoRoot, inputs, pkgs, lib, system }: 293 | let 294 | project = lib.iogx.mkHaskellProject { 295 | combinedHaddock = { 296 | enable = system == "x86_64-linux"; 297 | packages = [ "foo" "bar" ]; 298 | prologue = "This is the prologue."; 299 | }; 300 | }; 301 | in 302 | [ 303 | { 304 | packages.combined-haddock = project.combined-haddock; 305 | } 306 | ] 307 | ''; 308 | }; 309 | 310 | readTheDocs = l.mkOption { 311 | type = read-the-docs-submodule; 312 | default = default-read-the-docs; 313 | description = '' 314 | Configuration for your [`read-the-docs`](https://readthedocs.org) site. 315 | 316 | If no site is required, this option can be omitted. 317 | 318 | The shells generated by ${ 319 | link "mkHaskellProject..shellArgs" 320 | } will be 321 | augmented with several scripts to make developing your site easier, 322 | grouped under the tag `read-the-docs`. 323 | 324 | The Read The Docs site derivation(s) will be available in: 325 | - ${link "mkHaskellProject..read-the-docs-site"} 326 | - ${link "mkHaskellProject..variants..read-the-docs-site"} 327 | ''; 328 | example = l.literalExpression '' 329 | # outputs.nix 330 | { repoRoot, inputs, pkgs, lib, system }: 331 | let 332 | project = lib.iogx.mkHaskellProject { 333 | readTheDocs.siteFolder = "doc/read-the-docs-site"; 334 | }; 335 | in 336 | [ 337 | { 338 | inherit (proejct) cabalProject; 339 | } 340 | { 341 | packages.read-the-docs-site = project.read-the-docs-site; 342 | } 343 | ] 344 | ''; 345 | }; 346 | }; 347 | }; 348 | 349 | mkHaskellProject-OUT-submodule = l.types.submodule { 350 | options = { 351 | flake = l.mkOption { 352 | type = l.types.attrs; 353 | description = '' 354 | An attribute set that can be included in your ${ 355 | link "mkFlake..outputs" 356 | } directly. 357 | 358 | For simple Haskell projects with no flake variants, this is all you need. 359 | 360 | It contains all the derivations for your project, but does not include project variants. 361 | 362 | If you set ${ 363 | link "mkHaskellProject..includeMingwW64HydraJobs" 364 | } to `true`, then 365 | this attrset will also include `hydraJobs.mingwW64`. 366 | 367 | This also automatically adds the `hydraJobs.required` job using ${ 368 | link "mkHydraRequiredJob" 369 | }. 370 | 371 | Below is a list of all its attributes: 372 | 373 | - `cabalProject` = ${link "mkHaskellProject..cabalProject"} 374 | - `devShells.default` = ${link "mkHaskellProject..devShell"} 375 | - `packages.*` = ${link "mkHaskellProject..packages"} 376 | - `packages.combined-haddock` = ${ 377 | link "mkHaskellProject..combined-haddock" 378 | } 379 | - `packages.read-the-docs-site` = ${ 380 | link "mkHaskellProject..read-the-docs-site" 381 | } 382 | - `packages.pre-commit-check` = ${ 383 | link "mkHaskellProject..pre-commit-check" 384 | } 385 | - `apps.*` = ${link "mkHaskellProject..apps"} 386 | - `checks.*` = ${link "mkHaskellProject..checks"} 387 | - `hydraJobs.*` = ${link "mkHaskellProject..hydraJobs"} 388 | - `hydraJobs.combined-haddock` = ${ 389 | link "mkHaskellProject..combined-haddock" 390 | } 391 | - `hydraJobs.read-the-docs-site` = ${ 392 | link "mkHaskellProject..read-the-docs-site" 393 | } 394 | - `hydraJobs.pre-commit-check` = ${ 395 | link "mkHaskellProject..pre-commit-check" 396 | } 397 | - `hydraJobs.mingwW64` = ${ 398 | link "mkHaskellProject..cross.mingwW64.hydraJobs" 399 | } (conditionally) 400 | - `hydraJobs.required` = ${link "mkHydraRequiredJob"} 401 | ''; 402 | example = l.literalExpression '' 403 | # flake.nix 404 | { 405 | outputs = inputs: inputs.iogx.lib.mkFlake { 406 | outputs = import ./outputs.nix; 407 | }; 408 | } 409 | 410 | # outputs.nix 411 | { repoRoot, inputs, pkgs, lib, system }: 412 | let 413 | project = lib.iogx.mkHaskellProject {}; 414 | in 415 | [ 416 | ( 417 | project.flake 418 | ) 419 | ] 420 | ''; 421 | }; 422 | 423 | packages = l.mkOption { 424 | type = l.types.attrs; 425 | description = '' 426 | A attrset containing the cabal executables, testsuites and benchmarks. 427 | 428 | The keys are the cabal target names, and the values are the derivations. 429 | 430 | IOGX will fail to evaluate if some of you cabal targets have the same name. 431 | ''; 432 | }; 433 | 434 | apps = l.mkOption { 435 | type = l.types.attrs; 436 | description = '' 437 | A attrset containing the cabal executables, testsuites and benchmarks. 438 | 439 | The keys are the cabal target names, and the values are the program paths. 440 | 441 | IOGX will fail to evaluate if some of you cabal targets have the same name. 442 | ''; 443 | }; 444 | 445 | checks = l.mkOption { 446 | type = l.types.attrs; 447 | description = '' 448 | A attrset containing the cabal testsuites. 449 | 450 | When these derivations are **built**, the actual tests will be run as part of the build. 451 | 452 | The keys are the cabal target names, and the values are the derivations. 453 | 454 | IOGX will fail to evaluate if some of you cabal targets have the same name. 455 | ''; 456 | }; 457 | 458 | hydraJobs = l.mkOption { 459 | type = l.types.attrs; 460 | description = '' 461 | A jobset containing `packages`, `checks`, `devShells.default` and `haskell.nix`'s `plan-nix` and `roots`. 462 | 463 | The `devShell` comes from your implementation of ${ 464 | link "mkHaskellProject..shellArgs" 465 | }. 466 | 467 | This attrset does not contain: 468 | - ${link "mkHaskellProject..combined-haddock"} 469 | - ${link "mkHaskellProject..read-the-docs-site"} 470 | - ${link "mkHaskellProject..pre-commit-check"} 471 | 472 | If you need those you can use ${ 473 | link "mkHaskellProject..flake" 474 | }, or you can consume them directly. 475 | ''; 476 | }; 477 | 478 | devShell = l.mkOption { 479 | type = l.types.package; 480 | description = '' 481 | The `devShell` as provided by your implementation of ${ 482 | link "mkHaskellProject..shellArgs" 483 | }. 484 | ''; 485 | }; 486 | 487 | read-the-docs-site = l.mkOption { 488 | type = l.types.package; 489 | description = '' 490 | The derivation for your ${ 491 | link "mkHaskellProject..readTheDocs" 492 | } site. 493 | ''; 494 | }; 495 | 496 | combined-haddock = l.mkOption { 497 | type = l.types.package; 498 | description = '' 499 | The derivation for your ${ 500 | link "mkHaskellProject..combinedHaddock" 501 | }. 502 | ''; 503 | }; 504 | 505 | pre-commit-check = l.mkOption { 506 | type = l.types.package; 507 | description = '' 508 | The derivation for the ${link "mkShell..preCommit"} coming from ${ 509 | link "mkHaskellProject..shellArgs" 510 | }. 511 | ''; 512 | }; 513 | 514 | variants = l.mkOption { 515 | type = l.types.attrs; 516 | description = '' 517 | This attribute contains the variants for your project, 518 | as defined in your ${ 519 | link "mkHaskellProject..cabalProject" 520 | }`.flake.variants`. 521 | 522 | Each variant has exaclty the same attributes as the main project. 523 | 524 | See the example above for more information. 525 | ''; 526 | example = l.literalExpression '' 527 | # outputs.nix 528 | { repoRoot, inputs, pkgs, lib, system }: 529 | let 530 | project = lib.iogx.mkHaskellProject { 531 | cabalProject = pkgs.haskell-nix.cabalProject' { 532 | flake.variants.ghc928 = {}; 533 | flake.variants.profiled = {}; 534 | }; 535 | }; 536 | in 537 | [ 538 | { 539 | hydraJobs.normal = project.hydraJobs; 540 | hydraJobs.profiled = project.variants.profiled.hydraJobs; 541 | hydraJobs.ghc928 = project.variants.ghc928.hydraJobs; 542 | 543 | packages.read-the-docs-normal = project.read-the-docs-site; 544 | packages.read-the-docs-profiled = project.variants.profiled.read-the-docs-site; 545 | packages.read-the-docs-ghc928 = project.variants.ghc928.read-the-docs-site; 546 | 547 | hydraJobs.ghc928-mingwW64 = project.variants.ghc928.cross.mingwW64.hydraJobs; 548 | } 549 | ] 550 | ''; 551 | }; 552 | 553 | cross = l.mkOption { 554 | type = l.types.attrs; 555 | description = '' 556 | This attribute contains cross-compilation variants for your project. 557 | 558 | Each variant only has two attributes: 559 | - `cabalProject` the original project coming from `haskell.nix`'s `.projectCross.` 560 | - `hydraJobs` that can be included directly in your flake outputs 561 | ''; 562 | example = l.literalExpression '' 563 | # outputs.nix 564 | { repoRoot, inputs, pkgs, lib, system }: 565 | let 566 | project = lib.iogx.mkHaskellProject {}; 567 | in 568 | [ 569 | { 570 | projectMingwW64 = project.cross.mingwW64.cabalProject; 571 | projectMusl64 = project.cross.musl64.cabalProject; 572 | 573 | hydraJobs.mingwW64 = project.cross.mingwW64.hydraJobs; 574 | hydraJobs.musl64 = project.cross.musl64.hydraJobs; 575 | } 576 | ] 577 | ''; 578 | }; 579 | }; 580 | }; 581 | 582 | mkHaskellProject-IN = l.mkOption { 583 | type = mkHaskellProject-IN-submodule; 584 | description = '' 585 | # Not Rendered In Docs 586 | ''; 587 | }; 588 | 589 | mkHaskellProject-OUT = l.mkOption { 590 | type = mkHaskellProject-OUT-submodule; 591 | description = '' 592 | # Not Rendered In Docs 593 | ''; 594 | }; 595 | 596 | mkHaskellProject = l.mkOption { 597 | description = '' 598 | The `lib.iogx.mkHaskellProject` function builds your `haskell.nix`-based project. 599 | 600 | In this document: 601 | - Options for the input attrset are prefixed by `mkHaskellProject.`. 602 | - The returned attrset contains the attributes prefixed by `mkHaskellProject.`. 603 | ''; 604 | type = utils.mkApiFuncOptionType mkHaskellProject-IN.type 605 | mkHaskellProject-OUT.type; 606 | example = l.literalExpression '' 607 | # nix/project.nix 608 | { repoRoot, inputs, pkgs, lib, system }: 609 | lib.iogx.mkHaskellProject { 610 | 611 | shellArgs = repoRoot.nix.make-shell; 612 | 613 | readTheDocs = { 614 | enable = true; 615 | siteFolder = "doc/read-the-docs-site"; 616 | }; 617 | 618 | combinedHaddock.enable = true; 619 | 620 | cabalProject = pkgs.haskell-nix.cabalProject' { 621 | compiler-nix-name = "ghc8107"; 622 | 623 | flake.variants.FOO = { 624 | compiler-nix-name = "ghc927"; 625 | }; 626 | }; 627 | } 628 | 629 | # outputs.nix 630 | { repoRoot, inputs, pkgs, lib, system }: 631 | let 632 | project = repoRoot.nix.project; 633 | in 634 | [ 635 | { 636 | inherit (project) cabalProject; 637 | } 638 | ( 639 | project.flake 640 | ) 641 | { 642 | hydraJobs.FOO = project.variants.FOO.hydraJobs; 643 | } 644 | ] 645 | ''; 646 | }; 647 | 648 | in { 649 | inherit mkHaskellProject; 650 | "mkHaskellProject." = mkHaskellProject-IN; 651 | } 652 | -------------------------------------------------------------------------------- /src/options/mkHydraRequiredJob.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: { } 2 | -------------------------------------------------------------------------------- /src/options/mkShell.nix: -------------------------------------------------------------------------------- 1 | iogx-inputs: 2 | 3 | let 4 | 5 | l = builtins // iogx-inputs.nixpkgs.lib; 6 | 7 | utils = import ../lib/utils.nix iogx-inputs; 8 | 9 | link = x: utils.headerToMarkDownLink x x; 10 | 11 | tools-submodule = l.types.submodule { 12 | options = { 13 | haskellCompilerVersion = l.mkOption { 14 | default = null; 15 | type = l.types.nullOr l.types.str; 16 | description = '' 17 | The haskell compiler version. 18 | 19 | Any value that is accepected by `haskell.nix:compiler-nix-name` is valid, e.g: `ghc8107`, `ghc92`, `ghc963`. 20 | 21 | This determines the version of other tools like `cabal-install` and `haskell-language-server`. 22 | 23 | If this option is unset of null, then no Haskell tools will be made available in the shell. 24 | 25 | However if you enable some Haskell-specific ${ 26 | link "mkShell..preCommit" 27 | } hooks, then 28 | that Haskell tool will be installed automatically using `ghc8107` as the default compiler version. 29 | 30 | When using ${ 31 | link "mkHaskellProject..shellArgs" 32 | }, this option is automatically set to 33 | the same value as the project's (or project variant's) `compiler-nix-name`. 34 | ''; 35 | example = l.literalExpression '' 36 | # shell.nix 37 | { repoRoot, inputs, pkgs, lib, system }: 38 | lib.iogx.mkShell { 39 | tools.haskellCompilerVersion = "ghc8107"; 40 | # ^^^^^ This will bring the haskell tools in your shell 41 | } 42 | ''; 43 | }; 44 | 45 | cabal-fmt = l.mkOption { 46 | type = l.types.nullOr l.types.package; 47 | default = null; 48 | description = '' 49 | A package that provides the `cabal-fmt` executable. 50 | 51 | If unset or `null`, a default `cabal-fmt` will be provided, which is independent of ${ 52 | link "mkShell..tools.haskellCompilerVersion" 53 | }. 54 | ''; 55 | example = l.literalExpression '' 56 | # shell.nix 57 | { repoRoot, inputs, pkgs, lib, system }: 58 | lib.iogx.mkShell { 59 | tools.cabal-fmt = repoRoot.nix.patched-cabal-fmt; 60 | } 61 | ''; 62 | }; 63 | 64 | cabal-install = l.mkOption { 65 | type = l.types.nullOr l.types.package; 66 | default = null; 67 | description = '' 68 | A package that provides the `cabal-install` executable. 69 | 70 | If unset or `null`, ${ 71 | link "mkShell..tools.haskellCompilerVersion" 72 | } will be used to select a suitable derivation. 73 | ''; 74 | example = l.literalExpression '' 75 | # shell.nix 76 | { repoRoot, inputs, pkgs, lib, system }: 77 | lib.iogx.mkShell { 78 | tools.cabal-install = repoRoot.nix.patched-cabal-install; 79 | } 80 | ''; 81 | }; 82 | 83 | haskell-language-server = l.mkOption { 84 | type = l.types.nullOr l.types.package; 85 | default = null; 86 | description = '' 87 | A package that provides the `haskell-language-server` executable. 88 | 89 | If unset or `null`, ${ 90 | link "mkShell..tools.haskellCompilerVersion" 91 | } will be used to select a suitable derivation. 92 | ''; 93 | example = l.literalExpression '' 94 | # shell.nix 95 | { repoRoot, inputs, pkgs, lib, system }: 96 | lib.iogx.mkShell { 97 | tools.haskell-language-server = repoRoot.nix.patched-haskell-language-server; 98 | } 99 | ''; 100 | }; 101 | 102 | haskell-language-server-wrapper = l.mkOption { 103 | type = l.types.nullOr l.types.package; 104 | default = null; 105 | description = '' 106 | A package that provides the `haskell-language-server-wrapper` executable. 107 | 108 | If unset or `null`, ${ 109 | link "mkShell..tools.haskellCompilerVersion" 110 | } will be used to select a suitable derivation. 111 | ''; 112 | example = l.literalExpression '' 113 | # shell.nix 114 | { repoRoot, inputs, pkgs, lib, system }: 115 | lib.iogx.mkShell { 116 | tools.haskell-language-server-wrapper = repoRoot.nix.pathced-haskell-language-server-wrapper; 117 | } 118 | ''; 119 | }; 120 | 121 | fourmolu = l.mkOption { 122 | type = l.types.nullOr l.types.package; 123 | default = null; 124 | description = '' 125 | A package that provides the `fourmolu` executable. 126 | 127 | If unset or `null`, a default `fourmolu` will be provided, which is independent of ${ 128 | link "mkShell..tools.haskellCompilerVersion" 129 | }. 130 | ''; 131 | example = l.literalExpression '' 132 | # shell.nix 133 | { repoRoot, inputs, pkgs, lib, system }: 134 | lib.iogx.mkShell { 135 | tools.fourmolu = repoRoot.nix.patched-fourmolu; 136 | } 137 | ''; 138 | }; 139 | 140 | hlint = l.mkOption { 141 | type = l.types.nullOr l.types.package; 142 | default = null; 143 | description = '' 144 | A package that provides the `hlint` executable. 145 | 146 | If unset or `null`, ${ 147 | link "mkShell..tools.haskellCompilerVersion" 148 | } will be used to select a suitable derivation. 149 | ''; 150 | example = l.literalExpression '' 151 | # shell.nix 152 | { repoRoot, inputs, pkgs, lib, system }: 153 | lib.iogx.mkShell { 154 | tools.hlint = repoRoot.nix.patched-hlint; 155 | } 156 | ''; 157 | }; 158 | 159 | stylish-haskell = l.mkOption { 160 | type = l.types.nullOr l.types.package; 161 | default = null; 162 | description = '' 163 | A package that provides the `stylish-haskell` executable. 164 | 165 | If unset or `null`, ${ 166 | link "mkShell..tools.haskellCompilerVersion" 167 | } will be used to select a suitable derivation. 168 | ''; 169 | example = l.literalExpression '' 170 | # shell.nix 171 | { repoRoot, inputs, pkgs, lib, system }: 172 | lib.iogx.mkShell { 173 | tools.stylish-haskell = repoRoot.nix.patched-stylish-haskell; 174 | } 175 | ''; 176 | }; 177 | 178 | ghcid = l.mkOption { 179 | type = l.types.nullOr l.types.package; 180 | default = null; 181 | description = '' 182 | A package that provides the `ghcid` executable. 183 | 184 | If unset or `null`, ${ 185 | link "mkShell..tools.haskellCompilerVersion" 186 | } will be used to select a suitable derivation. 187 | ''; 188 | example = l.literalExpression '' 189 | # shell.nix 190 | { repoRoot, inputs, pkgs, lib, system }: 191 | lib.iogx.mkShell { 192 | tools.ghcid = repoRoot.nix.patched-ghcid; 193 | } 194 | ''; 195 | }; 196 | 197 | shellcheck = l.mkOption { 198 | type = l.types.nullOr l.types.package; 199 | default = null; 200 | description = '' 201 | A package that provides the `shellcheck` executable. 202 | 203 | If unset or `null`, the most recent version available will be used. 204 | ''; 205 | example = l.literalExpression '' 206 | # shell.nix 207 | { repoRoot, inputs, pkgs, lib, system }: 208 | lib.iogx.mkShell { 209 | tools.shellcheck = repoRoot.nix.patched-shellcheck; 210 | } 211 | ''; 212 | }; 213 | 214 | prettier = l.mkOption { 215 | type = l.types.nullOr l.types.package; 216 | default = null; 217 | description = '' 218 | A package that provides the `prettier` executable. 219 | 220 | If unset or `null`, the most recent version available will be used. 221 | ''; 222 | example = l.literalExpression '' 223 | # shell.nix 224 | { repoRoot, inputs, pkgs, lib, system }: 225 | lib.iogx.mkShell { 226 | tools.prettier = repoRoot.nix.patched-prettier; 227 | } 228 | ''; 229 | }; 230 | 231 | editorconfig-checker = l.mkOption { 232 | type = l.types.nullOr l.types.package; 233 | default = null; 234 | description = '' 235 | A package that provides the `editorconfig-checker` executable. 236 | 237 | If unset or `null`, the most recent version available will be used. 238 | ''; 239 | example = l.literalExpression '' 240 | # shell.nix 241 | { repoRoot, inputs, pkgs, lib, system }: 242 | lib.iogx.mkShell { 243 | tools.editorconfig-checker = repoRoot.nix.patched-editorconfig-checker; 244 | } 245 | ''; 246 | }; 247 | 248 | nixfmt-classic = l.mkOption { 249 | type = l.types.nullOr l.types.package; 250 | default = null; 251 | description = '' 252 | A package that provides the `nixfmt-classic` executable. 253 | 254 | If unset or `null`, the most recent version available will be used. 255 | ''; 256 | example = l.literalExpression '' 257 | # shell.nix 258 | { repoRoot, inputs, pkgs, lib, system }: 259 | lib.iogx.mkShell { 260 | tools.nixfmt-classic = repoRoot.nix.patched-nixfmt-classic; 261 | } 262 | ''; 263 | }; 264 | 265 | optipng = l.mkOption { 266 | type = l.types.nullOr l.types.package; 267 | default = null; 268 | description = '' 269 | A package that provides the `optipng` executable. 270 | 271 | If unset or `null`, the most recent version available will be used. 272 | ''; 273 | example = l.literalExpression '' 274 | # shell.nix 275 | { repoRoot, inputs, pkgs, lib, system }: 276 | lib.iogx.mkShell { 277 | tools.optipng = repoRoot.nix.patched-optipng; 278 | } 279 | ''; 280 | }; 281 | 282 | purs-tidy = l.mkOption { 283 | type = l.types.nullOr l.types.package; 284 | default = null; 285 | description = '' 286 | A package that provides the `purs-tidy` executable. 287 | 288 | If unset or `null`, the most recent version available will be used. 289 | ''; 290 | example = l.literalExpression '' 291 | # shell.nix 292 | { repoRoot, inputs, pkgs, lib, system }: 293 | lib.iogx.mkShell { 294 | tools.purs-tidy = repoRoot.nix.patched-purs-tidy; 295 | } 296 | ''; 297 | }; 298 | 299 | rustfmt = l.mkOption { 300 | type = l.types.nullOr l.types.package; 301 | default = null; 302 | description = '' 303 | A package that provides the `rustfmt` executable. 304 | 305 | If unset or `null`, the most recent version available will be used. 306 | ''; 307 | example = l.literalExpression '' 308 | # shell.nix 309 | { repoRoot, inputs, pkgs, lib, system }: 310 | lib.iogx.mkShell { 311 | tools.rustfmt = repoRoot.nix.patched-rustfmt; 312 | } 313 | ''; 314 | }; 315 | }; 316 | }; 317 | 318 | script-submodule = l.types.submodule { 319 | options = { 320 | exec = l.mkOption { 321 | type = l.types.str; 322 | description = '' 323 | Bash code to be executed when the script is run. 324 | 325 | This field is required. 326 | ''; 327 | example = l.literalExpression '' 328 | # shell.nix 329 | { repoRoot, inputs, pkgs, lib, system }: 330 | lib.iogx.mkShell { 331 | scripts = { 332 | foo = { 333 | exec = ''' 334 | echo "Hello, world!" 335 | '''; 336 | }; 337 | }; 338 | } 339 | ''; 340 | }; 341 | 342 | description = l.mkOption { 343 | type = l.types.str; 344 | default = ""; 345 | description = '' 346 | A string that will appear next to the script name when printed. 347 | ''; 348 | example = l.literalExpression '' 349 | # shell.nix 350 | { repoRoot, inputs, pkgs, lib, system }: 351 | lib.iogx.mkShell { 352 | scripts = { 353 | foo = { 354 | description = "Short description for script foo"; 355 | exec = "#"; 356 | }; 357 | }; 358 | } 359 | ''; 360 | }; 361 | 362 | group = l.mkOption { 363 | type = l.types.str; 364 | default = ""; 365 | description = '' 366 | A string to tag the script. 367 | 368 | This will be used to group scripts together so that they look prettier and more organized when listed. 369 | ''; 370 | example = l.literalExpression '' 371 | # shell.nix 372 | { repoRoot, inputs, pkgs, lib, system }: 373 | lib.iogx.mkShell { 374 | scripts = { 375 | foo = { 376 | group = "devops"; 377 | exec = "#"; 378 | }; 379 | }; 380 | } 381 | ''; 382 | }; 383 | 384 | enable = l.mkOption { 385 | type = l.types.bool; 386 | default = true; 387 | description = '' 388 | Whether to enable this script. 389 | 390 | This can be used to include scripts conditionally. 391 | ''; 392 | example = l.literalExpression '' 393 | # shell.nix 394 | { repoRoot, inputs, pkgs, lib, system }: 395 | lib.iogx.mkShell { 396 | scripts = { 397 | foo = { 398 | enable = pkgs.stdenv.hostPlatform.isLinux; 399 | exec = ''' 400 | echo "I only run on Linux." 401 | '''; 402 | }; 403 | }; 404 | } 405 | ''; 406 | }; 407 | }; 408 | }; 409 | 410 | mkShell-IN-submodule = l.types.submodule { 411 | options = { 412 | 413 | name = l.mkOption { 414 | type = l.types.str; 415 | default = "nix-shell"; 416 | description = '' 417 | This field will be used as the shell's derivation name and it will also be used to fill in the default values for ${ 418 | link "mkShell..prompt" 419 | } and ${link "mkShell..welcomeMessage"}. 420 | ''; 421 | }; 422 | 423 | prompt = l.mkOption { 424 | type = l.types.nullOr l.types.str; 425 | default = null; 426 | description = '' 427 | Terminal prompt, i.e. the value of the `PS1` environment variable. 428 | 429 | You can use ANSI color escape sequences to customize your prompt, but you'll need to double-escape the left slashes because `prompt` is a nix string that will be embedded in a bash string. 430 | 431 | For example, if you would normally do this in bash: 432 | ```bash 433 | export PS1="\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] " 434 | ``` 435 | Then you need to do this in `shell.nix`: 436 | ```nix 437 | prompt = "\n\\[\\033[1;32m\\][nix-shell:\\w]\\$\\[\\033[0m\\] "; 438 | ``` 439 | This field is optional and defaults to the familiar green `nix-shell` prompt. 440 | ''; 441 | }; 442 | 443 | welcomeMessage = l.mkOption { 444 | type = l.types.nullOr l.types.str; 445 | default = null; 446 | description = '' 447 | When entering the shell, this welcome message will be printed. 448 | 449 | The same caveat about escaping back slashes in ${ 450 | link "mkShell..prompt" 451 | } applies here. 452 | 453 | This field is optional and defaults to a simple welcome message using the ${ 454 | link "mkShell..name" 455 | } field. 456 | ''; 457 | }; 458 | 459 | packages = l.mkOption { 460 | type = l.types.listOf l.types.package; 461 | default = [ ]; 462 | description = '' 463 | You can add anything you want here, so long as it's a derivation with executables in the `/bin` folder. 464 | 465 | What you put here ends up in your `$PATH` (basically the `buildInputs` in `mkDerivation`). 466 | 467 | For example: 468 | ```nix 469 | packages = [ 470 | pkgs.hello 471 | pkgs.curl 472 | pkgs.sqlite3 473 | pkgs.nodePackages.yo 474 | ] 475 | ``` 476 | 477 | If you `cabalProject` (coming from ${ 478 | link "mkHaskellProject" 479 | }) is in scope, you could use `hsPkgs` to obtain some useful binaries: 480 | ```nix 481 | packages = [ 482 | cabalProject.hsPkgs.cardano-cli.components.exes.cardano-cli 483 | cabalProject.hsPkgs.cardano-node.components.exes.cardano-node 484 | ]; 485 | ``` 486 | 487 | Be careful not to reference your project's own cabal packages via `hsPkgs`. 488 | 489 | If you do, then `nix develop` will build your project every time you enter the shell, and it will fail to do so if there are Haskell compiler errors. 490 | ''; 491 | }; 492 | 493 | scripts = l.mkOption { 494 | type = l.types.lazyAttrsOf script-submodule; 495 | default = { }; 496 | description = '' 497 | Custom scripts for your shell. 498 | 499 | `scripts` is an attrset where each attribute name is the script name each the attribute value is an attrset. 500 | 501 | The attribute names (`foobar` and `waz` in the example above) will be available in your shell as commands under the same name. 502 | ''; 503 | example = l.literalExpression '' 504 | scripts = { 505 | 506 | foobar = { 507 | exec = ''' 508 | # Bash code to be executed whenever the script `foobar` is run. 509 | echo "Delete me from your nix/shell.nix!" 510 | '''; 511 | description = ''' 512 | You might want to delete the foobar script. 513 | '''; 514 | group = "bazwaz"; 515 | enable = true; 516 | }; 517 | 518 | waz.exec = ''' 519 | echo "I don't have a group!" 520 | '''; 521 | }; 522 | ''; 523 | }; 524 | 525 | env = l.mkOption { 526 | type = l.types.lazyAttrsOf l.types.raw; 527 | default = { }; 528 | description = '' 529 | Custom environment variables. 530 | 531 | Considering the example above, the following bash code will be executed every time you enter the shell: 532 | 533 | ```bash 534 | export PGUSER="postgres" 535 | export THE_ANSWER="42" 536 | ``` 537 | ''; 538 | example = l.literalExpression '' 539 | env = { 540 | PGUSER = "postgres"; 541 | THE_ANSWER = 42; 542 | }; 543 | ''; 544 | }; 545 | 546 | shellHook = l.mkOption { 547 | type = l.types.str; 548 | default = ""; 549 | description = '' 550 | Standard nix `shellHook`, to be executed every time you enter the shell. 551 | ''; 552 | example = l.literalExpression '' 553 | shellHook = ''' 554 | # Bash code to be executed when you enter the shell. 555 | echo "I'm inside the shell!" 556 | '''; 557 | ''; 558 | }; 559 | 560 | tools = l.mkOption { 561 | type = tools-submodule; 562 | default = { }; 563 | description = '' 564 | An attrset of packages to be made available in the shell. 565 | 566 | This can be used to override the default derivations used by IOGX. 567 | 568 | The value of ${ 569 | link "mkShell..tools.haskellCompilerVersion" 570 | } will be used to determine the version of the Haskell tools (e.g. `cabal-install` or `stylish-haskell`). 571 | ''; 572 | }; 573 | 574 | preCommit = l.mkOption { 575 | type = l.types.lazyAttrsOf l.types.attrs; 576 | default = { }; 577 | description = '' 578 | Configuration for arbitrary pre-commit hooks, passed verbatim to [`pre-commit-hooks-nix`](https://github.com/cachix/pre-commit-hooks.nix#custom-hooks). 579 | 580 | This is an attrset where each attribute name is the name of the hook, and each attribute value is the attrset of options for a [`custom-hook`](https://github.com/cachix/pre-commit-hooks.nix#custom-hooks). 581 | 582 | There is an additional string option named `extraOptions` for convenience, which is appended to [`entry`](https://github.com/cachix/pre-commit-hooks.nix/blob/ffa9a5b90b0acfaa03b1533b83eaf5dead819a05/modules/pre-commit.nix#L54). 583 | 584 | The `pre-commit` executable will be made available in the shell, and should be used to test and run your hooks. 585 | 586 | Some hooks are pre-configured by default and can be enabled by setting the `enable` option to `true`. 587 | 588 | For these hooks, the `extraOptions` option becomes especially relevant. 589 | 590 | The list of pre-configured hooks is presented below: 591 | 592 | - `cabal-fmt` 593 | - `stylish-haskell` 594 | - `shellcheck` 595 | - `prettier` 596 | - `editorconfig-checker` 597 | - `nixfmt-classic` 598 | - `optipng` 599 | - `fourmolu` 600 | - `hlint` 601 | - `purs-tidy` 602 | 603 | When enabled, some of the above hooks expect to find a configuration file in the root of the repository: 604 | 605 | | Hook Name | Config File | 606 | | --------- | ----------- | 607 | | `stylish-haskell` | `.stylish-haskell.yaml` | 608 | | `editorconfig-checker` | `.editorconfig` | 609 | | `fourmolu` | `fourmolu.yaml` (note the missing dot `.`) | 610 | | `hlint` | `.hlint.yaml` | 611 | | `hindent` | `.hindent.yaml` | 612 | 613 | Currently there is no way to change the location of the configuration files. 614 | 615 | Each pre-configured hook knows which file extensions to look for, which files to ignore, and how to modify the files in-place. 616 | ''; 617 | example = l.literalExpression '' 618 | # shell.nix 619 | { repoRoot, inputs, pkgs, lib, system }: 620 | 621 | lib.iogx.mkShell { 622 | preCommit = { 623 | cabal-fmt.enable = true; 624 | cabal-fmt.extraOptions = "--tabular"; 625 | 626 | stylish-haskell.enable = false; 627 | stylish-haskell.extraOptions = ""; 628 | 629 | shellcheck.enable = false; 630 | shellcheck.extraOptions = ""; 631 | 632 | prettier.enable = false; 633 | prettier.extraOptions = ""; 634 | 635 | editorconfig-checker.enable = false; 636 | editorconfig-checker.extraOptions = ""; 637 | 638 | nixfmt-classic.enable = false; 639 | nixfmt-classic.extraOptions = ""; 640 | 641 | optipng.enable = false; 642 | optipng.extraOptions = ""; 643 | 644 | fourmolu.enable = true; 645 | fourmolu.extraOptions = "--ghc-option -XOverloadedStrings"; 646 | 647 | hlint.enable = false; 648 | hlint.extraOptions = ""; 649 | 650 | purs-tidy.enable = false; 651 | purs-tidy.extraOptions = ""; 652 | 653 | # https://github.com/cachix/pre-commit-hooks.nix#custom-hooks 654 | my-custom-hook = { 655 | extraOptions = [ "--foo" "--bar" ]; 656 | 657 | enable = true; 658 | name = "Unit tests"; 659 | entry = "make check"; 660 | files = "\\.(c|h)$"; 661 | types = [ "text" "c" ]; 662 | excludes = [ "irrelevant\\.c" ]; 663 | language = "system"; 664 | pass_filenames = false; 665 | }; 666 | }; 667 | } 668 | ''; 669 | }; 670 | }; 671 | }; 672 | 673 | mkShell-OUT-submodule = l.types.submodule { 674 | options = { 675 | 676 | pre-commit-check = l.mkOption { 677 | type = l.types.package; 678 | description = '' 679 | A derivation that when built will run all the installed shell hooks. 680 | 681 | The hooks are configured in ${link "mkShell..preCommit"}. 682 | 683 | This derivation can be included in your `packages` and in `hydraJobs`. 684 | ''; 685 | example = l.literalExpression '' 686 | { repoRoot, inputs, pkgs, lib, system }: 687 | let 688 | shell = lib.iogx.mkShell {}; 689 | in 690 | [ 691 | { 692 | devShells.foo = shell; 693 | packages.pre-commit-check = shell.pre-commit-check; 694 | hydraJobs.pre-commit-check = shell.pre-commit-check; 695 | } 696 | ] 697 | ''; 698 | }; 699 | }; 700 | }; 701 | 702 | mkShell-IN = l.mkOption { 703 | type = mkShell-IN-submodule; 704 | description = '' 705 | # Not Rendered In Docs 706 | ''; 707 | }; 708 | 709 | mkShell-OUT = l.mkOption { 710 | type = mkShell-OUT-submodule; 711 | description = '' 712 | # Not Rendered In Docs 713 | ''; 714 | }; 715 | 716 | mkShell = l.mkOption { 717 | description = '' 718 | The `lib.iogx.mkShell` function takes an attrset of options and returns a normal `devShell` with an additional attribute named ${ 719 | link "mkShell..pre-commit-check" 720 | }. 721 | 722 | In this document: 723 | - Options for the input attrset are prefixed by `mkShell.`. 724 | - The returned attrset contains the attributes prefixed by `mkShell.`. 725 | ''; 726 | type = utils.mkApiFuncOptionType mkShell-IN.type mkShell-OUT.type; 727 | example = l.literalExpression '' 728 | { repoRoot, inputs, pkgs, lib, system }: 729 | 730 | lib.iogx.mkShell { 731 | name = "dev-shell"; 732 | packages = [ pkgs.hello ]; 733 | env = { 734 | FOO = "bar"; 735 | }; 736 | scripts = { 737 | foo = { 738 | description = ""; 739 | group = "general"; 740 | enabled = false; 741 | exec = ''' 742 | echo "Hello, World!" 743 | '''; 744 | }; 745 | }; 746 | shellHook = ""; 747 | preCommit = { 748 | shellcheck.enable = true; 749 | }; 750 | tools.haskellCompilerVersion = "ghc8107"; 751 | }; 752 | ''; 753 | }; 754 | 755 | in { 756 | inherit mkShell; 757 | "mkShell." = mkShell-IN; 758 | } 759 | -------------------------------------------------------------------------------- /templates/haskell/cabal.project: -------------------------------------------------------------------------------- 1 | repository cardano-haskell-packages 2 | url: https://chap.intersectmbo.org/ 3 | secure: True 4 | root-keys: 5 | 3e0cce471cf09815f930210f7827266fd09045445d65923e6d0238a6cd15126f 6 | 443abb7fb497a134c343faf52f0b659bd7999bc06b7f63fa76dc99d631f9bea1 7 | a86a1f6ce86c449c46666bda44268677abf29b5b2d2eb5ec7af903ec2f117a82 8 | bcec67e8e99cabfa7764d75ad9b158d72bfacf70ca1d0ec8bc6b4406d1bf8413 9 | c00aae8461a256275598500ea0e187588c35a5d5d7454fb57eac18d9edb86a56 10 | d4a35cd3121aa00d18544bb0ac01c3e1691d618f462c46129271bccf39f7e8ee 11 | -------------------------------------------------------------------------------- /templates/haskell/flake.nix: -------------------------------------------------------------------------------- 1 | # Docs for this file: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#flakenix 2 | { 3 | description = "Change the description field in your flake.nix"; 4 | 5 | inputs = { 6 | iogx = { 7 | url = "github:input-output-hk/iogx"; 8 | # inputs.hackage.follows = "hackage"; 9 | # inputs.CHaP.follows = "CHaP"; 10 | # inputs.haskell-nix.follows = "haskell-nix"; 11 | # inputs.nixpkgs.follows = "nixpkgs"; 12 | }; 13 | 14 | # nixpkgs.follows = "haskell-nix/nixpkgs"; 15 | 16 | # hackage = { 17 | # url = "github:input-output-hk/hackage.nix"; 18 | # flake = false; 19 | # }; 20 | 21 | # CHaP = { 22 | # url = "github:input-output-hk/cardano-haskell-packages?ref=repo"; 23 | # flake = false; 24 | # }; 25 | 26 | # haskell-nix = { 27 | # url = "github:input-output-hk/haskell.nix"; 28 | # inputs.hackage.follows = "hackage"; 29 | # }; 30 | }; 31 | 32 | # Docs for mkFlake: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#mkflake 33 | outputs = inputs: 34 | inputs.iogx.lib.mkFlake { 35 | 36 | inherit inputs; 37 | 38 | repoRoot = ./.; 39 | 40 | outputs = import ./nix/outputs.nix; 41 | 42 | # systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" "aarch64-linux" ]; 43 | 44 | # debug = false; 45 | 46 | # nixpkgsArgs = { 47 | # config = {}; 48 | # overlays = []; 49 | # }; 50 | 51 | # flake = { repoRoot, inputs }: {}; 52 | }; 53 | 54 | nixConfig = { 55 | extra-substituters = [ "https://cache.iog.io" ]; 56 | extra-trusted-public-keys = 57 | [ "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" ]; 58 | allow-import-from-derivation = true; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /templates/haskell/nix/outputs.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, inputs, pkgs, lib, system }: 2 | 3 | let 4 | 5 | project = repoRoot.nix.project; 6 | 7 | in [ 8 | ( 9 | # Docs for project.flake: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#mkhaskellprojectoutflake 10 | project.flake) 11 | ] 12 | -------------------------------------------------------------------------------- /templates/haskell/nix/project.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, inputs, pkgs, system, lib }: 2 | 3 | let 4 | 5 | cabalProject' = pkgs.haskell-nix.cabalProject' ({ pkgs, config, ... }: 6 | let 7 | # When `isCross` is `true`, it means that we are cross-compiling the project. 8 | # WARNING You must use the `pkgs` coming from cabalProject' for `isCross` to work. 9 | isCross = pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform; 10 | in { 11 | src = ../.; 12 | 13 | shell.withHoogle = false; 14 | 15 | inputMap = { 16 | "https://chap.intersectmbo.org/" = inputs.iogx.inputs.CHaP; 17 | }; 18 | 19 | name = "my-project"; 20 | 21 | compiler-nix-name = lib.mkDefault "ghc96"; 22 | 23 | # flake.variants.profiled = { 24 | # modules = [{ 25 | # enableProfiling = true; 26 | # enableLibraryProfiling = true; 27 | # }]; 28 | # }; 29 | 30 | # flake.variants.ghc928 = { 31 | # compiler-nix-name = "ghc928"; 32 | # }; 33 | 34 | # flake.variants.ghc8107 = { 35 | # compiler-nix-name = "ghc8107"; 36 | # }; 37 | 38 | modules = [ { packages = { }; } { packages = { }; } ]; 39 | }); 40 | 41 | cabalProject = cabalProject'.appendOverlays [ ]; 42 | 43 | # Docs for mkHaskellProject: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#mkhaskellproject 44 | project = lib.iogx.mkHaskellProject { 45 | inherit cabalProject; 46 | 47 | shellArgs = repoRoot.nix.shell; 48 | 49 | # includeMingwW64HydraJobs = false; 50 | 51 | # includeProfiledHydraJobs = false; 52 | 53 | # readTheDocs = { 54 | # enable = false; 55 | # siteFolder = "doc/read-the-docs-site"; 56 | # sphinxToolchain = null; 57 | # }; 58 | 59 | # combinedHaddock = { 60 | # enable = false; 61 | # prologue = ""; 62 | # packages = []; 63 | # }; 64 | }; 65 | 66 | in project 67 | -------------------------------------------------------------------------------- /templates/haskell/nix/shell.nix: -------------------------------------------------------------------------------- 1 | # Docs for this file: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#mkhaskellprojectinshellargs 2 | # See `shellArgs` in `mkHaskellProject` in ./project.nix for more details. 3 | 4 | { repoRoot, inputs, pkgs, lib, system }: 5 | 6 | # Each flake variant defined in your project.nix project will yield a separate 7 | # shell. If no flake variants are defined, then cabalProject is the original 8 | # project. 9 | cabalProject: 10 | 11 | { 12 | name = "nix-shell"; 13 | 14 | # prompt = null; 15 | 16 | # welcomeMessage = null; 17 | 18 | # packages = []; 19 | 20 | # scripts = { 21 | # foo = { 22 | # description = ""; 23 | # group = "general"; 24 | # enabled = true; 25 | # exec = '' 26 | # echo "Hello, World!" 27 | # ''; 28 | # }; 29 | # }; 30 | 31 | # env = { 32 | # KEY = "VALUE"; 33 | # }; 34 | 35 | # shellHook = ""; 36 | 37 | tools = { 38 | # haskellCompilerVersion = cabalProject.args.compiler-nix-name; 39 | # cabal-fmt = null; 40 | # cabal-install = null; 41 | # haskell-language-server = null; 42 | # haskell-language-server-wrapper = null; 43 | # fourmolu = null; 44 | # hlint = null; 45 | # stylish-haskell = null; 46 | # ghcid = null; 47 | # shellcheck = null; 48 | # prettier = null; 49 | # editorconfig-checker = null; 50 | # nixfmt-classic = null; 51 | # optipng = null; 52 | # purs-tidy = null; 53 | }; 54 | 55 | # preCommit = { 56 | # cabal-fmt.enable = false; 57 | # cabal-fmt.extraOptions = ""; 58 | # stylish-haskell.enable = false; 59 | # stylish-haskell.extraOptions = ""; 60 | # fourmolu.enable = false; 61 | # fourmolu.extraOptions = ""; 62 | # hlint.enable = false; 63 | # hlint.extraOptions = ""; 64 | # shellcheck.enable = false; 65 | # shellcheck.extraOptions = ""; 66 | # prettier.enable = false; 67 | # prettier.extraOptions = ""; 68 | # editorconfig-checker.enable = false; 69 | # editorconfig-checker.extraOptions = ""; 70 | # nixfmt-classic.enable = false; 71 | # nixfmt-classic.extraOptions = ""; 72 | # optipng.enable = false; 73 | # optipng.extraOptions = ""; 74 | # purs-tidy.enable = false; 75 | # purs-tidy.extraOptions = ""; 76 | # }; 77 | } 78 | -------------------------------------------------------------------------------- /templates/vanilla/flake.nix: -------------------------------------------------------------------------------- 1 | # Docs for this file: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#flakenix 2 | { 3 | description = "Change the description field in your flake.nix"; 4 | 5 | inputs = { iogx.url = "github:input-output-hk/iogx"; }; 6 | 7 | # Docs for mkFlake: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#mkflake 8 | outputs = inputs: 9 | inputs.iogx.lib.mkFlake { 10 | 11 | inherit inputs; 12 | 13 | repoRoot = ./.; 14 | 15 | outputs = import ./nix/outputs.nix; 16 | 17 | # systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" "aarch64-linux" ]; 18 | 19 | # debug = false; 20 | 21 | # nixpkgsArgs = { 22 | # config = {}; 23 | # overlays = []; 24 | # }; 25 | 26 | # flake = { repoRoot, inputs }: {}; 27 | }; 28 | 29 | nixConfig = { 30 | extra-substituters = [ "https://cache.iog.io" ]; 31 | extra-trusted-public-keys = 32 | [ "hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ=" ]; 33 | allow-import-from-derivation = true; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /templates/vanilla/nix/outputs.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, inputs, pkgs, lib, system }: 2 | 3 | [{ 4 | devShells.default = repoRoot.nix.shell; 5 | 6 | hydraJobs.devShells.default = inputs.self.devShells.default; 7 | }] 8 | -------------------------------------------------------------------------------- /templates/vanilla/nix/shell.nix: -------------------------------------------------------------------------------- 1 | { repoRoot, inputs, pkgs, lib, system }: 2 | 3 | # Docs for mkShell: https://github.com/input-output-hk/iogx/blob/main/doc/api.md#mkshell 4 | lib.iogx.mkShell { 5 | 6 | name = "nix-shell"; 7 | 8 | # prompt = null; 9 | 10 | # welcomeMessage = null; 11 | 12 | # packages = []; 13 | 14 | # scripts = { 15 | # foo = { 16 | # description = ""; 17 | # group = "general"; 18 | # enabled = true; 19 | # exec = '' 20 | # echo "Hello, World!" 21 | # ''; 22 | # }; 23 | # }; 24 | 25 | # env = { 26 | # KEY = "VALUE"; 27 | # }; 28 | 29 | # shellHook = ""; 30 | 31 | tools = { 32 | # haskellCompilerVersion = "ghc8107"; 33 | # cabal-fmt = null; 34 | # cabal-install = null; 35 | # haskell-language-server = null; 36 | # haskell-language-server-wrapper = null; 37 | # fourmolu = null; 38 | # hlint = null; 39 | # stylish-haskell = null; 40 | # ghcid = null; 41 | # shellcheck = null; 42 | # prettier = null; 43 | # editorconfig-checker = null; 44 | # nixfmt-classic = null; 45 | # optipng = null; 46 | # purs-tidy = null; 47 | }; 48 | 49 | # preCommit = { 50 | # cabal-fmt.enable = false; 51 | # cabal-fmt.extraOptions = ""; 52 | # stylish-haskell.enable = false; 53 | # stylish-haskell.extraOptions = ""; 54 | # fourmolu.enable = false; 55 | # fourmolu.extraOptions = ""; 56 | # hlint.enable = false; 57 | # hlint.extraOptions = ""; 58 | # shellcheck.enable = false; 59 | # shellcheck.extraOptions = ""; 60 | # prettier.enable = false; 61 | # prettier.extraOptions = ""; 62 | # editorconfig-checker.enable = false; 63 | # editorconfig-checker.extraOptions = ""; 64 | # nixfmt-classic.enable = false; 65 | # nixfmt-classic.extraOptions = ""; 66 | # optipng.enable = false; 67 | # optipng.extraOptions = ""; 68 | # purs-tidy.enable = false; 69 | # purs-tidy.extraOptions = ""; 70 | # }; 71 | } 72 | --------------------------------------------------------------------------------