-
-
Notifications
You must be signed in to change notification settings - Fork 160
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
[RFC 0049] Flakes #49
Changes from 28 commits
13b4857
734d920
3b16530
c83185c
cc5d0a2
f2cf8a8
1441693
9334af1
57f139b
368d932
f2c6f44
4bcf196
15f1c51
b2d6b9c
0d94186
859d44a
efa667f
79e33d8
970c31c
d63b4cc
d053724
e04ad16
2bdbe70
e09a75e
fe0386d
7e9497c
c24dca8
3058b36
dd6c657
abb2799
b60f889
95a9ff7
969b079
3c734b8
07767e6
421a9cd
6fa3b7b
bfa627e
8c36f76
f053ecf
0dbf231
ca2845b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,384 @@ | ||||||
--- | ||||||
feature: flakes | ||||||
start-date: 2019-07-09 | ||||||
author: Eelco Dolstra | ||||||
co-authors: TBD | ||||||
shepherd-team: Domen Kožar, Alyssa Ross, Shea Levy, John Ericson | ||||||
shepherd-leader: Domen Kožar | ||||||
related-issues: (will contain links to implementation PRs) | ||||||
--- | ||||||
|
||||||
# Summary | ||||||
[summary]: #summary | ||||||
|
||||||
This RFC proposes a mechanism to package Nix expressions into | ||||||
composable entities called "flakes". Flakes allow hermetic, | ||||||
reproducible evaluation of multi-repository Nix projects; impose a | ||||||
discoverable, standard structure on Nix projects; and replace previous | ||||||
mechanisms such as Nix channels and the Nix search path. | ||||||
|
||||||
# Motivation | ||||||
[motivation]: #motivation | ||||||
|
||||||
Flakes are motivated by a number of serious shortcomings in Nix: | ||||||
|
||||||
* While Nix pioneered reproducible builds, sadly, Nix expressions are | ||||||
not nearly as reproducible as Nix builds. Nix expressions can access | ||||||
arbitrary files (such as `~/.config/nixpkgs/config.nix`), | ||||||
environment variables, and Git repositories. This means for instance | ||||||
that it is hard to ensure reproducible evaluation of NixOS or NixOps | ||||||
configurations. | ||||||
|
||||||
* Nix projects lack discoverability and a standard structure. For | ||||||
example, it's just convention that a repository has a `release.nix` | ||||||
for Hydra jobs and a `default.nix` for packages. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, |
||||||
|
||||||
* There is no standard way to compose Nix projects. Typical ways are | ||||||
to rely on the Nix search path (e.g. `import <nixpkgs>`) or to use | ||||||
`fetchGit` or `fetchTarball`. The former has poor reproducibility, | ||||||
while the latter is bad UX because of the need to manually update | ||||||
Git hashes to update dependencies. | ||||||
|
||||||
* `nix-channel` needs a replacement: channels are hard to create, | ||||||
users cannot easily pin specific versions of channels, channels | ||||||
interact in *ad hoc* ways with the Nix search path, and so on. | ||||||
|
||||||
The flakes mechanism seeks to address all these problems. This RFC, | ||||||
however, only describes the format and semantics of flakes; it doesn't | ||||||
describe changes to the `nix` command to support flakes. | ||||||
|
||||||
# Detailed design | ||||||
[design]: #detailed-design | ||||||
|
||||||
## Flakes | ||||||
|
||||||
A flake is a Git repository that contains a file named `flake.nix` in | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Defining flakes explicitly as git repos seems like a useful stepping-stone toward a delightful future but I sense that it may even now be too restrictive for what I think even early-majority flake adopters will want, let alone what the long tail of users will want. As a result, would it be possible to sketch out at least a little bit, here or elsewhere, where the current design is driven to depend exclusively on git for short-term convenience, expediency, or concreteness vs more long-term reasons so that we can think together about how the needs or aspirations those reasons represent might be addressed in non-git scenarios e.g., involving different network, security, compatibility, usability, or scale considerations? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To add a couple of high-level use cases motivating the question beyond more abstract design value judgments about configurability and coupling:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is nothing about flakes that makes them inherently tied to Git or GitHub. It's just that those are the only two mechanisms implemented at the moment. Mercurial and tarball support would be straightforward to add. |
||||||
the root directory. (In the future, there might be other types of | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Supporting tarballs seems like it's pretty necessary to me, especially since there's no pre-existing requirement to put nix files in a git repository (instead of e.g. mercurial, etc etc). On that point, it seems odd to merge in a possible RFC without even writing down the possibility of something non-git-based; especially since (to me) it feels like the only reason these are not in the spec is because it isn't yet implemented in nix. |
||||||
flakes, such as Mercurial repositories or tarballs.) `flake.nix` | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
specifies some metadata about the flake such as dependencies (called | ||||||
*inputs*), as well as its *outputs* (the Nix values such as packages | ||||||
or NixOS modules provided by the flake). | ||||||
|
||||||
As an example, below is the `flake.nix` of | ||||||
[`dwarffs`](https://github.com/edolstra/dwarffs) (a FUSE filesystem | ||||||
for automatically fetching DWARF debug symbols by ELF build ID). It | ||||||
depends on the Nixpkgs flake and provides a package (i.e. an | ||||||
installable derivation) and a NixOS module. | ||||||
|
||||||
``` | ||||||
{ | ||||||
edition = 201911; | ||||||
|
||||||
description = "A filesystem that fetches DWARF debug info from the Internet on demand"; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we have an explicit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't having a name for talking of a flake in general (and not in terms of a specific URL) useful on its own? For example, when people inevitably end up fetching the flake sources into the store… There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe, but we can add such an attribute when there is a use case. Without that we can only speculate about what the semantics should be. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, I guess we could continue once the «download-all-dependencies» use case is addressed fully. |
||||||
|
||||||
outputs = { self, nixpkgs }: rec { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the set be recursive? Just read yesterday about how recursive overlays should be avoided and the proper way is to use the self fixpoint. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it doesn't have to be recursive, you can also use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the advice about overlays, specifically about a "recursive attrset", doesn't apply? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, because this is not an overlay and there is no override mechanism at the moment (so the |
||||||
packages.dwarffs = | ||||||
with nixpkgs.packages; | ||||||
with nixpkgs.builders; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just tried this example and it failed with:
After that I ran into this issue:
I ended up with the following flake file: {
description = "A filesystem that fetches DWARF debug info from the Internet on demand";
outputs = { self, nixpkgs }: rec {
packages.x86_64-linux.dwarffs =
with nixpkgs.packages.x86_64-linux;
# with nixpkgs.builders;
with nixpkgs.lib;
stdenv.mkDerivation {
name = "dwarffs-0.1.${substring 0 8 self.lastModifiedDate}";
buildInputs = [ fuse nix nlohmann_json boost ];
NIX_CFLAGS_COMPILE = "-I ${nix.dev}/include/nix -include ${nix.dev}/include/nix/config.h -D_FILE_OFFSET_BITS=64";
src = self;
installPhase =
''
mkdir -p $out/bin $out/lib/systemd/system
cp dwarffs $out/bin/
ln -s dwarffs $out/bin/mount.fuse.dwarffs
cp ${./run-dwarffs.mount} $out/lib/systemd/system/run-dwarffs.mount
cp ${./run-dwarffs.automount} $out/lib/systemd/system/run-dwarffs.automount
'';
};
# nixosModules.dwarffs = ...;
defaultPackage.x86_64-linux = packages.x86_64-linux.dwarffs;
checks.build = packages.x86_64-linux.dwarffs;
};
} Steps to reproduce:
Maybe it is worth exchanging the example with something simpler. How about a variant of the example(s) presented in the blog post? |
||||||
with nixpkgs.lib; | ||||||
|
||||||
stdenv.mkDerivation { | ||||||
name = "dwarffs-0.1.${substring 0 8 self.lastModified}"; | ||||||
|
||||||
buildInputs = [ fuse nix nlohmann_json boost ]; | ||||||
|
||||||
NIX_CFLAGS_COMPILE = "-I ${nix.dev}/include/nix -include ${nix.dev}/include/nix/config.h -D_FILE_OFFSET_BITS=64"; | ||||||
|
||||||
src = self; | ||||||
|
||||||
installPhase = | ||||||
'' | ||||||
mkdir -p $out/bin $out/lib/systemd/system | ||||||
|
||||||
cp dwarffs $out/bin/ | ||||||
ln -s dwarffs $out/bin/mount.fuse.dwarffs | ||||||
|
||||||
cp ${./run-dwarffs.mount} $out/lib/systemd/system/run-dwarffs.mount | ||||||
cp ${./run-dwarffs.automount} $out/lib/systemd/system/run-dwarffs.automount | ||||||
''; | ||||||
}; | ||||||
|
||||||
nixosModules.dwarffs = ...; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can I nest modules here? i.e. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, the current flake outputs (except There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed this as well and wonder how we are going to handle sub package sets then. Maybe that's something for later after this RFC is accepted. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We definitely need |
||||||
|
||||||
defaultPackage = packages.dwarffs; | ||||||
|
||||||
checks.build = packages.dwarffs; | ||||||
}; | ||||||
} | ||||||
``` | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
A flake has the following attributes: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are those the only allowed values? Is a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, any other attribute is an error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps we should add that to the document. It currently leaves space for interpretation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @edolstra would it then make sense to have a top-level attribute like |
||||||
|
||||||
* `edition`: A number that specifies the version of the flake | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (nitpick) while There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is inspired by https://doc.rust-lang.org/nightly/edition-guide/editions/index.html There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it has a very different meaning. In rust edition is opt-in: According to text below it seems Nix will force the user to upgrade the version, so it's a different meaning. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, the intent is definitely that Nix would support previous editions (though maybe not forever). You only get an error if you use a flake that uses an edition more recent than what your version of Nix supports. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be optional for the first edition, since it's just line noise if we end up never breaking compatibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @edolstra I misunderstood the RFC then. Proposal to change:
to
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know Rust has had a lot of popularity with adding an I feel that declaring up front that Don't get me wrong: editions can be a great tool to fix mistakes of the past in a way that doesn't break everything that depends on them. But it's a commitment and also a maintenance burden. And without a clear roadmap of what should be fixed and why, just willy nilly adding an edition flag is, in my opinion, very dangerous! |
||||||
syntax/semantics to be used. This allows the interpretation of | ||||||
flakes to change in the future. It also enables some evolution of | ||||||
the Nix language; for example, the Nix files in the flake could be | ||||||
parsed using a syntax determined by the edition. The only currently | ||||||
allowed value is `201911`. Nix rejects flakes with an unsupported | ||||||
edition. | ||||||
|
||||||
* `description`: A short description of the flake. | ||||||
|
||||||
* `inputs`: An attrset specifying the dependencies of the flake | ||||||
(described below). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it permissible to have some of the input attributes be computed based on the other inputs? |
||||||
|
||||||
* `outputs`: A function that, given an attribute set containing the | ||||||
outputs of each of the input flakes keyed by their identifier, | ||||||
yields the Nix values provided by this flake. Thus, in the example | ||||||
above, `inputs.nixpkgs` contains the result of the call to the | ||||||
`outputs` function of the `nixpkgs` flake, and | ||||||
`inputs.nixpkgs.packages.fuse` refers to the `packages.fuse` output | ||||||
attribute of `nixpkgs`. | ||||||
|
||||||
In addition to the outputs of each input, each input in `inputs` | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some of this metatdata seems to be quite input-type dependent (e.g. |
||||||
also contains some metadata about the inputs. These are: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if a flake provides an output with the same name as one of the metadata attributes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a more verbose way to refer to outputs of an input, namely |
||||||
|
||||||
* `outPath`: The path in the Nix store of the flake's source | ||||||
tree. This means that you could import Nixpkgs in a more | ||||||
legacy-ish way by writing | ||||||
|
||||||
with import inputs.nixpkgs { system = "x86_64-linux"; }; | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
since `nixpkgs` still contains a `/default.nix`. In this case we | ||||||
bypass its outputs entirely and only use the flake mechanism to | ||||||
get its source tree. | ||||||
|
||||||
* `rev`: The commit hash of the flake's Git repository. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpicking: how about Mercurial changeset IDs, etc? :p |
||||||
|
||||||
* `revCount`: The number of ancestors of the revision `rev`. This is | ||||||
not available for `github:...` repositories (see below), since | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
they're fetched as tarballs rather than as Git repositories. | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
* `lastModified`: The commit time of the revision `rev`, in the | ||||||
format `%Y%m%d%H%M%S` (e.g. `20181231100934`). Unlike `revCount`, | ||||||
this is available for both Git and GitHub repositories, so it's | ||||||
useful for generating (hopefully) monotonically increasing version | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
strings. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lost here as to what and why is lastModifiedDate. Is it an alternative for rev? Or is it an alternative for revCount (which then doesn't make sense for tarball/github fetcher)? Would a option tag serve the purpose? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The "hopefully" refers to the fact that you can always fake timestamps with Git, but that's a case of "just don't do that" :-) |
||||||
|
||||||
The value returned by the `outputs` function must be an attribute | ||||||
set. The attributes can have arbitrary values; however, some tools | ||||||
may require specific attributes to have a specific value (e.g. the | ||||||
`nix` command may expect the value of `packages` to be an attribute | ||||||
set of derivations). | ||||||
|
||||||
## Flake inputs | ||||||
|
||||||
The attribute `inputs` specifies the dependencies of a flake. These | ||||||
specify the location of the dependency, or a symbolic flake identifier | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will it be possible to force-override an input location (with the user being responsible for validity of such subsitution)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe mention it somewhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, I removed all user interface aspects from the RFC :-) But it's something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I underestimated the completeness of removal of UI mentions. I thought it is about «how something is done», but some of the «what should be possible to do» is still specified. |
||||||
that is looked up in a registry or in a command-line flag. For | ||||||
example, the following specifies a dependency on the Nixpkgs and Hydra | ||||||
domenkozar marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
repositories: | ||||||
|
||||||
# A GitHub repository. | ||||||
inputs.import-cargo = { | ||||||
type = "github"; | ||||||
owner = "edolstra"; | ||||||
repo = "import-cargo"; | ||||||
}; | ||||||
|
||||||
# An indirection through the flake registry. | ||||||
inputs.nixpkgs.id = "nixpkgs"; | ||||||
|
||||||
Each input is fetched, evaluated and passed to the `outputs` function | ||||||
as a set of attributes with the same name as the corresponding | ||||||
input. The special input named `self` refers to the outputs and source | ||||||
tree of *this* flake. Thus, a typical `outputs` function looks like | ||||||
this: | ||||||
|
||||||
outputs = { self, nixpkgs, import-cargo }: { | ||||||
... outputs ... | ||||||
}; | ||||||
|
||||||
It is also possible to omit inputs entirely and *only* list them as | ||||||
expected function arguments in `outputs`. Thus, | ||||||
|
||||||
outputs = { self, nixpkgs }: ...; | ||||||
|
||||||
without an `inputs.nixpkgs` attribute will simply look up `nixpkgs` in | ||||||
the flake registry. | ||||||
Comment on lines
+190
to
+196
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no specification for any sort of flake registry in this spec, what is this referring to? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/NixOS/flake-registry AFAIK it's like aliases for longer URLs (there's a section on it in the blog post above) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well yeah, but that's not part of the spec here, is it? I don't think it's a good idea to go ahead and specify "this implicitly talks to the flake registry" without specifying how the flake registry behaves, and how it will be ran. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True. Maybe it was dropped as part of this: https://github.com/NixOS/rfcs/pull/49/files#r354288001 |
||||||
|
||||||
Repositories that don't contain a `flake.nix` can also be used as | ||||||
inputs, by setting the input's `flake` attribute to `false`: | ||||||
|
||||||
inputs.grcov = { | ||||||
type = "github"; | ||||||
owner = "mozilla"; | ||||||
repo = "grcov"; | ||||||
flake = false; | ||||||
}; | ||||||
|
||||||
outputs = { self, nixpkgs, grcov }: { | ||||||
packages.grcov = stdenv.mkDerivation { | ||||||
src = grcov; | ||||||
... | ||||||
}; | ||||||
}; | ||||||
|
||||||
The following input types are specified at present: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Specified where? This doesn’t go into any more detail, and they’re never mentioned again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be really nice to be able to define new input/fetcher types. This would be somewhat tricky, since most such definitions would rely on a nixpkgs, so you need a kind of overlay-style process where the fetchers and inputs are mutually defined (see nmattia/niv#137 for a POC implementation of such a scheme in However, otherwise I would guess that every fetcher needs to be implemented in the flake tool ( |
||||||
|
||||||
* `git`: A Git repository or dirty local working tree. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. git protocol doesn't let you get to the working tree, so should be two-three different options
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll have to check - it might be that this description is out of date. It may be that |
||||||
|
||||||
* `github`: A more efficient scheme to fetch repositories from GitHub | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
as tarballs. These have slightly different semantics from `git` | ||||||
(in particular, the `revCount` attribute is not available). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imo github option shouldn't be a part of the spec (but may have special shortcuts/tools in cli)
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice for things like github to be provided by external plugins, but github fetch does have different semantics than http fetch, in part because of the things you call out here! IMO it should be a separate primitive. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bitbucket for example also has tarball downloads of git repos, GitLab too, likely many others. So going this way would probably mean that in future more git (or even non-git) hosting services will need special handling. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Other hosting services aren't the center of NixOS coordination, though.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, most Nix-related repos are on GitHub, so in the interest of UX, it's nice to provide a shorter syntax and better semantics for GitHub. We don't have to support every other hosting service out there. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I find that most repos use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@rycee uses many GitLab inputs for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, especially if we can get the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I create a Libfetcher for GitLab: NixOS/nix#3636 |
||||||
|
||||||
* `tarball`: A `.tar.{gz,xz,bz2}` file. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this local, URL, or URL that can also be file:// URL? |
||||||
|
||||||
* `path`: A directory in the file system. This generally should be | ||||||
avoided in favor of `git` inputs, since `path` inputs have no | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
concept of revisions (only a content hash) or tracked files | ||||||
(anything in the source directory is copied). | ||||||
|
||||||
* `hg`: A Mercurial repository. | ||||||
|
||||||
## Lock files | ||||||
|
||||||
Inputs specified in `flake.nix` are typically "unlocked" in that they | ||||||
don't specify an exact revision. To ensure reproducibility, Nix will | ||||||
automatically generate and use a *lock file* called `flake.lock` in | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a flake contains a locked (already in |
||||||
the flake's directory. The lock file contains a tree of mappings from | ||||||
the inputs specified in `flake.nix` to inputs specifications that | ||||||
contain revisions. | ||||||
|
||||||
For example, if `flake.nix` has the inputs in the example above, then | ||||||
the resulting lock file might be: | ||||||
``` | ||||||
{ | ||||||
"version": 4, | ||||||
"inputs": { | ||||||
"import-cargo": { | ||||||
"inputs": {}, | ||||||
"narHash": "sha256-mxwKMDFOrhjrBQhIWwwm8mmEugyx/oVlvBH1CKxchlw=", | ||||||
"original": { | ||||||
"type": "github", | ||||||
"owner": "edolstra", | ||||||
"repo": import-cargo" | ||||||
}, | ||||||
"resolved": { | ||||||
"type": "github", | ||||||
"owner": "edolstra", | ||||||
"repo": "import-cargo", | ||||||
"rev": "c33e13881386931038d46a7aca4c9561144d582e" | ||||||
} | ||||||
}, | ||||||
"nixpkgs": { | ||||||
"inputs": {}, | ||||||
"narHash": "sha256-p7UqhvhwS5MZfqUbLbFm+nfG/SMJrgpNXxWpRMFif8c=", | ||||||
"original": { | ||||||
"type": "github", | ||||||
"owner": "NixOS", | ||||||
"repo": nixpkgs" | ||||||
}, | ||||||
"resolved": { | ||||||
"type": "github", | ||||||
"owner": "NixOS", | ||||||
"repo": "nixpkgs", | ||||||
"rev": "4a7047c6e93e8480eb4ca7fd1fd5a2aa457d9082" | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
Thus, when we build this flake, the input `nixpkgs` is mapped to | ||||||
revision `c33e13881386931038d46a7aca4c9561144d582e` of the | ||||||
`edolstra/import-cargo` repository on GitHub. Nix will also check that | ||||||
the content hash of the input is equal to the one recorded in the lock | ||||||
file. This check is superfluous for Git repositories (since the commit | ||||||
hash serves a similar purpose), but for GitHub archives, we cannot | ||||||
directly check that the contents match the commit hash. | ||||||
|
||||||
Note that lock files are only used at top-level: the `flake.lock` | ||||||
files in dependencies (if they exist) are ignored. The lock file | ||||||
transitively locks direct as well as indirect dependencies. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be useful to have an option when fetching a flake as input to use the built version of that flake according to its own lockfile. But that can be a future enhancement. |
||||||
|
||||||
## Reproducible evaluation | ||||||
|
||||||
Lock files are not sufficient by themselves to ensure reproducible | ||||||
evaluation. We also need to disallow certain impurities that the Nix | ||||||
language previously allowed. In particular, the following are | ||||||
disallowed in a flake: | ||||||
Comment on lines
+412
to
+415
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Flakes for the most part feels like an attempt to solve the centralisation problem: it's too difficult / awkward to manage things outside of the nixpkgs monorepo. I can see how improving on reproducability is important, but why must it be inextricably linked with flakes? These impurities have allowed existing tools (like my own nix-wrangle) to serve as testing grounds for similar approaches to flakes, which lets us try out new ideas before baking them into nix. It seems reasonable for flakes to be able to lock down these features - i.e. to have an attribute which opts in or out of this strict reproducible mode, and have that be enforced by the evaluator. A strict flake couldn't depend on a non-strict flake, and there could potentially be a toplevel command line argument to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure flakes are going to solve the problems with packages maintained outside Nixpkgs at scale, so maybe reproducible evaluation is the main thing that has a chance of working with flakes. And maybe some UI-adjacent stuff for package enumeration. |
||||||
|
||||||
* Access to files outside of the top-level flake or its inputs, as | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading this, it's not clear to me whether I'd be able to do things like
Is this "inside" the current flake? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, |
||||||
well as paths fetched using `fetchTarball`, `fetchGit` and so on | ||||||
without a commit hash or content hash. In particular this means that | ||||||
Nixpkgs will not be able to use `~/.config/nixpkgs` anymore. | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
* Access to the environment. This means that `builtins.getEnv "<var>"` | ||||||
always returns an empty string. | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
* Access to the system type (`builtins.currentSystem`). | ||||||
|
||||||
* Access to the current time (`builtins.currentTime`). | ||||||
|
||||||
* Use of the Nix search path (`<...>`); composition must be done | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What rationale is there for continuing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you misread that, because |
||||||
through flake inputs or `fetchX` builtins. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this mean that the
when I haven't added nixpkgs to my flakes.nix? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, they can't. You can only use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that we have more single-project fetchers in Nixpkgs than will be ever supported inside Nix, allowing use of FOD for import does sound like a good idea… |
||||||
|
||||||
# Drawbacks | ||||||
[drawbacks]: #drawbacks | ||||||
|
||||||
Pure evaluation breaks certain workflows. In particular, it breaks the | ||||||
use of the Nixpkgs configuration file. Similarly, there are people who | ||||||
rely on `$NIX_PATH` to pass configuration data to NixOps | ||||||
configurations. | ||||||
|
||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
# Alternatives | ||||||
[alternatives]: #alternatives | ||||||
|
||||||
For composition of multi-repository projects, the main alternative is | ||||||
to continue on with explicit `fetchGit` / `fetchTarball` calls to pull | ||||||
in other repositories. However, since there is no explicit listing of | ||||||
dependencies, this does not provide automatic updating (i.e. there is | ||||||
no equivalent of `nix flake update`). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be eased with tool support, e.g. niv provides a similar workflow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And we even have experience with various autoupdaters in Nixpkgs! If the autoupdate does nothing interesting as it is limited to git repositories, none of the complicated cases would arise.
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
Instead of a `flake.nix`, flakes could store their metadata in a | ||||||
simpler format such as JSON or TOML. This avoids the Turing tarpit | ||||||
where getting flake metadata requires the execution of an arbitrarily | ||||||
complex, possibly non-terminating program. | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe a part of the flake metadata (edition, description, name if added) could live in a JSON file and the rest like outputs in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's the idea. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, inputs are also almost metadata, and there keeping things in Turing-complete Nix has some benefits (and some drawbacks, sure); as written the alternative seems a bit too binary. (I am among those who support basic part of the metdata being in JSON, but I do support input definitions near the output definitions, in Nix) |
||||||
|
||||||
Flakes could be implemented as an external tool on top of Nix. Indeed, | ||||||
there is nothing that flakes allow you to do that couldn't previously | ||||||
be done using `fetchGit`, the `--pure-eval` flag and some shell | ||||||
scripting. However, implementing flake-like functionality in an | ||||||
external tool would defeat the goals of this RFC. First, it probably | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reasoning behind Flakes deserving to be blessed as the standard before they've proven their merit or even having given other implementations a chance? Or is the intention to race RFCs in becoming the standard? I like the Flakes RFC btw. I just don't see why it should be treated differently. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, no Nix feature has ever followed that process before being included, except I'm very skeptical that external implementations would ever end up being merged in practice. See There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Counterexamples: Cargo, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If anything, I think for a shift such as the one this RFC suggests, trying it out in practice somewhat at scale would make it easier to adopt it. That way, potential issues in the design can be revealed and iterated on/addressed before we bolt the design down. I'm not really familiar with the rpm ecosystem, so I don't know why I'm not sure I see why the (to me fairly marginal) risk of having two tools instead of one is bigger than the risk of the community moving in a direction and then looking back in retrospect and wanting to make changes to the flake design. Don't get me wrong, I like a lot in the Flakes design. I just don't want us to have to repeat this process in a few years' time just because we didn't test the design well enough before giving it its blessing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the current revision of this RFC to only talk about the flake format, this paragraph seems out of place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’m not sure it is. Whether it would be possible to generate this lockfile using an out-of-tree tool, or has to be implemented in Nix itself, is an important thing to know. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know the full history, but I believe families like apt/deb, yum/rpm/dnf, gem/bundler were often built separately so as not to risk disruption by drastically modifying the (legacy) original tool, or to add on extra layers of functionality which only a subset of the community wanted (at the time). If external-flakes is built with the explicit goal that it will be modified until it's either made part of nix or abandoned, that seems like it would alleviate that risk perfectly well. I for one would love to try out a real implementation before comitting to it, since it's much harder to spot some usability or functionality gaps in an RFC than in practice. And I have a bunch of my own projects which are ripe for trying out a distributed approach to nix expressions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Technically speaking, Nix and Nixpkgs are still separate repositories, and there are some cases where it causes a need for more complicated coordination. Still, Nixpkgs is the defacto repository of packages for Nix, but there are forks that are usable by anyone who is interested. At the same time, NixOS was a separate repository at some point, then got into the same monorepo. So merges happen here if they make sense. What is the specific problem being addressed by flakes being built-in and not just the defacto tool? |
||||||
wouldn't lead to a standard way to structure and compose Nix projects, | ||||||
since we might well end up with numerous competing | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think home-manager is an example where we had exactly that, then one standard got majority? |
||||||
"standards". Second, it would degrade rather than improve the Nix UX, | ||||||
since users would now have to deal with Nix *and* the flake-like tool | ||||||
on top of it. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The tool being a part of Nix doesn't do much to remove needing to keep track of flake-specific invokations There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not true, commands like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part can be achieved by specifying that flake should have a |
||||||
|
||||||
# Unresolved questions | ||||||
[unresolved]: #unresolved-questions | ||||||
|
||||||
* Should flakes have arguments (like "system type")? This must be done | ||||||
in a way that maintains hermetic evaluation and evaluation caching. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems very important to me. Nix and nixpkgs are shaping up to be a best-in-class solution to cross-compiling complex software. It would be a shame if flakes made this harder again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be implemented via inputs that are literal Nix values? |
||||||
|
||||||
* Currently, if flake dependencies (repositories or branches) get | ||||||
deleted upstream, rebuilding the flake may fail. (This is similar to | ||||||
`fetchurl` referencing a stale URL.) We need a command to gather all | ||||||
flake dependencies and copy them somewhere else (possibly vendor | ||||||
them into the repository of the calling flake). | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could existing binary cache logic be reused here? In a locked flake all inputs are checksummed, so they can be built as FODs, which could be made fetchable even from untrusted caches. Maybe this requires an extra field in flake metadata. Anyway, as long as one doesn't vendor or binary cache all the sources and bootstrap binaries, one doesn't get fully safe. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, in principle you could use a binary cache for this. |
||||||
|
||||||
* Maybe flake metadata should be stored in a `flake.json` or | ||||||
`flake.toml` file. This would prevent ambiguities when the Nix | ||||||
language changes in a future edition. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry for being so late to the party, but while I understand that there were valid reasons to remove the I'm asking because @edolstra stated in #49 (comment) that it's not 100% clear whether There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The decision was to name next version |
||||||
|
||||||
# Future work | ||||||
[future]: #future-work | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of the CLI, etc. work should be mentioned here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also think that at least a short roadmap of what happens next should be added here before accepting the RFC. At least I'd feel more confident in this if I know how we'll proceed. |
||||||
|
||||||
* The "edition" feature enables future Nix changes, including language | ||||||
changes. For example, changing the parsing of multiline strings | ||||||
(https://github.com/NixOS/nix/pull/2490) could be conditional on the | ||||||
flake's edition. | ||||||
edolstra marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
* Currently flake outputs are untyped; we only have some conventions | ||||||
about what they should be (e.g. `packages` should be an attribute | ||||||
set of derivations). For discoverability, it would be nice if | ||||||
outputs were typed. Maybe this could be done via the Nix | ||||||
configurations concept | ||||||
(https://gist.github.com/edolstra/29ce9d8ea399b703a7023073b0dbc00d). | ||||||
|
||||||
# Acknowledgments | ||||||
|
||||||
Funding for the development of the flakes prototype was provided by | ||||||
[Target Corporation](https://www.target.com/). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mention how pure evaluation mode fits in and if/why that is not sufficient.