nix icon indicating copy to clipboard operation
nix copied to clipboard

Flake schemas

Open edolstra opened this issue 2 years ago • 15 comments

Motivation

A big problem with current flakes is that commands like nix flake check and nix flake show have built-in support for a limited set of flake output types. For instance, they know about nixosConfigurations but not homeConfigurations.

This PR moves support for flake output types out of C++ and into Nix functions supplied by a new schemas flake output. This allows users to define their own flake output types, and have them show up in nix flake show and checked by nix flake check. As a result, there are no more flake output types that are more "blessed" than others.

There is no central registry of schemas. Instead a flake should add an appropriate schemas flake output for the outputs it wants to have checked. Typically this will include schemas from other flakes. In particular, I've made a flake-schemas flake with schemas for the flake output types that were previously built into Nix.

The format of schemas is documented in doc/manual/src/protocols/flake-schemas.md.

To do:

  • Currently, to support flakes that don't have a schemas attribute, Nix will fetch the flake-schemas flake at runtime if needed. A copy of this flake should be included at compile-time to avoid a fetch from the network.
  • The evalChecks schema attribute need to be refined to allow checkers to return warnings/errors in a more sensible way than an assertion failure.

Context

Fixes #3487, #6453, #6454.

Checklist for maintainers

Maintainers: tick if completed or explain if not relevant

  • [ ] agreed on idea
  • [ ] agreed on implementation strategy
  • [ ] tests, as appropriate
    • functional tests - tests/**.sh
    • unit tests - src/*/tests
    • integration tests - tests/nixos/*
  • [ ] documentation in the manual
  • [ ] documentation in the internal API docs
  • [ ] code and comments are self-explanatory
  • [ ] commit message explains why the change was made
  • [ ] new feature or incompatible change: updated release notes

Priorities

Add :+1: to pull requests you find important.

edolstra avatar Aug 31 '23 15:08 edolstra

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/flake-schemas-making-flake-outputs-extensible/32421/1

nixos-discourse avatar Aug 31 '23 15:08 nixos-discourse

If I understand the description correctly, this would also solve #6453 and #6454.

NobbZ avatar Aug 31 '23 17:08 NobbZ

Considering that the schema returns the children, this defines a new "derivation" centric, tree shaped view of the flake that is determined by the flake itself, as the flake defines the schema. Shouldn't other commands go through the schema then? Otherwise, we are still making assumptions about the schema contents.

Should the evaluated schema be exposed to flakes that consume the flake as an input?

Do we need the interface between the CLI and the flake need to be schema-like? Why don't we expose the desired functionality directly by defining one or two new outputs that don't rely on the concept of a schema? For example:

outputs = inputs@{ self, ... }: {
  evalChecks = {
    myChecks = ...;
    fooChecks = inputs.foo.lib.evalChecks self;
  };

  treeView = {
    hasPackage = 1;
    children = {
      defaultPackage = { # picked this simpler one for brevity. Users won't write this by hand, just as they generally don't write schemas
        children = {
          isDict = true;
          role = "system";
          children = {
            x86_64-linux = {
              isPackage = true;
              value = ...;
            };
          };
        };        
      };
    };
  };
}

Outputs like this seem conceptually simpler to me, and they seem easier to use from within the language.

They will be similarly easy to use when the outputs are defined through a library. Considering that the flake-schemas repo is already a library, this condition holds.

If we do decide that we need a concept of schema, is the metaschema as simple as it can be, or can it leverage more of the language?

roberth avatar Sep 01 '23 10:09 roberth

Considering that the schema returns the children, this defines a new "derivation" centric, tree shaped view of the flake that is determined by the flake itself, as the flake defines the schema. Shouldn't other commands go through the schema then? Otherwise, we are still making assumptions about the schema contents.

I didn't think this is intended to be a new view of the flake at all. And it is absolutely not intended to be derivation centric. The real concept being embodied is conceptual outputs that user care about. That is to say: packages, modules, checks, configurations, ci jobs, overlays, deployment definitions, etc. These big picture concept that people would generally hope would show up in nix flake show output. Of course, some of them may be smaller picture items too, like a library flake's conceptual outputs are the library functions it exports. We don't have a great name for these conceptual outputs, so I will use "artifact" for them.

The problem, of course, is that there is no simple way to enumerate these artifacts. It is not like these are all top level output attributes. Indeed, they pretty deliberately are mostly not top level attributes, since having some structure makes it possible for tooling to distinguish outputs in useful ways. Most standard output attributes are either attribute sets of these, or attribute sets of attribute sets of these. In practice these artifacts are almost always just nix values that can be reached as an attribute path from output attribute set.

Given that in practice in order to use any of these artifacts you need to know how to reference them from an imported flake, it would really make no sense to have the schema define a different tree structure then the output's attributes already have, albeit truncated.

Do we need the interface between the CLI and the flake need to be schema-like? Why don't we expose the desired functionality directly by defining one or two new outputs that don't rely on the concept of a schema? For example: ... Outputs like this seem conceptually simpler to me, and they seem easier to use from within the language.

This is a possibility. From a flake authoring perspective, importing evalchecks sounds like a fairly low level thing to be working with, while importing a schema from a flake and assigning that sounds more high level. Similarly from an authoring perspective to add a new schema would be schemas = flake-schemas.schemas // other-schemas.schemas; but with this approach, I'd need to not only do something similar for eval checks, but also need to compose the standard attributes treeview generating function with the one for the community defined output type and assign that. Doable, but just more boilerplate, and there is already more boilerplate stuff with flakes than many people like.

KevinCathcart avatar Sep 01 '23 21:09 KevinCathcart

I didn't think this is intended to be a new view of the flake at all.

I agree about the intent, but it's the details of the design that matter. The children attribute does seem to form a view of the flake, which could be expressed more simply as another output attribute.

It is worth repeating that Nix is highly constrained when it comes to making changes, because we've successfully provided compatibility with old expressions, and would like to keep that unique property. While this doesn't apply during the experimental phase of a feature, we will eventually have to judge it by the usual standard before we can call it stable. So a feature like this could further delay the stabilization of flakes if it isn't as simple as it could be.

Another important observation is that the less we do in C++ and the more we do in Nix expressions, we get better opportunities to iterate on our solutions, because users can pin or lock such expressions and adapt to any changes at their own pace, or not at all if we're talking about support for old expressions.

Hence I'd take a PR that reifies CLI behavior as an expression-based interface over a PR that introduces new concepts any day - as long as it's clear what the CLI should do, and in case of enumerating and checking that seems rather clear. Extensions to that behavior can be represented by following the dict vs record pattern we've described in the JSON guideline (applied to attrsets instead).

roberth avatar Sep 03 '23 14:09 roberth

any update on this? i don't think i saw any mention of this on any of the team meetings i glanced through recently

aanderse avatar Feb 24 '24 15:02 aanderse

For what it’s worth we’re using them a lot at determinate systems to good effect.

On Sat, Feb 24, 2024, at 10:07 AM, Aaron Andersen wrote:

any update on this? i don't think i saw any mention of this on any of the team meetings i glanced through recently

— Reply to this email directly, view it on GitHub https://github.com/NixOS/nix/pull/8892#issuecomment-1962396573, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAASXLEZJUVWTZZFGZSEOX3YVH63XAVCNFSM6AAAAAA4GIL65OVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSNRSGM4TMNJXGM. You are receiving this because you commented.Message ID: @.***>

grahamc avatar Feb 24 '24 18:02 grahamc

For what it’s worth we’re using them a lot at determinate systems to good effect.

I’ve also had success with them. Here’s a fairly rich schema I use in most of my projects: https://github.com/sellout/project-manager/blob/495cb847eb2cbf6b7981576c3b1610cc077c4768/nix/schemas.nix#L6-L80

sellout avatar Feb 26 '24 21:02 sellout

sounds lovely

i wonder what it would take to move forward with this

aanderse avatar Feb 26 '24 21:02 aanderse

For what it’s worth we’re using them a lot at determinate systems to good effect.

That's great to hear. What is the status of y'all moving this forward?

adamcstephens avatar Mar 29 '24 18:03 adamcstephens

Forgive my ignorance, but how are people using schemas without the nix command supporting them?

Or are people running a build of this PR branch instead of upstream?

MattSturgeon avatar Mar 29 '24 18:03 MattSturgeon

Or are people running a build of this PR branch instead of upstream?

I, for one, am doing that: https://github.com/chaotic-cx/nyx/blob/7cc6282416833f2dea96f97f516aceadbf406404/.github/workflows/flakehub-publish-rolling.yml#L26

PedroHLC avatar Mar 29 '24 18:03 PedroHLC

I really wish I could use that feature too.

While I have great respect for the advancements and discussions shared here and on the Determinate System blog, I noticed that Nix does not currently support this feature. Therefore, I fail to see why that feature has been promoted yet. Could someone clarify the decision-making process or the timeline for Nix's support of this functionality? I'm eager to understand more about it and how eventually everyone could finally use it without running a particular version of Nix.

drupol avatar Apr 07 '24 10:04 drupol

I realize that my previous comment didn’t convey what I intended.

I mentioned that I use schemas (and gave an example of one) to show to the NixOS/nix maintainers that this PR is helpful, but I feel like it came across to users like “don’t worry about the PR, just use schemas like I do.”

To address @MattSturgeon’s comment, I opened DeterminateSystems/flake-schemas#14 a few months ago, recommending that there be an easy way to use schemas without relying on this PR being merged. That issue also shows how I have it working for my projects.

sellout avatar Apr 11 '24 15:04 sellout

i wonder what it would take to move forward with this

The following points have not yet been addressed:

  • [ ] Shouldn't other commands go through the schema? E.g. nix build does not use the schema, and specifying an alternate schema doesn't make it behave differently. These kinds of shortcuts create room for discrepancies (bugs) and don't allow schemas to improve the nix build behavior. The latter is more of a lost opportunity than an immediate issue.
  • [ ] Continuing the previous point, we have an opportunity to clarify the meanings of the various <system> attributes - specifically whether they related to the build or host system (or target - probably rare and to be avoided)
  • [ ] Explore what can be un-hardcoded from Nix. Perhaps logic from src/nix/call-flake-schemas.nix could be moved into the schemas repo?

Shouldn't other commands go through the schema then?

I didn't get a response, but let's assume we want schemas to be discoverable without having any values for them.

  • [ ] Give inventory a static counterpart that declares something about the contents without having them, as schemas do. This is important for discoverability.

(these seem to be all my own points; the other ones seem minor, not so much relating to the design, but do need to be addressed)

roberth avatar Jun 10 '24 12:06 roberth

Shouldn't other commands go through the schema?

No, the schemas reflect what those commands do (e.g. the nixosConfigurations schema reflects that nixos-rebuild requires a config.system.build.toplevel attribute that evaluates to a derivation), they don't determine what those commands do.

In the future, some commands could use schemas, e.g. nix build .#nixosConfigurations.my-machine could use the nixosConfigurations schema to figure out that it needs to expand my-machine to my-machine.config.system.build.toplevel. That would be pretty nice.

Continuing the previous point, we have an opportunity to clarify the meanings of the various attributes - specifically whether they related to the build or host system (or target - probably rare and to be avoided)

Assigning a meaning to system attributes is up to the schemas. E.g. if some tool has a more refined notion of system types, the corresponding schema could check those types.

Explore what can be un-hardcoded from Nix. Perhaps logic from src/nix/call-flake-schemas.nix could be moved into the schemas repo?

call-flake-schemas.nix is a fairly minimal bit of helper code to evaluate schemas. It's completely internal to nix::flake_schemas::call(). It could be done in C++, but that would be more code.

Give inventory a static counterpart that declares something about the contents without having them, as schemas do. This is important for discoverability.

Schemas already have some static info (like a doc attribute). However, statically declaring what the contents should look like sounds like a type system, which is a much bigger project. The flake schema format is extensible so it could support that though.

edolstra avatar Jul 09 '24 12:07 edolstra

Discussed in the meeting today. We didn't have a lot of time because we were towards the end of it, so I'll just summarize the thoughts I shared.

Naming

  • "Schemas" has a fairly well-defined meaning in software that doesn't quite align with this. Perhaps we could change the name. We've considered "flake protocols" (similarly vague and not a massive improvement), "flake interfaces" (might interfere with potential terminology for Nix types we might have at some point) and "flake descriptors" (worth considering?)
  • Schemas may evolve into types when that happens. I think it would be good to change the name at that point, because that's a significant change, and they could coexist (certainly during a "migration" phase)

Is it possible to export a flake schema into a serializable form? (Or documentation)

  • It could evolve to support that?
  • It'd be beneficial to have an expression-level indirection that mediates between users and a minimal Nix<->expressions interface. This indirection would feel similar to the module system, but users would write even fewer lambdas. It could live in the flake-schemas repo.
    • Similar to a mkDerivation or a libc (in Nix at least where you can have multiple libcs). This layer provides ease of use to take the pressure of the kernel (ie Nix's C++ code) to incorporate conveniences that need to evolve over time.
    • Also allows the extension of the Nix<->expressions interface. For instance support for outputting a serializable schema or documentation would be a matter of Nix calling .schema or .docs instead of .check.
      • Ie docs is an attribute that the flake schemas repo synthesizes from the individual doc attributes it processes.
    • Would be usable without Nix assigning semantics to those attributes on the schema attrsets
  • Users could still talk to the low level Nix<->expressions interface directly, but they wouldn't get the benefits from the flake schemas repo indirection. I don't think that's a problem; just a missed opportunity for them.

roberth avatar Jul 15 '24 15:07 roberth