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

Flake schemas #8892

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
9e731ea
Move NIX_ALLOW_EVAL check
edolstra Aug 17, 2023
26cfd6e
Remove dead code
edolstra Aug 17, 2023
af0f1e0
Initial flake schemas
edolstra Aug 30, 2023
4910c93
nix flake check: Always evaluate the "name" attribute of derivations
edolstra Aug 30, 2023
f2af081
Syntax highlighting
edolstra Aug 30, 2023
00a81a9
Update activity
edolstra Aug 31, 2023
2a87db5
Handle legacyPackages eval errors
edolstra Aug 31, 2023
f7ae12f
nix flake show: Fix --legacy
edolstra Aug 31, 2023
84867fa
Update flake.nix
cole-h Oct 20, 2023
d76e5fb
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Mar 11, 2024
8d2c6be
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jun 24, 2024
922d4b5
Add option --default-flake-schemas to override the default schemas flake
edolstra Jun 25, 2024
60b45f6
Include a copy of the flake-schemas flake
edolstra Jun 26, 2024
c55c9dd
flake.lock: Update
edolstra Jun 26, 2024
69b5a4a
Fix some tests
edolstra Jun 26, 2024
367bd59
Move schema-related stuff
edolstra Jun 26, 2024
f014ee3
Formatting
edolstra Jun 26, 2024
de07a98
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jun 26, 2024
17e4d91
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jul 1, 2024
2b872b9
flake.lock: Update
edolstra Jul 1, 2024
d72cd90
nix flake check: Improve error message for failed eval checks
edolstra Jul 1, 2024
28b8cce
nix flake check: Remove unnecessary check for the name attribute
edolstra Jul 1, 2024
e2cf40a
flake.lock: Update
edolstra Jul 1, 2024
e8318bd
Re-enable some tests
edolstra Jul 1, 2024
28a2641
nix flake check --json: Make AAAAAASomeThingsFailToEvaluate show up
edolstra Jul 1, 2024
e264678
flake.lock: Update
edolstra Jul 1, 2024
7420b5f
Fix evaluation
edolstra Jul 2, 2024
d506779
Use Markdown tables for config
lucperkins Jul 11, 2024
ac952d6
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jul 12, 2024
e1ed2d1
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jul 12, 2024
cf699c0
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jan 17, 2025
c925fc2
Fix apps check
edolstra Jan 17, 2025
6a7722a
Fix build
edolstra Jan 17, 2025
4a3a71f
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jan 21, 2025
5ba138e
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Jan 22, 2025
9a4502b
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Feb 10, 2025
f5a0caf
Merge remote-tracking branch 'origin/master' into flake-schemas
edolstra Feb 23, 2025
1fcb5d1
Fix test
edolstra Feb 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/manual/source/SUMMARY.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
- [Store Path Specification](protocols/store-path.md)
- [Nix Archive (NAR) Format](protocols/nix-archive.md)
- [Derivation "ATerm" file format](protocols/derivation-aterm.md)
- [Flake Schemas](protocols/flake-schemas.md)
- [C API](c-api.md)
- [Glossary](glossary.md)
- [Development](development/index.md)
Expand Down
64 changes: 64 additions & 0 deletions doc/manual/source/protocols/flake-schemas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Flake Schemas

Flake schemas are a mechanism to allow tools like `nix flake show` and `nix flake check` to enumerate and check the contents of a flake
in a generic way, without requiring built-in knowledge of specific flake output types like `packages` or `nixosConfigurations`.

A flake can define schemas for its outputs by defining a `schemas` output. `schemas` should be an attribute set with an attribute for
every output type that you want to be supported. If a flake does not have a `schemas` attribute, Nix uses a built-in set of schemas (namely https://github.com/DeterminateSystems/flake-schemas).

A schema is an attribute set with the following attributes:

| Attribute | Description | Default |
| :---------- | :---------------------------------------------------------------------------------------------- | :------ |
| `version` | Should be set to 1 | |
| `doc` | A string containing documentation about the flake output type in Markdown format. | |
| `allowIFD` | Whether the evaluation of the output attributes of this flake can read from derivation outputs. | `true` |
| `inventory` | A function that returns the contents of the flake output (described [below](#inventory)). | |

# Inventory

The `inventory` function returns a _node_ describing the contents of the flake output. A node is either a _leaf node_ or a _non-leaf node_. This allows nested flake output attributes to be described (e.g. `x86_64-linux.hello` inside a `packages` output).

Non-leaf nodes must have the following attribute:

| Attribute | Description |
| :--------- | :------------------------------------------------------------------------------------- |
| `children` | An attribute set of nodes. If this attribute is missing, the attribute is a leaf node. |

Leaf nodes can have the following attributes:

| Attribute | Description |
| :----------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `derivation` | The main derivation of this node, if any. It must evaluate for `nix flake check` and `nix flake show` to succeed. |
| `evalChecks` | An attribute set of Boolean values, used by `nix flake check`. Each attribute must evaluate to `true`. |
| `isFlakeCheck` | Whether `nix flake check` should build the `derivation` attribute of this node. |
| `shortDescription` | A one-sentence description of the node (such as the `meta.description` attribute in Nixpkgs). |
| `what` | A brief human-readable string describing the type of the node, e.g. `"package"` or `"development environment"`. This is used by tools like `nix flake show` to describe the contents of a flake. |

Both leaf and non-leaf nodes can have the following attributes:

| Attribute | Description |
| :----------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `forSystems` | A list of Nix system types (e.g. `["x86_64-linux"]`) supported by this node. This is used by tools to skip nodes that cannot be built on the user's system. Setting this on a non-leaf node allows all the children to be skipped, regardless of the `forSystems` attributes of the children. If this attribute is not set, the node is never skipped. |

# Example

Here is a schema that checks that every element of the `nixosConfigurations` flake output evaluates and builds correctly (meaning that it has a `config.system.build.toplevel` attribute that yields a buildable derivation).

```nix
outputs = {
schemas.nixosConfigurations = {
version = 1;
doc = ''
The `nixosConfigurations` flake output defines NixOS system configurations.
'';
inventory = output: {
children = builtins.mapAttrs (configName: machine:
{
what = "NixOS configuration";
derivation = machine.config.system.build.toplevel;
}) output;
};
};
};
```
5 changes: 0 additions & 5 deletions src/libcmd/installables.cc
Original file line number Diff line number Diff line change
Expand Up @@ -454,11 +454,6 @@ ref<eval_cache::EvalCache> openEvalCache(
: std::nullopt;
auto rootLoader = [&state, lockedFlake]()
{
/* For testing whether the evaluation cache is
complete. */
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
throw Error("not everything is cached, but evaluation is not allowed");

auto vFlake = state.allocValue();
flake::callFlake(state, *lockedFlake, *vFlake);

Expand Down
6 changes: 6 additions & 0 deletions src/libexpr/eval-cache.cc
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,12 @@ Value * EvalCache::getRootValue()
{
if (!value) {
debug("getting root value");

/* For testing whether the evaluation cache is
complete. */
if (getEnv("NIX_ALLOW_EVAL").value_or("1") == "0")
throw Error("not everything is cached, but evaluation is not allowed");

value = allocRootValue(rootLoader());
}
return *value;
Expand Down
7 changes: 7 additions & 0 deletions src/libexpr/eval-cache.hh
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ class EvalCache : public std::enable_shared_from_this<EvalCache>
friend struct CachedEvalError;

std::shared_ptr<AttrDb> db;

public:
EvalState & state;

private:
typedef std::function<Value *()> RootLoader;
RootLoader rootLoader;
RootValue value;
Expand Down Expand Up @@ -89,7 +93,10 @@ class AttrCursor : public std::enable_shared_from_this<AttrCursor>
friend class EvalCache;
friend struct CachedEvalError;

public:
ref<EvalCache> root;

private:
typedef std::optional<std::pair<std::shared_ptr<AttrCursor>, Symbol>> Parent;
Parent parent;
RootValue _value;
Expand Down
36 changes: 28 additions & 8 deletions src/libflake/flake/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ static std::pair<std::map<FlakeId, FlakeInput>, fetchers::Attrs> parseFlakeInput
return {inputs, selfAttrs};
}

static Flake readFlake(
Flake readFlake(
EvalState & state,
const FlakeRef & originalRef,
const FlakeRef & resolvedRef,
Expand Down Expand Up @@ -441,22 +441,18 @@ static LockFile readLockFile(
: LockFile();
}

/* Compute an in-memory lock file for the specified top-level flake,
and optionally write it to file, if the flake is writable. */
LockedFlake lockFlake(
const Settings & settings,
EvalState & state,
const FlakeRef & topRef,
const LockFlags & lockFlags)
const LockFlags & lockFlags,
Flake flake,
FlakeCache & flakeCache)
{
experimentalFeatureSettings.require(Xp::Flakes);

FlakeCache flakeCache;

auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);

auto flake = getFlake(state, topRef, useRegistries, flakeCache, {});

if (lockFlags.applyNixConfig) {
flake.config.apply(settings);
state.store->setOptions();
Expand Down Expand Up @@ -921,6 +917,30 @@ LockedFlake lockFlake(
}
}

LockedFlake lockFlake(
const Settings & settings,
EvalState & state,
const FlakeRef & topRef,
const LockFlags & lockFlags)
{
FlakeCache flakeCache;

auto useRegistries = lockFlags.useRegistries.value_or(settings.useRegistries);

return lockFlake(settings, state, topRef, lockFlags, getFlake(state, topRef, useRegistries, flakeCache, {}), flakeCache);
}

LockedFlake lockFlake(
const Settings & settings,
EvalState & state,
const FlakeRef & topRef,
const LockFlags & lockFlags,
Flake flake)
{
FlakeCache flakeCache;
return lockFlake(settings, state, topRef, lockFlags, std::move(flake), flakeCache);
}

void callFlake(EvalState & state,
const LockedFlake & lockedFlake,
Value & vRes)
Expand Down
23 changes: 23 additions & 0 deletions src/libflake/flake/flake.hh
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,35 @@ struct LockFlags
std::set<InputAttrPath> inputUpdates;
};

/**
* Return a `Flake` object representing the flake read from the
* `flake.nix` file in `rootDir`.
*/
Flake readFlake(
EvalState & state,
const FlakeRef & originalRef,
const FlakeRef & resolvedRef,
const FlakeRef & lockedRef,
const SourcePath & rootDir,
const InputAttrPath & lockRootPath);

/**
* Compute an in-memory lock file for the specified top-level flake,
* and optionally write it to file, if the flake is writable.
*/
LockedFlake lockFlake(
const Settings & settings,
EvalState & state,
const FlakeRef & flakeRef,
const LockFlags & lockFlags);

LockedFlake lockFlake(
const Settings & settings,
EvalState & state,
const FlakeRef & topRef,
const LockFlags & lockFlags,
Flake flake);

void callFlake(
EvalState & state,
const LockedFlake & lockedFlake,
Expand Down
Loading
Loading