Skip to content

Flake schemas #8892

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

Open
wants to merge 53 commits into
base: master
Choose a base branch
from
Open

Flake schemas #8892

wants to merge 53 commits into from

Conversation

edolstra
Copy link
Member

@edolstra edolstra commented Aug 31, 2023

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:

  • 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 👍 to pull requests you find important.

Flake schemas allow us to get rid of output-specific code in commands
like `nix flake [show|check|search]`. This is done by allowing flakes
to have a `schemas` attribute` that defines how to enumerate the
contents of the output (including documentation), and how to check
it.

In the future, this can be extended to support configurability of
flakes in a discoverable way.
@github-actions github-actions bot added documentation new-cli Relating to the "nix" command labels Aug 31, 2023
@nixos-discourse
Copy link

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

@NobbZ
Copy link
Contributor

NobbZ commented Aug 31, 2023

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

flake.nix Outdated
@@ -5,8 +5,9 @@
inputs.nixpkgs-regression.url = "github:NixOS/nixpkgs/215d4d0fd80ca5163643b03a33fde804a29cc1e2";
inputs.lowdown-src = { url = "github:kristapsdz/lowdown"; flake = false; };
inputs.flake-compat = { url = "github:edolstra/flake-compat"; flake = false; };
inputs.flake-schemas.url = github:DeterminateSystems/flake-schemas;
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be owned by the NixOS organization?

Copy link
Member Author

Choose a reason for hiding this comment

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

Definitely, if this PR gets accepted.

This comment was marked as duplicate.

# FIXME: a pre-cached copy of the flake-schemas needs to be built in to the nix binary
defaultSchemas = (builtins.getFlake "github:DeterminateSystems/flake-schemas/3b4d5fef938f698c8737515532a1be53bf6355f2").schemas;

schemaOverrides = {}; # FIXME
Copy link
Member

Choose a reason for hiding this comment

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

What does FIXME mean here for schemaOverrides?

Copy link
Member Author

Choose a reason for hiding this comment

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

This was intended to support adding/overriding (some) schemas from the command line, but I didn't implement that yet.

@roberth
Copy link
Member

roberth commented Sep 1, 2023

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?

@KevinCathcart
Copy link

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.

@roberth
Copy link
Member

roberth commented Sep 3, 2023

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).


Leaf nodes can have the following attributes:

* `derivation`: The main derivation of this node, if any. It must evaluate for `nix flake check` and `nix flake show` to succeed.
Copy link

Choose a reason for hiding this comment

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

Some outputs are not technically a derivation, but rather a path, yet they are paths referring to the output of a derivation. apps.*.program for example. How can they be represented in this schema?

Ref: DeterminateSystems/flake-schemas#5

Co-authored-by: Greg Pfeil <greg@technomadic.org>
jlesquembre added a commit to jlesquembre/clj-nix that referenced this pull request Dec 4, 2023
That was causing issues, and now we have a better solution:
NixOS/nix#8892
@aanderse
Copy link
Member

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

@grahamc
Copy link
Member

grahamc commented Feb 24, 2024 via email

@sellout
Copy link
Contributor

sellout commented Feb 26, 2024

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

@aanderse
Copy link
Member

sounds lovely

i wonder what it would take to move forward with this

@github-actions github-actions bot added the with-tests Issues related to testing. PRs with tests have some priority label Mar 11, 2024
@adamcstephens
Copy link

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?

@MattSturgeon
Copy link

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?

This reverts commit 1fcb5d1.
Also, we now use the `derivation` attribute returned by the schema to
build a derivation. This means you can now do

  nix build .#nixosConfigurations.machine

instead of

  nix build .#nixosConfigurations.machine.config.system.build.toplevel

since the schema for `nixosConfigurations` returns
`config.system.build.toplevel` as its `derivation` attribute.
Roles allow schemas to declare what commands operate on them. For
instance, the `nix-build` role means that the `nix build` command
should try to build the corresponding flake output.

This means that Nix commands no longer need a hard-coded list of
attrpath prefixes and default attrpaths, and it makes them
extensible. For instance, we could specify that `nixosConfigurations`
should have the `nix-build` role, e.g. `nix build` would try to build
`nixosConfigurations.default`, and `nix build machine` would try to
build `nixosConfigurations.machine`.
@github-actions github-actions bot added the repl The Read Eval Print Loop, "nix repl" command and debugger label Mar 1, 2025
edolstra added a commit to DeterminateSystems/nix-src that referenced this pull request Mar 5, 2025
This applies upstream NixOS#8892.
@nixos-discourse
Copy link

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

https://discourse.nixos.org/t/determinate-nix-3-0/61202/66

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation new-cli Relating to the "nix" command repl The Read Eval Print Loop, "nix repl" command and debugger with-tests Issues related to testing. PRs with tests have some priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Custom flake checkers (as plugins?)