Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configuration file formats for JSON, INI, YAML and TOML #75584

Merged
merged 6 commits into from
Aug 2, 2020

Conversation

infinisil
Copy link
Member

@infinisil infinisil commented Dec 12, 2019

Motivation for this change

This is a main part of the implementation of NixOS/rfcs#42 (see https://github.com/Infinisil/rfcs/blob/config-option/rfcs/0042-config-option.md#part-1-1).

Since that part of the RFC is fairly uncontroversial, I think we can merge this before the RFC is eventually accepted. This allows people to use such types in their NixOS options already.

Ping @Profpatsch @rycee @aanderse @kampka

Things done
  • Tested that these types and generator combinations indeed can be used in modules.
  • Write some tests
  • Wrote documentation on how to use them..

Here's a simple example module though:

{ lib, config, pkgs, ... }:
let
  format = pkgs.formats.json {};
in {
  options.settings = lib.mkOption {
    type = format.type;
    default = {};
  };

  config = {
    settings.foo = 10;
    settings.bar.baz = "Hello";
    environment.etc.test.source = format.generate "config.json" config.settings;
  };
}

@infinisil
Copy link
Member Author

infinisil commented Dec 12, 2019

Potentially/Eventually these formats might be implemented using lazyAttrsOf (or what becomes of it) from #70138, such that self-dependent assignments of them will be possible. Edit: This is now done in this PR :)

@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild 10.rebuild-linux: 0 This PR does not cause any packages to rebuild labels Dec 12, 2019
@aanderse
Copy link
Member

@infinisil awesome stuff!

lib/settings-formats.nix Outdated Show resolved Hide resolved
@infinisil
Copy link
Member Author

(This still needs a bit of docs probably and a history cleanup at least btw, before anybody merges)

@ofborg ofborg bot added 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: documentation labels Jan 9, 2020
@infinisil
Copy link
Member Author

I've now rebased this ontop of #70138 to implement types where values can depend on other values. E.g. this previously threw an infinite recursion error, but now it doesn't anymore:

{ lib, config, ... }:
let format = lib.formats.json;
in {
  options.settings = lib.mkOption {
    type = format.type;
    default = {};
  };

  config = {
    settings.foo = true;
    settings.bar = lib.mkIf config.settings.foo "Foo is true!";

    environment.etc.test.text = format.generate config.settings;
  };
}

Due to how the implementation works, it's however a bit more cumbersome if you aren't sure whether an attribute exists at all. This is how that can now be handled gracefully:

{
  # To make sure the `.name` attribute exists
  settings.name = format.empty;
  # To handle the case of `.name` being empty
  settings.greeting = "Hello, ${format.get "nobody" config.settings.name}!";
}

This will generate { greeting = "Hello, nobody!"; }

Alternatively if you don't mind name being set:

{
  settings.name = lib.mkDefault "nobody";
  settings.greeting = "Hello, ${config.settings.name}!";
}

Which will generate { name = "nobody"; greeting = "Hello, nobody!"; }

@infinisil infinisil changed the title Configuration file formats for JSON and INI Configuration file formats for JSON, INI and YAML Jan 9, 2020
@ryantm ryantm added 2.status: blocked by pr/issue Another PR or issue is preventing this from being completed and removed 2.status: blocked by pr/issue Another PR or issue is preventing this from being completed labels Jan 9, 2020
@ofborg ofborg bot removed 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: documentation labels Jan 10, 2020
samueldr added a commit to samueldr-wip/mobile-nixos-wip that referenced this pull request Jan 13, 2020
samueldr added a commit to samueldr-wip/mobile-nixos-wip that referenced this pull request Jan 13, 2020
samueldr added a commit to samueldr-wip/mobile-nixos-wip that referenced this pull request Jan 13, 2020
samueldr added a commit to samueldr-wip/mobile-nixos-wip that referenced this pull request Jan 14, 2020
@Profpatsch Profpatsch self-requested a review January 15, 2020 12:25

empty = { _nixos_empty_json_value =
throw ("You tried to access an option value of type json that is empty. "
+ "Use `lib.formats.json.get` on the value before using it.");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be a good idea to only have a single empty value for all

@infinisil
Copy link
Member Author

On IRC @adisbladis brought to my attention that to support more formats like TOML, we could use builtins.toJSON and convert that to toml with a build-time tool, meaning the generated strings won't be available to nix at eval time anymore and the generate functions have the signature generate :: Value -> Derivation. This doesn't seem like a problem: Nobody should need the raw Nix strings anyways, and this will speed up evaluation too.

@domenkozar
Copy link
Member

I'd review it if it was written with terminal length in mind, but currently it's not feasible via github ui.

Copy link
Member

@roberth roberth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be noted in the docs that some of these functions may write to the store, which is not safe when Nix is handling secrets. This is in contrast to builtins.toJSON etc, which operate entirely in memory.

lib/generators.nix Show resolved Hide resolved
# they depend on some packages. This notably is *not* for supporting package
# building, instead pkgs/build-support is the place for that.
{ lib, pkgs }: {
# setting format types and generators. These would fit in lib/types.nix, but
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# setting format types and generators. These would fit in lib/types.nix, but
# setting format types and generators. These do not fit in lib/types.nix, because

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -0,0 +1,11 @@
# pkgs-lib is for things that should be in lib, but can't beceause
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# pkgs-lib is for things that should be in lib, but can't beceause
# pkgs-lib is for functions that can't be in lib, because

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -75,6 +75,11 @@ let
inherit (self) runtimeShell;
};

pkgsLib = self: super: import ../pkgs-lib {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An overlay is unnecessary because the same could be achieved by putting formats in all-packages.nix.

Categorizing functions separately from packages is beyond the scope of this change. It can be discussed in a separate issue/pr, although I doubt its usefulness.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't have to be just functions though, can be arbitrary values, and other functions like fetchgit wouldn't fit in a pkgs-lib. I guess it won't be a problem to just put formats in all-packages.nix, but imo having a separation between "things needed to create packages" and "things that just happen to depend on pkgs" would make sense. Could still introduce that in the future though

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not using an overlay anymore now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of formats, fetchgit, dockerTools depend on Nixpkgs, take arguments, produce derivations, are maintained and released in the same way. What makes formats so architecturally different that it needs to be treated differently?

imo having a separation between "things needed to create packages" and "things that just happen to depend on pkgs" would make sense. Could still introduce that in the future though

If only one attribute is separate it doesn't make sense though. For it to make sense it must be applied to the whole of Nixpkgs and it must have practical benefits. The prior can be done in the future and I'm skeptical about the benefits, but would love to be proven wrong in that same future.

For now please follow the example of, say, dockerTools.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like my view of the PR was outdated (maybe caused by the current service disruption?) anyway, thanks for removing the overlay. Currently build-support has the role of pkgs-lib so you may want to move the files to build-support/formats. I don't really like that name either so I'm going to leave this change up to you to decide.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference between e.g. fetchgit and formats.json is that fetchgit is needed for building many of the pkgs.* derivations, whereas formats.json doesn't have any use for any pkgs.*. Instead formats are mostly just useful for NixOS modules (really, the module system in general, so it wouldn't fit in nixos/lib)

This is also why I don't think build-support would make sense, because formats.json doesn't support any package building.

@worldofpeace
Copy link
Contributor

I'd review it if it was written with terminal length in mind, but currently it's not feasible via github ui.

this is useful https://github.com/StylishThemes/GitHub-code-wrap

@rycee
Copy link
Member

rycee commented Jul 29, 2020

An alternative that doesn't need any installations is to use the "Split" diff option. For whatever reason, it seems to wrap lines while "Unified" doesn't.
github-split

@infinisil
Copy link
Member Author

It should be noted in the docs that some of these functions may write to the store, which is not safe when Nix is handling secrets. This is in contrast to builtins.toJSON etc, which operate entirely in memory.

For NixOS modules at least it doesn't make much of a difference, because the only way for a string (which toJSON returns) to influence a system build is by putting it in a derivation. With the interface this PR adds, this step is just done a little earlier. But yeah it can't hurt to point this out, I'll add a note

@infinisil
Copy link
Member Author

@roberth Looking good now?

@roberth roberth merged commit 150bf4f into NixOS:master Aug 2, 2020
@Ericson2314
Copy link
Member

I wonder if pkgs-lib should be pkgs/lib to match nixos/lib.

@infinisil
Copy link
Member Author

Awesome, thanks for merging!

@Ericson2314 I intentionally didn't name it lib because it could easily be confused with pkgs.lib.

@infinisil infinisil deleted the settings-formats branch August 2, 2020 15:11
@infinisil infinisil mentioned this pull request Aug 3, 2020
9 tasks
@midchildan midchildan mentioned this pull request Sep 1, 2020
9 tasks
vcunat referenced this pull request Oct 19, 2020
Foremost, the message was discarding double quotes on one side of the
diff, which was super-confusing to me, as I thought that the format
convertor broke that when in fact only whitespace was changed.

I thought I'd cat the files, but then... switching to `diff -u` seemed
self-sufficient.  It felt sufficiently non-controversial to push
directly, but certainly feel free to improve further.
@fzakaria
Copy link
Contributor

@infinisil you might want to update this documentation section as well https://nixos.org/manual/nixpkgs/unstable/#sec-generators
It seems out of date considering the new NixOS doc you added.

@fzakaria
Copy link
Contributor

Actually reading lib/generators.nix it looks like it should be consolidated with the new formats option introduced here.

@infinisil infinisil added the significant Novel ideas, large API changes, notable refactorings, issues with RFC potential, etc. label Apr 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: module system About "NixOS" module system internals 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: documentation 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild 10.rebuild-linux: 1-10 10.rebuild-linux: 1 significant Novel ideas, large API changes, notable refactorings, issues with RFC potential, etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.