diff options
Diffstat (limited to 'src/libexpr/flake/flakeref.hh')
-rw-r--r-- | src/libexpr/flake/flakeref.hh | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/src/libexpr/flake/flakeref.hh b/src/libexpr/flake/flakeref.hh new file mode 100644 index 000000000..addf5449f --- /dev/null +++ b/src/libexpr/flake/flakeref.hh @@ -0,0 +1,200 @@ +#pragma once + +#include "types.hh" +#include "hash.hh" + +#include <variant> + +namespace nix { + +/* Flake references are a URI-like syntax to specify a flake. + + Examples: + + * <flake-id>(/rev-or-ref(/rev)?)? + + Look up a flake by ID in the flake lock file or in the flake + registry. These must specify an actual location for the flake + using the formats listed below. Note that in pure evaluation + mode, the flake registry is empty. + + Optionally, the rev or ref from the dereferenced flake can be + overriden. For example, + + nixpkgs/19.09 + + uses the "19.09" branch of the nixpkgs' flake GitHub repository, + while + + nixpkgs/98a2a5b5370c1e2092d09cb38b9dcff6d98a109f + + uses the specified revision. For Git (rather than GitHub) + repositories, both the rev and ref must be given, e.g. + + nixpkgs/19.09/98a2a5b5370c1e2092d09cb38b9dcff6d98a109f + + * github:<owner>/<repo>(/<rev-or-ref>)? + + A repository on GitHub. These differ from Git references in that + they're downloaded in a efficient way (via the tarball mechanism) + and that they support downloading a specific revision without + specifying a branch. <rev-or-ref> is either a commit hash ("rev") + or a branch or tag name ("ref"). The default is: "master" if none + is specified. Note that in pure evaluation mode, a commit hash + must be used. + + Flakes fetched in this manner expose "rev" and "lastModified" + attributes, but not "revCount". + + Examples: + + github:edolstra/dwarffs + github:edolstra/dwarffs/unstable + github:edolstra/dwarffs/41c0c1bf292ea3ac3858ff393b49ca1123dbd553 + + * git+https://<server>/<path>(\?attr(&attr)*)? + git+ssh://<server>/<path>(\?attr(&attr)*)? + git://<server>/<path>(\?attr(&attr)*)? + file:///<path>(\?attr(&attr)*)? + + where 'attr' is one of: + rev=<rev> + ref=<ref> + + A Git repository fetched through https. The default for "ref" is + "master". + + Examples: + + git+https://example.org/my/repo.git + git+https://example.org/my/repo.git?ref=release-1.2.3 + git+https://example.org/my/repo.git?rev=e72daba8250068216d79d2aeef40d4d95aff6666 + git://github.com/edolstra/dwarffs.git?ref=flake&rev=2efca4bc9da70fb001b26c3dc858c6397d3c4817 + + * /path(\?attr(&attr)*)? + + Like file://path, but if no "ref" or "rev" is specified, the + (possibly dirty) working tree will be used. Using a working tree + is not allowed in pure evaluation mode. + + Examples: + + /path/to/my/repo + /path/to/my/repo?ref=develop + /path/to/my/repo?rev=e72daba8250068216d79d2aeef40d4d95aff6666 + + * https://<server>/<path>.tar.xz(?hash=<sri-hash>) + file:///<path>.tar.xz(?hash=<sri-hash>) + + A flake distributed as a tarball. In pure evaluation mode, an SRI + hash is mandatory. It exposes a "lastModified" attribute, being + the newest file inside the tarball. + + Example: + + https://releases.nixos.org/nixos/unstable/nixos-19.03pre167858.f2a1a4e93be/nixexprs.tar.xz + https://releases.nixos.org/nixos/unstable/nixos-19.03pre167858.f2a1a4e93be/nixexprs.tar.xz?hash=sha256-56bbc099995ea8581ead78f22832fee7dbcb0a0b6319293d8c2d0aef5379397c + + Note: currently, there can be only one flake per Git repository, and + it must be at top-level. In the future, we may want to add a field + (e.g. "dir=<dir>") to specify a subdirectory inside the repository. +*/ + +typedef std::string FlakeId; +typedef std::string FlakeUri; + +struct FlakeRef +{ + struct IsId + { + FlakeId id; + bool operator<(const IsId & b) const { return id < b.id; }; + bool operator==(const IsId & b) const { return id == b.id; }; + }; + + struct IsGitHub { + std::string owner, repo; + bool operator<(const IsGitHub & b) const { + return std::make_tuple(owner, repo) < std::make_tuple(b.owner, b.repo); + } + bool operator==(const IsGitHub & b) const { + return owner == b.owner && repo == b.repo; + } + }; + + // Git, Tarball + struct IsGit + { + std::string uri; + bool operator<(const IsGit & b) const { return uri < b.uri; } + bool operator==(const IsGit & b) const { return uri == b.uri; } + }; + + struct IsPath + { + Path path; + bool operator<(const IsPath & b) const { return path < b.path; } + bool operator==(const IsPath & b) const { return path == b.path; } + }; + + // Git, Tarball + + std::variant<IsId, IsGitHub, IsGit, IsPath> data; + + std::optional<std::string> ref; + std::optional<Hash> rev; + Path subdir = ""; // This is a relative path pointing at the flake.nix file's directory, relative to the git root. + + bool operator<(const FlakeRef & flakeRef) const + { + return std::make_tuple(data, ref, rev, subdir) < + std::make_tuple(flakeRef.data, flakeRef.ref, flakeRef.rev, subdir); + } + + bool operator==(const FlakeRef & flakeRef) const + { + return std::make_tuple(data, ref, rev, subdir) == + std::make_tuple(flakeRef.data, flakeRef.ref, flakeRef.rev, flakeRef.subdir); + } + + // Parse a flake URI. + FlakeRef(const std::string & uri, bool allowRelative = false); + + // FIXME: change to operator <<. + std::string to_string() const; + + /* Check whether this is a "direct" flake reference, that is, not + a flake ID, which requires a lookup in the flake registry. */ + bool isDirect() const + { + return !std::get_if<FlakeRef::IsId>(&data); + } + + /* Check whether this is an "immutable" flake reference, that is, + one that contains a commit hash or content hash. */ + bool isImmutable() const; + + FlakeRef baseRef() const; + + bool isDirty() const + { + return std::get_if<FlakeRef::IsPath>(&data) + && rev == Hash(rev->type); + } + + /* Return true if 'other' is not less specific than 'this'. For + example, 'nixpkgs' contains 'nixpkgs/release-19.03', and both + 'nixpkgs' and 'nixpkgs/release-19.03' contain + 'nixpkgs/release-19.03/<hash>'. */ + bool contains(const FlakeRef & other) const; +}; + +std::ostream & operator << (std::ostream & str, const FlakeRef & flakeRef); + +MakeError(BadFlakeRef, Error); +MakeError(MissingFlake, BadFlakeRef); + +std::optional<FlakeRef> parseFlakeRef( + const std::string & uri, bool allowRelative = false); + +} |