From cb84f77f1d845a986369b62d561be24a85a5b165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 6 Jun 2024 19:08:07 +0200 Subject: [PATCH 01/13] Make the toplevel interface a module --- lib/lib.nix | 8 ++-- lib/modules/main.ncl | 44 +++++++++++++++++++ lib/organist.ncl | 1 + lib/schema.ncl | 2 +- project.ncl | 102 ++++++++++++++++++++++--------------------- 5 files changed, 104 insertions(+), 53 deletions(-) create mode 100644 lib/modules/main.ncl diff --git a/lib/lib.nix b/lib/lib.nix index 6dba8f0d..6abbea84 100644 --- a/lib/lib.nix +++ b/lib/lib.nix @@ -11,8 +11,6 @@ # Export a Nix value to be consumed by Nickel typeField = "$__organist_type"; - isInStore = lib.hasPrefix builtins.storeDir; - # Take a symbolic derivation (a datastructure representing a derivation), as # produced by Nickel, and transform it into valid arguments to # `derivation` @@ -87,7 +85,11 @@ let organistType = value."${typeField}" or ""; in - if isNickelDerivation organistType + if organistType == "module" + then + assert value ? config; + importFromNickel_ value.config + else if isNickelDerivation organistType then let prepared = prepareDerivation system (builtins.mapAttrs (_: importFromNickel_) diff --git a/lib/modules/main.ncl b/lib/modules/main.ncl new file mode 100644 index 00000000..3223cb4d --- /dev/null +++ b/lib/modules/main.ncl @@ -0,0 +1,44 @@ +# A library defining some kind of module system for Nickel +{ + T + | doc m%" + The type of a module. + "% + = { + config | Schema = {}, + Schema + | not_exported + = {}, + "$__organist_type" | force = "module", + }, + + submodules + | { _ : T } -> T + | doc m%" + Joins all the given module into a single one, prefixing all the keys of + their options with the key of the corresponding module. + + ```nickel + submodules { + mod1 = { Schema.foo | Number, config.foo = 1 }, + mod2 = { Schema.foo | String, config.foo = "bar" }, + } + + => { + Schema = { mod1.foo | Number, mod2.foo | String }, + config = { mod1.foo = 1, mod2.bar = "bar" } + } + ``` + "% + = fun modules => + let configs = modules |> std.record.map (fun k v => v.config) in + let schemas = modules |> std.record.map (fun k v => v.Schema) in + { + Schema = schemas, + config | Schema = configs, + } | T, + + merge2 + | T -> T -> T + = fun m1 m2 => m1 & m2, +} diff --git a/lib/organist.ncl b/lib/organist.ncl index ef6d7096..d9f2e1db 100644 --- a/lib/organist.ncl +++ b/lib/organist.ncl @@ -1,5 +1,6 @@ { nix = import "./nix-interop/nix.ncl", + modules = import "./modules/main.ncl", shells = nix.shells, schema = import "./schema.ncl", OrganistExpression = schema.OrganistExpression, diff --git a/lib/schema.ncl b/lib/schema.ncl index b293f6ce..d8358c75 100644 --- a/lib/schema.ncl +++ b/lib/schema.ncl @@ -47,7 +47,7 @@ let filegen = import "files.ncl" in # # The contract must be: what the Nix side of the code can "parse" without # erroring out. - OrganistExpression = + OrganistExpression.Schema = { shells | doc m%" diff --git a/project.ncl b/project.ncl index 39fc528c..1ba551e4 100644 --- a/project.ncl +++ b/project.ncl @@ -2,56 +2,60 @@ let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in let import_nix = organist.nix.import_nix in -{ - shells = - organist.shells.Bash - & { - build.packages = { - nickel = import_nix "nixpkgs#nickel", - parallel = import_nix "nixpkgs#parallel", - gnused = import_nix "nixpkgs#gnused", - }, - }, +organist.OrganistExpression +& { + Schema, + config | Schema = { - flake.apps.run-test = - let testScript | organist.nix.builders.ShellApplication = { - name = "run-test.sh", - content.file = organist.nix.builtins.import_file "run-test.sh", - runtime_inputs = { - nickel = import_nix "nixpkgs#nickel", - parallel = import_nix "nixpkgs#parallel", - gnused = import_nix "nixpkgs#gnused", + shells = + organist.shells.Bash + & { + build.packages = { + nickel = import_nix "nixpkgs#nickel", + parallel = import_nix "nixpkgs#parallel", + gnused = import_nix "nixpkgs#gnused", + }, }, - } - in - { - type = "app", - program | organist.nix.derivation.NixString = nix-s%"%{testScript}/bin/run-test.sh"% - }, - flake.packages = { - # Re-exported to make it easy for downstream to depend on them - nickel = import_nix "nixpkgs#nickel", - nls = import_nix "nixpkgs#nls", - }, + flake.apps.run-test = + let testScript | organist.nix.builders.ShellApplication = { + name = "run-test.sh", + content.file = organist.nix.builtins.import_file "run-test.sh", + runtime_inputs = { + nickel = import_nix "nixpkgs#nickel", + parallel = import_nix "nixpkgs#parallel", + gnused = import_nix "nixpkgs#gnused", + }, + } + in + { + type = "app", + program | organist.nix.derivation.NixString = nix-s%"%{testScript}/bin/run-test.sh"% + }, - flake.checks - | { _ | organist.nix.builders.NixpkgsPkg } - = { - alejandra = { - name = "check-alejandra", - version = "0.0", - env.buildCommand = nix-s%" + flake.packages = { + # Re-exported to make it easy for downstream to depend on them + nickel = import_nix "nixpkgs#nickel", + nls = import_nix "nixpkgs#nls", + }, + + flake.checks + | { _ | organist.nix.builders.NixpkgsPkg } + = { + alejandra = { + name = "check-alejandra", + version = "0.0", + env.buildCommand = nix-s%" %{import_nix "nixpkgs#alejandra"}/bin/alejandra --check %{import_nix "self"} touch $out "%, - }, + }, - nickel-format = { - name = "check-nickel-format", - version = "0.0", - env.buildInputs.nickel = import_nix "nixpkgs#nickel", - env.buildCommand = nix-s%" + nickel-format = { + name = "check-nickel-format", + version = "0.0", + env.buildInputs.nickel = import_nix "nixpkgs#nickel", + env.buildCommand = nix-s%" cd %{import_nix "self"} failed="" for f in $(find . -name future -prune -or -name '*.ncl' -print); do @@ -65,17 +69,17 @@ let import_nix = organist.nix.import_nix in fi touch $out "%, - }, - }, + }, + }, - flake.checks = import "tests/main.ncl", + flake.checks = import "tests/main.ncl", - files.".gitignore".materialisation_method = 'Copy, - files.".gitignore".content = m%" + files.".gitignore".materialisation_method = 'Copy, + files.".gitignore".content = m%" # This file is generated by Organist, please don't edit directly /examples/*/result /result "%, -} - | organist.OrganistExpression + }, +} | organist.modules.T From da8747265d4d025cd7ca394d03be15f235264c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 6 Jun 2024 19:17:33 +0200 Subject: [PATCH 02/13] Use a module for the files generation --- lib/files.ncl | 134 ++++++++++++++++++++++++++----------------------- lib/schema.ncl | 20 ++++---- 2 files changed, 81 insertions(+), 73 deletions(-) diff --git a/lib/files.ncl b/lib/files.ncl index bbf11298..c6b0ea8d 100644 --- a/lib/files.ncl +++ b/lib/files.ncl @@ -1,86 +1,92 @@ let nix = import "./nix-interop/nix.ncl" in -{ - File = { - target - | doc m%" +let File = { + target + | doc m%" The file to write to. If null, defaults to the attribute name of the file. "% - | String - | optional, - content - | doc m%" + | String + | optional, + content + | doc m%" The content of the file. "% - | nix.derivation.NixString, - materialisation_method - : [| 'Symlink, 'Copy |] - | doc m%" + | nix.derivation.NixString, + materialisation_method + : [| 'Symlink, 'Copy |] + | doc m%" How the file should be materialized on-disk. Symlinking makes it easier to track where the files are coming from, but their target only exists after a first call to Organist, which might be undesirable. "% - | default - = 'Copy, - .. - }, - Files = { _ : File }, - NormaliseTargets = fun label files => - files - |> std.record.map (fun name file_descr => file_descr & { target | default = name }), - - Schema = { - files - | Files - | NormaliseTargets - | doc m%" - Set of files that should be generated in the project's directory. - "% - = {}, - flake.apps.regenerate-files.program = nix-s%"%{regenerate_files files}/bin/regenerate-files"%, - }, - - regenerate_files | Files -> nix.derivation.Derivation = fun files_to_generate => - let regnerate_one | String -> File -> nix.derivation.NixString = fun key file_descr => - let file_content = file_descr.content in - let target = file_descr.target in - let copy_command = - match { - 'Symlink => "ln -s", - 'Copy => "cp", - } - file_descr.materialisation_method - in - let file_in_store = - nix.builtins.to_file - (nix.utils.escape_drv_name key) - file_content - in - nix-s%" + | default + = 'Copy, + .. +} +in +let Files = { _ : File } +in +let NormaliseTargets = fun label files => + files + |> std.record.map (fun name file_descr => file_descr & { target | default = name }) +in +let regenerate_files | Files -> nix.derivation.Derivation = fun files_to_generate => + let regnerate_one | String -> File -> nix.derivation.NixString = fun key file_descr => + let file_content = file_descr.content in + let target = file_descr.target in + let copy_command = + match { + 'Symlink => "ln -s", + 'Copy => "cp", + } + file_descr.materialisation_method + in + let file_in_store = + nix.builtins.to_file + (nix.utils.escape_drv_name key) + file_content + in + nix-s%" rm -f %{target} echo "Regenerating %{target}" target_dir=$(dirname %{target}) test "${target_dir}" != "." && mkdir -p "${target_dir}" %{copy_command} %{file_in_store} %{target} "% - in - { - name = "regenerate-files", - content.text = - files_to_generate - |> std.record.to_array - |> std.array.map (fun { field, value } => regnerate_one field value) - |> std.array.fold_left - ( - fun acc elt => - nix-s%" + in + { + name = "regenerate-files", + content.text = + files_to_generate + |> std.record.to_array + |> std.array.map (fun { field, value } => regnerate_one field value) + |> std.array.fold_left + ( + fun acc elt => + nix-s%" %{acc} %{elt} "% - ) - "", - } - | nix.builders.ShellApplication, + ) + "", + } + | nix.builders.ShellApplication + in +{ + Schema = { + files + | Files + | NormaliseTargets + | doc m%" + Set of files that should be generated in the project's directory. + "% + = {}, + flake.apps, # Forward declaration. Not great but would need some refactor to fix + }, + config | Schema = { + files, + flake.apps.regenerate-files.program = nix-s%"%{regenerate_files files}/bin/regenerate-files"%, + }, } diff --git a/lib/schema.ncl b/lib/schema.ncl index d8358c75..1da513af 100644 --- a/lib/schema.ncl +++ b/lib/schema.ncl @@ -47,21 +47,23 @@ let filegen = import "files.ncl" in # # The contract must be: what the Nix side of the code can "parse" without # erroring out. - OrganistExpression.Schema = + OrganistExpression = { - shells - | doc m%" + Schema = { + shells + | doc m%" The shells that will be entered by `nix develop`. "% - | OrganistShells, - flake - | doc m%" + | OrganistShells, + flake + | doc m%" The raw flake outputs that will be passed to Nix. These should follow the Nix flake outputs schema. "% - | FlakeOutputs - = {}, + | FlakeOutputs + = {}, + }, } - & filegen.Schema + & filegen } From 7ed577b4e6bcdad0b37c1a52db59b7d34beee6e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Thu, 6 Jun 2024 19:32:30 +0200 Subject: [PATCH 03/13] Use a module for the direnv module --- examples/direnv/project.ncl | 27 ++++++++++++++------------- lib/direnv.ncl | 23 ++++++++++++++--------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/examples/direnv/project.ncl b/examples/direnv/project.ncl index bbceeef8..cfd0971d 100644 --- a/examples/direnv/project.ncl +++ b/examples/direnv/project.ncl @@ -1,18 +1,19 @@ let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in -{ - shells = organist.shells.Bash, +organist.OrganistExpression +& organist.tools.direnv +& { + Schema, + config | Schema = { + shells = organist.shells.Bash, - shells.build = { - packages = {}, - }, + shells.build = { + packages = {}, + }, - shells.dev = { - packages.direnv = organist.import_nix "nixpkgs#direnv" - }, -} - | ( - organist.OrganistExpression - & organist.tools.direnv.Schema - ) + shells.dev = { + packages.direnv = organist.import_nix "nixpkgs#direnv" + }, + }, +} | organist.modules.T diff --git a/lib/direnv.ncl b/lib/direnv.ncl index 292eac9d..3707f44c 100644 --- a/lib/direnv.ncl +++ b/lib/direnv.ncl @@ -1,4 +1,6 @@ -{ +let files = import "files.ncl" in +files +& { Schema = { direnv = { enable @@ -9,10 +11,13 @@ | default = true, }, - files = - if direnv.enable then - { - ".envrc".content = m%" + }, + config | Schema = { + direnv, + files = + if direnv.enable then + { + ".envrc".content = m%" if ! has nix_direnv_version || ! nix_direnv_version 2.4.0; then source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/2.4.0/direnvrc" "sha256-XQzUAvL6pysIJnRJyR7uVpmUSZfc7LSgWQwq/4mBr1U=" fi @@ -20,8 +25,8 @@ source_env_if_exists ".envrc.private" use flake "%, - } - else - {}, - }, + } + else + {}, + }, } From 6cfd81dedfe7a507c33228a6828c7242ca8327a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 7 Jun 2024 05:45:17 +0200 Subject: [PATCH 04/13] Use a module for editorconfig --- examples/filegen/project.ncl | 44 +++++++++++++++++++----------------- lib/editorconfig.ncl | 25 ++++++++++++-------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/examples/filegen/project.ncl b/examples/filegen/project.ncl index d86f437e..dd385795 100644 --- a/examples/filegen/project.ncl +++ b/examples/filegen/project.ncl @@ -1,29 +1,31 @@ let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in -{ - shells = organist.shells.Bash, +organist.OrganistExpression +& organist.tools.editorconfig +& { + Schema, + config | Schema = { + shells = organist.shells.Bash, - shells.build = { - packages = {}, - }, + shells.build = { + packages = {}, + }, - shells.dev = { - packages.hello = organist.import_nix "nixpkgs#hello", - }, + shells.dev = { + packages.hello = organist.import_nix "nixpkgs#hello", + }, - editorconfig.sections = { - "*".indent_style = 'space, - "*".indent_size = 2, - "*".insert_final_newline = true, - "Makefile".indent_style = 'tab, - "*.py" = { - indent_style = 'space, - indent_size = 4, + editorconfig.sections = { + "*".indent_style = 'space, + "*".indent_size = 2, + "*".insert_final_newline = true, + "Makefile".indent_style = 'tab, + "*.py" = { + indent_style = 'space, + indent_size = 4, + }, + }, }, - }, } - | ( - organist.OrganistExpression - & organist.tools.editorconfig.Schema - ) + | organist.modules.T diff --git a/lib/editorconfig.ncl b/lib/editorconfig.ncl index 932c5419..c4d04c4f 100644 --- a/lib/editorconfig.ncl +++ b/lib/editorconfig.ncl @@ -1,3 +1,4 @@ +let files = import "./files.ncl" in let IndentSize = fun label value => if value == 'tab then value @@ -68,7 +69,8 @@ let EditorConfigSchema = { = {}, } in -{ +files +& { Schema = { editorconfig | doc m%" @@ -77,14 +79,17 @@ in | EditorConfigSchema | default = {}, - files = - if editorconfig.sections != {} then - { - ".editorconfig" = { - content = generate editorconfig.is_root editorconfig.sections, - } - } - else - {}, }, + config | Schema = { + editorconfig, + files = + if editorconfig.sections != {} then + { + ".editorconfig" = { + content = generate editorconfig.is_root editorconfig.sections, + } + } + else + {}, + }, } From 74d2bee715ff02d3833d6a312ebbd558dac0e254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 7 Jun 2024 05:54:45 +0200 Subject: [PATCH 05/13] Use a module for the services config --- examples/services/project.ncl | 65 ++++++++++++++++++----------------- lib/services.ncl | 38 +++++++++++--------- 2 files changed, 55 insertions(+), 48 deletions(-) diff --git a/examples/services/project.ncl b/examples/services/project.ncl index f2b8a624..37656acc 100644 --- a/examples/services/project.ncl +++ b/examples/services/project.ncl @@ -4,43 +4,46 @@ let organist = inputs.organist in let postgres_listen_port = 64441 in let redis_listen_port = 64442 in -{ - shells = organist.shells.Bash, +organist.OrganistExpression +& organist.tools.editorconfig +& organist.services +& { - shells.build = { - packages = {}, - }, + Schema, + config | Schema = { + shells = organist.shells.Bash, - shells.dev = { - packages = { - postgresql = organist.import_nix "nixpkgs#postgresql", - redis = organist.import_nix "nixpkgs#redis", - honcho = organist.import_nix "nixpkgs#honcho", - }, - env.POSTGRES_PORT = "%{std.to_string postgres_listen_port}", - env.REDIS_URI = "redis://localhost:%{std.to_string redis_listen_port}", - }, + shells.build = { + packages = {}, + }, + + shells.dev = { + packages = { + postgresql = organist.import_nix "nixpkgs#postgresql", + redis = organist.import_nix "nixpkgs#redis", + honcho = organist.import_nix "nixpkgs#honcho", + }, + env.POSTGRES_PORT = "%{std.to_string postgres_listen_port}", + env.REDIS_URI = "redis://localhost:%{std.to_string redis_listen_port}", + }, - services = { - postgres = - let start_script = - { - name = "start-postgres", - runtime_inputs.postgres = organist.import_nix "nixpkgs#postgresql", - content.text = nix-s%" + services = { + postgres = + let start_script = + { + name = "start-postgres", + runtime_inputs.postgres = organist.import_nix "nixpkgs#postgresql", + content.text = nix-s%" mkdir -p ./.postgres-data initdb ./.postgres-data postgres -D ./.postgres-data -k "$PWD/.postgres-data" -p %{std.to_string postgres_listen_port} "% - } | organist.nix.builders.ShellApplication - in - nix-s%"%{start_script}/bin/start-postgres"%, - redis = nix-s%"%{organist.import_nix "nixpkgs#redis"}/bin/redis-server --port %{std.to_string redis_listen_port}"%, - }, + } | organist.nix.builders.ShellApplication + in + nix-s%"%{start_script}/bin/start-postgres"%, + redis = nix-s%"%{organist.import_nix "nixpkgs#redis"}/bin/redis-server --port %{std.to_string redis_listen_port}"%, + }, + }, } - | ( - organist.OrganistExpression - & organist.tools.editorconfig.Schema - & organist.services.Schema - ) + | organist.modules.T diff --git a/lib/services.ncl b/lib/services.ncl index 30990cce..39404ddb 100644 --- a/lib/services.ncl +++ b/lib/services.ncl @@ -36,21 +36,25 @@ let generate_procfile | ProcfileSchema -> nix.derivation.NixString = fun schema | { _ : nix.derivation.NixString } | default = {}, - flake.apps = - if services != {} then - let procfile = nix.builtins.to_file "Procfile" (generate_procfile services) in - { - start-services.type = "app", - start-services.program | nix.derivation.NixString = - let run = - nix.builders.ShellApplication - & { - name = "start-services", - content.text = nix-s%"exec %{nix.import_nix "nixpkgs#honcho"}/bin/honcho -f %{procfile} "$@""% - } - in nix-s%"%{run}/bin/start-services"% - } - else - {}, - } + flake.apps, + }, + config | Schema = { + services, + flake.apps = + if services != {} then + let procfile = nix.builtins.to_file "Procfile" (generate_procfile services) in + { + start-services.type = "app", + start-services.program | nix.derivation.NixString = + let run = + nix.builders.ShellApplication + & { + name = "start-services", + content.text = nix-s%"exec %{nix.import_nix "nixpkgs#honcho"}/bin/honcho -f %{procfile} "$@""% + } + in nix-s%"%{run}/bin/start-services"% + } + else + {}, + }, } From f677689132338ebd4c3a3317e986001a872e109d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 7 Jun 2024 05:57:14 +0200 Subject: [PATCH 06/13] Migrate the remaining examples to the schema structure --- examples/c-hello-world/project.ncl | 44 ++++++++++++++----------- examples/raw_nix_expression/project.ncl | 34 ++++++++++--------- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/examples/c-hello-world/project.ncl b/examples/c-hello-world/project.ncl index 2a9fd564..9aca4def 100644 --- a/examples/c-hello-world/project.ncl +++ b/examples/c-hello-world/project.ncl @@ -1,30 +1,34 @@ let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in -{ - flake.packages."default" = flake.packages.hello, - flake.packages.hello = - organist.nix.builders.NixpkgsPkg - & { - name = "hello", - version = "0.1", - structured_env.buildInputs = { - gcc = organist.import_nix "nixpkgs#gcc", - coreutils = organist.import_nix "nixpkgs#coreutils", - bash = organist.import_nix "nixpkgs#bash", - }, - env.src = organist.nix.builtins.import_file ".", - env.buildCommand = - nix-s%" +organist.OrganistExpression +& { + Schema, + config | Schema = { + flake.packages."default" = flake.packages.hello, + flake.packages.hello = + organist.nix.builders.NixpkgsPkg + & { + name = "hello", + version = "0.1", + structured_env.buildInputs = { + gcc = organist.import_nix "nixpkgs#gcc", + coreutils = organist.import_nix "nixpkgs#coreutils", + bash = organist.import_nix "nixpkgs#bash", + }, + env.src = organist.nix.builtins.import_file ".", + env.buildCommand = + nix-s%" cp $src/* . mkdir -p $out/{include,lib} gcc -shared hello.c -o libhello.so cp *.so $out/lib/ cp hello.h $out/include/hello.h "% - | organist.nix.derivation.NixString, - }, + | organist.nix.derivation.NixString, + }, - shells = organist.shells.Bash, - shells.build.packages.hello = flake.packages.hello, -} | organist.OrganistExpression + shells = organist.shells.Bash, + shells.build.packages.hello = flake.packages.hello, + }, +} | organist.modules.T diff --git a/examples/raw_nix_expression/project.ncl b/examples/raw_nix_expression/project.ncl index e2d0f347..04265a00 100644 --- a/examples/raw_nix_expression/project.ncl +++ b/examples/raw_nix_expression/project.ncl @@ -1,23 +1,27 @@ let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in -{ - shells = organist.shells.Bash, +organist.OrganistExpression +& { + Schema, + config | Schema = { + shells = organist.shells.Bash, - shells.build = { - packages = {}, - }, + shells.build = { + packages = {}, + }, - shells.dev = { - packages.python = - organist.nix.derivation.CallNix - & { - function = m%" + shells.dev = { + packages.python = + organist.nix.derivation.CallNix + & { + function = m%" { python }: python.withPackages (p: [p.pyyaml]) "%, - args = { - python = organist.import_nix "nixpkgs#python3" - }, + args = { + python = organist.import_nix "nixpkgs#python3" + }, + }, }, - }, -} | organist.OrganistExpression + }, +} | organist.modules.T From 6c9b1adf47854961bceeef24af903b5d570ad219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 7 Jun 2024 06:10:14 +0200 Subject: [PATCH 07/13] Migrate the project to the module system --- templates/default/project.ncl | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/templates/default/project.ncl b/templates/default/project.ncl index e7cb8bc4..82840353 100644 --- a/templates/default/project.ncl +++ b/templates/default/project.ncl @@ -1,15 +1,19 @@ let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in -{ - shells = organist.shells.Bash, +organist.OrganistExpression +& { + Schema, + config | Schema = { + shells = organist.shells.Bash, - shells.build = { - packages = {}, - }, + shells.build = { + packages = {}, + }, - shells.dev = { - packages.hello = organist.import_nix "nixpkgs#hello", - }, + shells.dev = { + packages.hello = organist.import_nix "nixpkgs#hello", + }, + }, } - | organist.OrganistExpression + | organist.modules.T From fd6e42bdd286d328a850033f5723fac2a77b5cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Fri, 7 Jun 2024 06:19:28 +0200 Subject: [PATCH 08/13] Mirror the module system in the documentation --- RELEASES.md | 7 +++++++ doc/dependency-management.md | 24 ++++++++++++++---------- doc/services.md | 10 ++++------ 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 82de2cc0..7c1a633a 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,10 @@ +## HEAD + +### Breaking changes + +- The overall structure has been reshaped to use a NixOS-modules-like interface. + XXX: Extend and explain how to migrate + ## Version 0.1 (2023-11-16) First release (🎉) diff --git a/doc/dependency-management.md b/doc/dependency-management.md index d01939f1..66d75439 100644 --- a/doc/dependency-management.md +++ b/doc/dependency-management.md @@ -10,17 +10,21 @@ After running `nix flake init`, you should end-up with a `project.ncl` looking l let inputs = import "./nickel.lock.ncl" in let organist = inputs.organist in -{ - shells = organist.shells.Bash, - - shells.build = { - packages = {}, - }, - - shells.dev = { - packages.hello = organist.lib.import_nix "nixpkgs#hello", +organist.contracts.OrganistExpression +& { + Schema, + config | Schema = { + shells = organist.shells.Bash, + + shells.build = { + packages = {}, + }, + + shells.dev = { + packages.hello = organist.lib.import_nix "nixpkgs#hello", + }, }, -} | organist.contracts.OrganistExpression +} | organist.modules.T ``` This defines two variants of the shell: `build` and `dev`, both of which inherit from `organist.shells.Bash` (more precisely, `build` inherits from `organist.shells.Bash.build` and `dev` inherits from `organist.shells.Bash.dev`). diff --git a/doc/services.md b/doc/services.md index 684eb55f..19096f05 100644 --- a/doc/services.md +++ b/doc/services.md @@ -8,13 +8,11 @@ For instance, if the `project.ncl` contains: ```nickel let redis_listen_port = 64442 in -{ - services.redis = nix-s%"%{organist.import_nix "nixpkgs#redis"}/bin/redis-server --port %{std.to_string redis_listen_port}"%, +organist.OrganistExpression +& organist.services +& { + config.services.redis = nix-s%"%{organist.import_nix "nixpkgs#redis"}/bin/redis-server --port %{std.to_string redis_listen_port}"%, } - | ( - organist.OrganistExpression - & organist.services.Schema - ) ``` then running `nix run .#start-services start` will run a local `redis` instance listening on port 64442. From 12eaa6415a109c4d2b38a3b7109c3dac51bddc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Mon, 17 Jun 2024 10:23:40 +0200 Subject: [PATCH 09/13] document the migration to the module system --- doc/migrate-from-0.1.md | 74 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 doc/migrate-from-0.1.md diff --git a/doc/migrate-from-0.1.md b/doc/migrate-from-0.1.md new file mode 100644 index 00000000..dd8ea7f5 --- /dev/null +++ b/doc/migrate-from-0.1.md @@ -0,0 +1,74 @@ +# Migrating from 0.1 + +Since 0.2, Organist uses a [module system] for the configuration. +This means that the `project.ncl` is now a module, with a slightly different interface. + +## Migrating `project.ncl` + +In the vast majority of cases, the migration consists of three things: + +1. Declare a new `Schema` field; +2. Move all the options under a `config` field; +3. Replace the schema by `organist.module.T`, and instead merge the config with the previous schema. + +As an example is probably more eloquent, this means rewriting: + +```nickel +{ + shells = …, + foo = bar, + … +} | (organist.OrganistExpression & organist.something.Schema & organist.somethingElse.Schema) +``` + +into: + +```nickel +organist.OrganistExpression +& organist.something +& organist.somethingElse +& { + Schema, + config | Schema = { + shells = …, + foo = bar, + … + }, +} | organist.module.T +``` + +## Migrating custom modules + +Migrating custom modules is similar, except that +1. The module system now separates the _declaration_ of options from their _definition_ (respectively the `Schema` and `config` fields) +2. Modules must now merge with all of their dependencies (the modules whose option they use). + +For instance, an hypothetical module like the one below: + +```nickel +{ + readme.content + | doc "Content of the README file" + | String, + files."foo".content = readme.content, +} +``` + +should be rewritten to + +```nickel +(import organist.files) # Because we set the `files` option which is defined there +& { + # New options defined by the module + Schema = { + readme.content + | doc "Content of the README file" + | String, + }, + # Implementation of the module, setting options from other modules + config | Schema = { + readme, + files."foo".content = readme.content, + }, +} +``` From 9a4ee05880f7b22eae1ee09ba9dcee7bfa8fd4d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 19 Jun 2024 18:01:46 +0200 Subject: [PATCH 10/13] Apply suggestions from code review Co-authored-by: Yann Hamdaoui --- lib/modules/main.ncl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/modules/main.ncl b/lib/modules/main.ncl index 3223cb4d..93fe472b 100644 --- a/lib/modules/main.ncl +++ b/lib/modules/main.ncl @@ -7,6 +7,7 @@ = { config | Schema = {}, Schema + | { .. } | not_exported = {}, "$__organist_type" | force = "module", @@ -15,7 +16,7 @@ submodules | { _ : T } -> T | doc m%" - Joins all the given module into a single one, prefixing all the keys of + Joins all the given modules into a single one, prefixing all the keys of their options with the key of the corresponding module. ```nickel From e43b559550c84850357bb28b264b788fd9ffd166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 19 Jun 2024 17:58:21 +0200 Subject: [PATCH 11/13] Remove a useless module merge function Just an alias for `&`, but which breaks the LSP --- lib/modules/main.ncl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/modules/main.ncl b/lib/modules/main.ncl index 93fe472b..0a9fd080 100644 --- a/lib/modules/main.ncl +++ b/lib/modules/main.ncl @@ -38,8 +38,4 @@ Schema = schemas, config | Schema = configs, } | T, - - merge2 - | T -> T -> T - = fun m1 m2 => m1 & m2, } From 04bfa741c2371fca1bfbfefdafcf4367d6b7ebe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 19 Jun 2024 18:38:01 +0200 Subject: [PATCH 12/13] Only export the `flake` field of the nickel config (#208) Makes it clear what Nix has (or not) access to. Also makes it possible in the future to fearlessly use enums in the config, as they won't be exported --- flake.nix | 10 ++++------ lib/lib.nix | 6 +++--- lib/schema.ncl | 4 ++++ 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/flake.nix b/flake.nix index 80e06fac..32a06a8f 100644 --- a/flake.nix +++ b/flake.nix @@ -44,17 +44,15 @@ then {} else { inherit nickelOutputs; - packages = nickelOutputs.packages or {} // nickelOutputs.flake.packages or {}; - checks = nickelOutputs.flake.checks or {}; + packages = nickelOutputs.packages or {}; + checks = nickelOutputs.checks or {}; # Can't define this app in Nickel, yet apps = { regenerate-lockfile = lib.regenerateLockFileApp lockFileContents; } - // nickelOutputs.flake.apps or {}; - # We can't just copy `shells` to `flake.devShells` in the contract - # because of a bug in Nickel: https://github.com/tweag/nickel/issues/1630 - devShells = nickelOutputs.shells or {} // nickelOutputs.flake.devShells or {}; + // nickelOutputs.apps or {}; + devShells = nickelOutputs.devShells or {}; }); computedOutputs = outputsFromNickel ./. (inputs // {organist = self;}) { diff --git a/lib/lib.nix b/lib/lib.nix index 6abbea84..3eaa119a 100644 --- a/lib/lib.nix +++ b/lib/lib.nix @@ -157,7 +157,7 @@ in let organist = (import "${src}/nickel.lock.ncl").organist in - let nickel_expr | (organist.OrganistExpression & {..}) = + let nickel_expr = import "${src}/${nickelFile}" in nickel_expr & params @@ -184,13 +184,13 @@ cat > eval.ncl < $out + ${nickel}/bin/nickel export eval.ncl --field config.flake > $out '' else '' cat > eval.ncl < $out + ${nickel}/bin/nickel export eval.ncl --field config.flake > $out '' ); diff --git a/lib/schema.ncl b/lib/schema.ncl index 1da513af..207b05d7 100644 --- a/lib/schema.ncl +++ b/lib/schema.ncl @@ -64,6 +64,10 @@ let filegen = import "files.ncl" in | FlakeOutputs = {}, }, + config | Schema = { + shells, + flake.devShells = shells, + }, } & filegen } From 88b3dc7cc95997b8f59cd37099c6293188e7f598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= <7226587+thufschmitt@users.noreply.github.com> Date: Wed, 19 Jun 2024 19:21:50 +0200 Subject: [PATCH 13/13] =?UTF-8?q?Add=20an=20=E2=80=9CExample=E2=80=9D=20he?= =?UTF-8?q?ader=20to=20a=20documentation=20field?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align with the usual Nickel convention Co-authored-by: Yann Hamdaoui --- lib/modules/main.ncl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/modules/main.ncl b/lib/modules/main.ncl index 0a9fd080..bbbd1247 100644 --- a/lib/modules/main.ncl +++ b/lib/modules/main.ncl @@ -19,6 +19,8 @@ Joins all the given modules into a single one, prefixing all the keys of their options with the key of the corresponding module. + # Example + ```nickel submodules { mod1 = { Schema.foo | Number, config.foo = 1 },