diff --git a/nix/getBins/default.nix b/nix/getBins/default.nix deleted file mode 100644 index e354b176c..000000000 --- a/nix/getBins/default.nix +++ /dev/null @@ -1,51 +0,0 @@ -{ lib, pkgs, depot, ... }: - -# Takes a derivation and a list of binary names -# and returns an attribute set of `name -> path`. -# The list can also contain renames in the form of -# `{ use, as }`, which goes `as -> usePath`. -# -# It is usually used to construct an attrset `bins` -# containing all the binaries required in a file, -# similar to a simple import system. -# -# Example: -# -# bins = getBins pkgs.hello [ "hello" ] -# // getBins pkgs.coreutils [ "printf" "ln" "echo" ] -# // getBins pkgs.execline -# [ { use = "if"; as = "execlineIf" } ] -# // getBins pkgs.s6-portable-utils -# [ { use = "s6-test"; as = "test" } -# { use = "s6-cat"; as = "cat" } -# ]; -# -# provides -# bins.{hello,printf,ln,echo,execlineIf,test,cat} -# - -let - getBins = drv: xs: - let - f = x: - # TODO(Profpatsch): typecheck - let x' = if builtins.isString x then { use = x; as = x; } else x; - in { - name = x'.as; - value = "${lib.getBin drv}/bin/${x'.use}"; - }; - in - builtins.listToAttrs (builtins.map f xs); - - - tests = import ./tests.nix { - inherit getBins; - inherit (depot.nix) writeScriptBin; - inherit (depot.nix.runTestsuite) assertEq it runTestsuite; - }; - -in -{ - __functor = _: getBins; - inherit tests; -} diff --git a/nix/getBins/tests.nix b/nix/getBins/tests.nix deleted file mode 100644 index e0f5ab426..000000000 --- a/nix/getBins/tests.nix +++ /dev/null @@ -1,40 +0,0 @@ -{ writeScriptBin, assertEq, it, runTestsuite, getBins }: - -let - drv = writeScriptBin "hello" "it’s me"; - drv2 = writeScriptBin "goodbye" "tschau"; - - bins = getBins drv [ - "hello" - { use = "hello"; as = "also-hello"; } - ] - // getBins drv2 [ "goodbye" ] - ; - - simple = it "path is equal to the executable name" [ - (assertEq "path" - bins.hello - "${drv}/bin/hello") - (assertEq "content" - (builtins.readFile bins.hello) - "it’s me") - ]; - - useAs = it "use/as can be used to rename attributes" [ - (assertEq "path" - bins.also-hello - "${drv}/bin/hello") - ]; - - secondDrv = it "by merging attrsets you can build up bins" [ - (assertEq "path" - bins.goodbye - "${drv2}/bin/goodbye") - ]; - -in -runTestsuite "getBins" [ - simple - useAs - secondDrv -] diff --git a/nix/mergePatch/default.nix b/nix/mergePatch/default.nix deleted file mode 100644 index d56106925..000000000 --- a/nix/mergePatch/default.nix +++ /dev/null @@ -1,192 +0,0 @@ -{ lib, depot, ... }: -/* - JSON Merge-Patch for nix - Spec: https://tools.ietf.org/html/rfc7396 - - An algorithm for changing and removing fields in nested objects. - - For example, given the following original document: - - { - a = "b"; - c = { - d = "e"; - f = "g"; - } - } - - Changing the value of `a` and removing `f` can be achieved by merging the patch - - { - a = "z"; - c.f = null; - } - - which results in - - { - a = "z"; - c = { - d = "e"; - }; - } - - Pseudo-code: - define MergePatch(Target, Patch): - if Patch is an Object: - if Target is not an Object: - Target = {} # Ignore the contents and set it to an empty Object - for each Name/Value pair in Patch: - if Value is null: - if Name exists in Target: - remove the Name/Value pair from Target - else: - Target[Name] = MergePatch(Target[Name], Value) - return Target - else: - return Patch -*/ - -let - foldlAttrs = op: init: attrs: - lib.foldl' op init - (lib.mapAttrsToList lib.nameValuePair attrs); - - mergePatch = target: patch: - if lib.isAttrs patch - then - let target' = if lib.isAttrs target then target else { }; - in foldlAttrs - (acc: patchEl: - if patchEl.value == null - then removeAttrs acc [ patchEl.name ] - else acc // { - ${patchEl.name} = - mergePatch - (acc.${patchEl.name} or "unnused") - patchEl.value; - }) - target' - patch - else patch; - - inherit (depot.nix.runTestsuite) - runTestsuite - it - assertEq - ; - - tests = - let - # example target from the RFC - testTarget = { - a = "b"; - c = { - d = "e"; - f = "g"; - }; - }; - # example patch from the RFC - testPatch = { - a = "z"; - c.f = null; - }; - emptyPatch = it "the empty patch returns the original target" [ - (assertEq "id" - (mergePatch testTarget { }) - testTarget) - ]; - nonAttrs = it "one side is a non-attrset value" [ - (assertEq "target is a value means the value is replaced by the patch" - (mergePatch 42 testPatch) - (mergePatch { } testPatch)) - (assertEq "patch is a value means it replaces target alltogether" - (mergePatch testTarget 42) - 42) - ]; - rfcExamples = it "the examples from the RFC" [ - (assertEq "a subset is deleted and overwritten" - (mergePatch testTarget testPatch) - { - a = "z"; - c = { - d = "e"; - }; - }) - (assertEq "a more complicated example from the example section" - (mergePatch - { - title = "Goodbye!"; - author = { - givenName = "John"; - familyName = "Doe"; - }; - tags = [ "example" "sample" ]; - content = "This will be unchanged"; - } - { - title = "Hello!"; - phoneNumber = "+01-123-456-7890"; - author.familyName = null; - tags = [ "example" ]; - }) - { - title = "Hello!"; - phoneNumber = "+01-123-456-7890"; - author = { - givenName = "John"; - }; - tags = [ "example" ]; - content = "This will be unchanged"; - }) - ]; - - rfcTests = - let - r = index: target: patch: res: - (assertEq "test number ${toString index}" - (mergePatch target patch) - res); - in - it "the test suite from the RFC" [ - (r 1 { "a" = "b"; } { "a" = "c"; } { "a" = "c"; }) - (r 2 { "a" = "b"; } { "b" = "c"; } { "a" = "b"; "b" = "c"; }) - (r 3 { "a" = "b"; } { "a" = null; } { }) - (r 4 { "a" = "b"; "b" = "c"; } - { "a" = null; } - { "b" = "c"; }) - (r 5 { "a" = [ "b" ]; } { "a" = "c"; } { "a" = "c"; }) - (r 6 { "a" = "c"; } { "a" = [ "b" ]; } { "a" = [ "b" ]; }) - (r 7 { "a" = { "b" = "c"; }; } - { "a" = { "b" = "d"; "c" = null; }; } - { "a" = { "b" = "d"; }; }) - (r 8 { "a" = [{ "b" = "c"; }]; } - { "a" = [ 1 ]; } - { "a" = [ 1 ]; }) - (r 9 [ "a" "b" ] [ "c" "d" ] [ "c" "d" ]) - (r 10 { "a" = "b"; } [ "c" ] [ "c" ]) - (r 11 { "a" = "foo"; } null null) - (r 12 { "a" = "foo"; } "bar" "bar") - (r 13 { "e" = null; } { "a" = 1; } { "e" = null; "a" = 1; }) - (r 14 [ 1 2 ] - { "a" = "b"; "c" = null; } - { "a" = "b"; }) - (r 15 { } - { "a" = { "bb" = { "ccc" = null; }; }; } - { "a" = { "bb" = { }; }; }) - ]; - - in - runTestsuite "mergePatch" [ - emptyPatch - nonAttrs - rfcExamples - rfcTests - ]; - -in -{ - __functor = _: mergePatch; - - inherit tests; -} diff --git a/nix/nint/OWNERS b/nix/nint/OWNERS deleted file mode 100644 index 557709b72..000000000 --- a/nix/nint/OWNERS +++ /dev/null @@ -1 +0,0 @@ -raitobezarius diff --git a/nix/nint/README.md b/nix/nint/README.md deleted file mode 100644 index 369a82761..000000000 --- a/nix/nint/README.md +++ /dev/null @@ -1,93 +0,0 @@ -# nint — Nix INTerpreter - -`nint` is a shebang compatible interpreter for nix. It is currently -implemented as a fairly trivial wrapper around `nix-instantiate --eval`. -It allows to run nix expressions as command line tools if they conform -to the following calling convention: - -* Every nix script needs to evaluate to a function which takes an - attribute set as its single argument. Ideally a set pattern with - an ellipsis should be used. By default `nint` passes the following - arguments: - - * `currentDir`: the current working directory as a nix path - * `argv`: a list of arguments to the invokation including the - program name at `builtins.head argv`. - * Extra arguments can be manually passed as described below. - -* The return value must either be - - * A string which is rendered to `stdout`. - - * An attribute set with the following optional attributes: - - * `stdout`: A string that's rendered to `stdout` - * `stderr`: A string that's rendered to `stderr` - * `exit`: A number which is used as an exit code. - If missing, nint always exits with 0 (or equivalent). - -## Usage - -``` -nint [ --arg ARG VALUE … ] script.nix [ ARGS … ] -``` - -Instead of `--arg`, `--argstr` can also be used. They both work -like the flags of the same name for `nix-instantiate` and may -be specified any number of times as long as they are passed -*before* the nix expression to run. - -Below is a shebang which also passes `depot` as an argument -(note the usage of `env -S` to get around the shebang limitation -to two arguments). - -```nix -#!/usr/bin/env -S nint --arg depot /path/to/depot -``` - -## Limitations - -* No side effects except for writing to `stdout`. - -* Output is not streaming, i. e. even if the output is incrementally - calculated, nothing will be printed until the full output is available. - With plain nix strings we can't do better anyways. - -* Limited error handling for the script, no way to set the exit code etc. - -Some of these limitations may be possible to address in the future by using -an alternative nix interpreter and a more elaborate calling convention. - -## Example - -Below is a (very simple) implementation of a `ls(1)`-like program in nix: - -```nix -#!/usr/bin/env nint -{ currentDir, argv, ... }: - -let - lib = import ; - - dirs = - let - args = builtins.tail argv; - in - if args == [] - then [ currentDir ] - else args; - - makeAbsolute = p: - if builtins.isPath p - then p - else if builtins.match "^/.*" p != null - then p - else "${toString currentDir}/${p}"; -in - - lib.concatStringsSep "\n" - (lib.flatten - (builtins.map - (d: (builtins.attrNames (builtins.readDir (makeAbsolute d)))) - dirs)) + "\n" -``` diff --git a/nix/nint/default.nix b/nix/nint/default.nix deleted file mode 100644 index 0087fc041..000000000 --- a/nix/nint/default.nix +++ /dev/null @@ -1,16 +0,0 @@ -{ depot, pkgs, ... }: - -let - inherit (depot.nix.writers) - rustSimpleBin - ; -in - -rustSimpleBin -{ - name = "nint"; - dependencies = [ - depot.third_party.rust-crates.serde_json - ]; -} - (builtins.readFile ./nint.rs) diff --git a/nix/nint/nint.rs b/nix/nint/nint.rs deleted file mode 100644 index abb0153c3..000000000 --- a/nix/nint/nint.rs +++ /dev/null @@ -1,149 +0,0 @@ -extern crate serde_json; - -use serde_json::Value; -use std::convert::TryFrom; -use std::ffi::OsString; -use std::io::{stderr, stdout, Error, ErrorKind, Write}; -use std::os::unix::ffi::{OsStrExt, OsStringExt}; -use std::process::Command; - -fn render_nix_string(s: &OsString) -> OsString { - let mut rendered = Vec::new(); - - rendered.extend(b"\""); - - for b in s.as_os_str().as_bytes() { - match char::from(*b) { - '\"' => rendered.extend(b"\\\""), - '\\' => rendered.extend(b"\\\\"), - '$' => rendered.extend(b"\\$"), - _ => rendered.push(*b), - } - } - - rendered.extend(b"\""); - - OsString::from_vec(rendered) -} - -fn render_nix_list(arr: &[OsString]) -> OsString { - let mut rendered = Vec::new(); - - rendered.extend(b"[ "); - - for el in arr { - rendered.extend(render_nix_string(el).as_os_str().as_bytes()); - rendered.extend(b" "); - } - - rendered.extend(b"]"); - - OsString::from_vec(rendered) -} - -/// Slightly overkill helper macro which takes a `Map` obtained -/// from `Value::Object` and an output name (`stderr` or `stdout`) as an -/// identifier. If a value exists for the given output in the object it gets -/// written to the appropriate output. -macro_rules! handle_set_output { - ($map_name:ident, $output_name:ident) => { - match $map_name.get(stringify!($output_name)) { - Some(Value::String(s)) => $output_name().write_all(s.as_bytes()), - Some(_) => Err(Error::new( - ErrorKind::Other, - format!("Attribute {} must be a string!", stringify!($output_name)), - )), - None => Ok(()), - } - }; -} - -fn main() -> std::io::Result<()> { - let mut nix_args = Vec::new(); - - let mut args = std::env::args_os().into_iter(); - let mut in_args = true; - - let mut argv: Vec = Vec::new(); - - // skip argv[0] - args.next(); - - loop { - let arg = match args.next() { - Some(a) => a, - None => break, - }; - - if !arg.to_str().map(|s| s.starts_with("-")).unwrap_or(false) { - in_args = false; - } - - if in_args { - match (arg.to_str()) { - Some("--arg") | Some("--argstr") => { - nix_args.push(arg); - nix_args.push(args.next().unwrap()); - nix_args.push(args.next().unwrap()); - Ok(()) - } - _ => Err(Error::new(ErrorKind::Other, "unknown argument")), - }? - } else { - argv.push(arg); - } - } - - if argv.len() < 1 { - Err(Error::new(ErrorKind::Other, "missing argv")) - } else { - let cd = std::env::current_dir()?.into_os_string(); - - nix_args.push(OsString::from("--arg")); - nix_args.push(OsString::from("currentDir")); - nix_args.push(cd); - - nix_args.push(OsString::from("--arg")); - nix_args.push(OsString::from("argv")); - nix_args.push(render_nix_list(&argv[..])); - - nix_args.push(OsString::from("--eval")); - nix_args.push(OsString::from("--strict")); - nix_args.push(OsString::from("--json")); - - nix_args.push(argv[0].clone()); - - let run = Command::new("nix-instantiate").args(nix_args).output()?; - - match serde_json::from_slice(&run.stdout[..]) { - Ok(Value::String(s)) => stdout().write_all(s.as_bytes()), - Ok(Value::Object(m)) => { - handle_set_output!(m, stdout)?; - handle_set_output!(m, stderr)?; - - match m.get("exit") { - Some(Value::Number(n)) => { - let code = n.as_i64().and_then(|v| i32::try_from(v).ok()); - - match code { - Some(i) => std::process::exit(i), - None => { - Err(Error::new(ErrorKind::Other, "Attribute exit is not an i32")) - } - } - } - Some(_) => Err(Error::new(ErrorKind::Other, "exit must be a number")), - None => Ok(()), - } - } - Ok(_) => Err(Error::new( - ErrorKind::Other, - "output must be a string or an object", - )), - _ => { - stderr().write_all(&run.stderr[..]); - Err(Error::new(ErrorKind::Other, "internal nix error")) - } - } - } -} diff --git a/nix/readTree/tests/default.nix b/nix/readTree/tests/default.nix deleted file mode 100644 index 6f9eb02ef..000000000 --- a/nix/readTree/tests/default.nix +++ /dev/null @@ -1,139 +0,0 @@ -{ depot, lib, ... }: - -let - inherit (depot.nix.runTestsuite) - runTestsuite - it - assertEq - assertThrows - ; - - tree-ex = depot.nix.readTree { - path = ./test-example; - args = { }; - }; - - example = it "corresponds to the README example" [ - (assertEq "third_party attrset" - (lib.isAttrs tree-ex.third_party - && (! lib.isDerivation tree-ex.third_party)) - true) - (assertEq "third_party attrset other attribute" - tree-ex.third_party.favouriteColour - "orange") - (assertEq "rustpkgs attrset aho-corasick" - tree-ex.third_party.rustpkgs.aho-corasick - "aho-corasick") - (assertEq "rustpkgs attrset serde" - tree-ex.third_party.rustpkgs.serde - "serde") - (assertEq "tools cheddear" - "cheddar" - tree-ex.tools.cheddar) - (assertEq "tools roquefort" - tree-ex.tools.roquefort - "roquefort") - ]; - - tree-tl = depot.nix.readTree { - path = ./test-tree-traversal; - args = { }; - }; - - traversal-logic = it "corresponds to the traversal logic in the README" [ - (assertEq "skip-tree/a is read" - tree-tl.skip-tree.a - "a is read normally") - (assertEq "skip-tree does not contain b" - (builtins.attrNames tree-tl.skip-tree) - [ "__readTree" "__readTreeChildren" "a" ]) - (assertEq "skip-tree children list does not contain b" - tree-tl.skip-tree.__readTreeChildren - [ "a" ]) - - (assertEq "skip subtree default.nix is read" - tree-tl.skip-subtree.but - "the default.nix is still read") - (assertEq "skip subtree a/default.nix is skipped" - (tree-tl.skip-subtree ? a) - false) - (assertEq "skip subtree b/c.nix is skipped" - (tree-tl.skip-subtree ? b) - false) - (assertEq "skip subtree a/default.nix would be read without .skip-subtree" - (tree-tl.no-skip-subtree.a) - "am I subtree yet?") - (assertEq "skip subtree b/c.nix would be read without .skip-subtree" - (tree-tl.no-skip-subtree.b.c) - "cool") - - (assertEq "default.nix attrset is merged with siblings" - tree-tl.default-nix.no - "siblings should be read") - (assertEq "default.nix means sibling isn’t read" - (tree-tl.default-nix ? sibling) - false) - (assertEq "default.nix means subdirs are still read and merged into default.nix" - (tree-tl.default-nix.subdir.a) - "but I’m picked up") - - (assertEq "default.nix can be not an attrset" - tree-tl.default-nix.no-merge - "I’m not merged with any children") - (assertEq "default.nix is not an attrset -> children are not merged" - (tree-tl.default-nix.no-merge ? subdir) - false) - - (assertEq "default.nix can contain a derivation" - (lib.isDerivation tree-tl.default-nix.can-be-drv) - true) - (assertEq "Even if default.nix is a derivation, children are traversed and merged" - tree-tl.default-nix.can-be-drv.subdir.a - "Picked up through the drv") - (assertEq "default.nix drv is not changed by readTree" - tree-tl.default-nix.can-be-drv - (import ./test-tree-traversal/default-nix/can-be-drv/default.nix { })) - ]; - - # these each call readTree themselves because the throws have to happen inside assertThrows - wrong = it "cannot read these files and will complain" [ - (assertThrows "this file is not a function" - (depot.nix.readTree { - path = ./test-wrong-not-a-function; - args = { }; - }).not-a-function) - # can’t test for that, assertThrows can’t catch this error - # (assertThrows "this file is a function but doesn’t have dots" - # (depot.nix.readTree {} ./test-wrong-no-dots).no-dots-in-function) - ]; - - read-markers = depot.nix.readTree { - path = ./test-marker; - args = { }; - }; - - assertMarkerByPath = path: - assertEq "${lib.concatStringsSep "." path} is marked correctly" - (lib.getAttrFromPath path read-markers).__readTree - path; - - markers = it "marks nodes correctly" [ - (assertMarkerByPath [ "directory-marked" ]) - (assertMarkerByPath [ "directory-marked" "nested" ]) - (assertMarkerByPath [ "file-children" "one" ]) - (assertMarkerByPath [ "file-children" "two" ]) - (assertEq "nix file children are marked correctly" - read-markers.file-children.__readTreeChildren [ "one" "two" ]) - (assertEq "directory children are marked correctly" - read-markers.directory-marked.__readTreeChildren [ "nested" ]) - (assertEq "absence of children is marked" - read-markers.directory-marked.nested.__readTreeChildren [ ]) - ]; - -in -runTestsuite "readTree" [ - example - traversal-logic - wrong - markers -] diff --git a/nix/runExecline/default.nix b/nix/runExecline/default.nix deleted file mode 100644 index 76fffdce7..000000000 --- a/nix/runExecline/default.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ depot, pkgs, lib, ... }: -let - runExecline = import ./runExecline.nix { - inherit (pkgs) stdenv; - inherit (depot.nix) escapeExecline getBins; - inherit pkgs lib; - }; - - runExeclineLocal = name: args: execline: - runExecline name - (args // { - derivationArgs = args.derivationArgs or { } // { - preferLocalBuild = true; - allowSubstitutes = false; - }; - }) - execline; - - tests = import ./tests.nix { - inherit runExecline runExeclineLocal; - inherit (depot.nix) getBins writeScript; - inherit (pkgs) stdenv coreutils; - inherit pkgs; - }; - -in -{ - __functor = _: runExecline; - local = runExeclineLocal; - inherit tests; -} diff --git a/nix/runExecline/runExecline.nix b/nix/runExecline/runExecline.nix deleted file mode 100644 index 23b9a6330..000000000 --- a/nix/runExecline/runExecline.nix +++ /dev/null @@ -1,122 +0,0 @@ -{ pkgs, stdenv, lib, getBins, escapeExecline }: - -# runExecline is a primitive building block -# for writing non-kitchen sink builders. -# -# It’s conceptually similar to `runCommand`, -# but instead of concatenating bash scripts left -# and right, it actually *uses* the features of -# `derivation`, passing things to `args` -# and making it possible to overwrite the `builder` -# in a sensible manner. -# -# Additionally, it provides a way to pass a nix string -# to `stdin` of the build script. -# -# Similar to //nix/writeExecline, the passed script is -# not a string, but a nested list of nix lists -# representing execline blocks. Escaping is -# done by the implementation, the user can just use -# normal nix strings. -# -# Example: -# -# runExecline "my-drv" { stdin = "hi!"; } [ -# "importas" "out" "out" -# # this pipes stdout of s6-cat to $out -# # and s6-cat redirects from stdin to stdout -# "redirfd" "-w" "1" "$out" bins.s6-cat -# ] -# -# which creates a derivation with "hi!" in $out. -# -# See ./tests.nix for more examples. - - -let - bins = getBins pkgs.execline [ - "execlineb" - { use = "if"; as = "execlineIf"; } - "redirfd" - "importas" - "exec" - ] - // getBins pkgs.s6-portable-utils [ - "s6-cat" - "s6-grep" - "s6-touch" - "s6-test" - "s6-chmod" - ]; - -in - -# TODO: move name into the attrset -name: -{ - # a string to pass as stdin to the execline script - stdin ? "" - # a program wrapping the acutal execline invocation; - # should be in Bernstein-chaining style -, builderWrapper ? bins.exec - # additional arguments to pass to the derivation -, derivationArgs ? { } -}: -# the execline script as a nested list of string, -# representing the blocks; -# see docs of `escapeExecline`. -execline: - -# those arguments can’t be overwritten -assert !derivationArgs ? system; -assert !derivationArgs ? name; -assert !derivationArgs ? builder; -assert !derivationArgs ? args; - -derivation (derivationArgs // { - # TODO(Profpatsch): what about cross? - inherit (stdenv) system; - inherit name; - - # okay, `builtins.toFile` does not accept strings - # that reference drv outputs. This means we need - # to pass the script and stdin as envvar; - # this might clash with another passed envar, - # so we give it a long & unique name - _runExeclineScript = - let - in escapeExecline execline; - _runExeclineStdin = stdin; - passAsFile = [ - "_runExeclineScript" - "_runExeclineStdin" - ] ++ derivationArgs.passAsFile or [ ]; - - # the default, exec acts as identity executable - builder = builderWrapper; - - args = [ - bins.importas # import script file as $script - "-ui" # drop the envvar afterwards - "script" # substitution name - "_runExeclineScriptPath" # passed script file - - bins.importas # do the same for $stdin - "-ui" - "stdin" - "_runExeclineStdinPath" - - bins.redirfd # now we - "-r" # read the file - "0" # into the stdin of execlineb - "$stdin" # that was given via stdin - - bins.execlineb # the actual invocation - # TODO(Profpatsch): depending on the use-case, -S0 might not be enough - # in all use-cases, then a wrapper for execlineb arguments - # should be added (-P, -S, -s). - "-S0" # set $@ inside the execline script - "-W" # die on syntax error - "$script" # substituted by importas - ]; -}) diff --git a/nix/runExecline/tests.nix b/nix/runExecline/tests.nix deleted file mode 100644 index f82b54422..000000000 --- a/nix/runExecline/tests.nix +++ /dev/null @@ -1,117 +0,0 @@ -{ stdenv -, pkgs -, runExecline -, runExeclineLocal -, getBins -, writeScript - # https://www.mail-archive.com/skaware@list.skarnet.org/msg01256.html -, coreutils -}: - -let - - bins = getBins coreutils [ "mv" ] - // getBins pkgs.execline [ - "execlineb" - { use = "if"; as = "execlineIf"; } - "redirfd" - "importas" - ] - // getBins pkgs.s6-portable-utils [ - "s6-chmod" - "s6-grep" - "s6-touch" - "s6-cat" - "s6-test" - ]; - - # execline block of depth 1 - block = args: builtins.map (arg: " ${arg}") args ++ [ "" ]; - - # derivation that tests whether a given line exists - # in the given file. Does not use runExecline, because - # that should be tested after all. - fileHasLine = line: file: derivation { - name = "run-execline-test-file-${file.name}-has-line"; - inherit (stdenv) system; - builder = bins.execlineIf; - args = - (block [ - bins.redirfd - "-r" - "0" - file # read file to stdin - bins.s6-grep - "-F" - "-q" - line # and grep for the line - ]) - ++ [ - # if the block succeeded, touch $out - bins.importas - "-ui" - "out" - "out" - bins.s6-touch - "$out" - ]; - preferLocalBuild = true; - allowSubstitutes = false; - }; - - # basic test that touches out - basic = runExeclineLocal "run-execline-test-basic" - { } [ - "importas" - "-ui" - "out" - "out" - "${bins.s6-touch}" - "$out" - ]; - - # whether the stdin argument works as intended - stdin = fileHasLine "foo" (runExeclineLocal "run-execline-test-stdin" - { - stdin = "foo\nbar\nfoo"; - } [ - "importas" - "-ui" - "out" - "out" - # this pipes stdout of s6-cat to $out - # and s6-cat redirects from stdin to stdout - "redirfd" - "-w" - "1" - "$out" - bins.s6-cat - ]); - - - wrapWithVar = runExeclineLocal "run-execline-test-wrap-with-var" - { - builderWrapper = writeScript "var-wrapper" '' - #!${bins.execlineb} -S0 - export myvar myvalue $@ - ''; - } [ - "importas" - "-ui" - "v" - "myvar" - "if" - [ bins.s6-test "myvalue" "=" "$v" ] - "importas" - "out" - "out" - bins.s6-touch - "$out" - ]; - -in -[ - basic - stdin - wrapWithVar -] diff --git a/nix/runTestsuite/default.nix b/nix/runTestsuite/default.nix deleted file mode 100644 index 8b02ed86d..000000000 --- a/nix/runTestsuite/default.nix +++ /dev/null @@ -1,199 +0,0 @@ -{ lib, pkgs, depot, ... }: - -# Run a nix testsuite. -# -# The tests are simple assertions on the nix level, -# and can use derivation outputs if IfD is enabled. -# -# You build a testsuite by bundling assertions into -# “it”s and then bundling the “it”s into a testsuite. -# -# Running the testsuite will abort evaluation if -# any assertion fails. -# -# Example: -# -# runTestsuite "myFancyTestsuite" [ -# (it "does an assertion" [ -# (assertEq "42 is equal to 42" "42" "42") -# (assertEq "also 23" 23 23) -# ]) -# (it "frmbls the brlbr" [ -# (assertEq true false) -# ]) -# ] -# -# will fail the second it group because true is not false. - -let - inherit (depot.nix.yants) - sum - struct - string - any - defun - list - drv - bool - ; - - bins = depot.nix.getBins pkgs.coreutils [ "printf" ] - // depot.nix.getBins pkgs.s6-portable-utils [ "s6-touch" "s6-false" "s6-cat" ]; - - # Returns true if the given expression throws when `deepSeq`-ed - throws = expr: - !(builtins.tryEval (builtins.deepSeq expr { })).success; - - # rewrite the builtins.partition result - # to use `ok` and `err` instead of `right` and `wrong`. - partitionTests = pred: xs: - let res = builtins.partition pred xs; - in { - ok = res.right; - err = res.wrong; - }; - - AssertErrorContext = - sum "AssertErrorContext" { - not-equal = struct "not-equal" { - left = any; - right = any; - }; - should-throw = struct "should-throw" { - expr = any; - }; - unexpected-throw = struct "unexpected-throw" { }; - }; - - # The result of an assert, - # either it’s true (yep) or false (nope). - # If it's nope we return an additional context - # attribute which gives details on the failure - # depending on the type of assert performed. - AssertResult = - sum "AssertResult" { - yep = struct "yep" { - test = string; - }; - nope = struct "nope" { - test = string; - context = AssertErrorContext; - }; - }; - - # Result of an it. An it is a bunch of asserts - # bundled up with a good description of what is tested. - ItResult = - struct "ItResult" { - it-desc = string; - asserts = list AssertResult; - }; - - # If the given boolean is true return a positive AssertResult. - # If the given boolean is false return a negative AssertResult - # with the provided AssertErrorContext describing the failure. - # - # This function is intended as a generic assert to implement - # more assert types and is not exposed to the user. - assertBoolContext = defun [ AssertErrorContext string bool AssertResult ] - (context: desc: res: - if res - then { yep = { test = desc; }; } - else { - nope = { - test = desc; - inherit context; - }; - }); - - # assert that left and right values are equal - assertEq = defun [ string any any AssertResult ] - (desc: left: right: - let - context = { not-equal = { inherit left right; }; }; - in - assertBoolContext context desc (left == right)); - - # assert that the expression throws when `deepSeq`-ed - assertThrows = defun [ string any AssertResult ] - (desc: expr: - let - context = { should-throw = { inherit expr; }; }; - in - assertBoolContext context desc (throws expr)); - - # assert that the expression does not throw when `deepSeq`-ed - assertDoesNotThrow = defun [ string any AssertResult ] - (desc: expr: - assertBoolContext { unexpected-throw = { }; } desc (!(throws expr))); - - # Annotate a bunch of asserts with a descriptive name - it = desc: asserts: { - it-desc = desc; - inherit asserts; - }; - - # Run a bunch of its and check whether all asserts are yep. - # If not, abort evaluation with `throw` - # and print the result of the test suite. - # - # Takes a test suite name as first argument. - runTestsuite = defun [ string (list ItResult) drv ] - (name: itResults: - let - goodAss = ass: AssertResult.match ass { - yep = _: true; - nope = _: false; - }; - res = partitionTests - (it: - (partitionTests goodAss it.asserts).err == [ ] - ) - itResults; - prettyRes = lib.generators.toPretty { } res; - in - if res.err == [ ] - then - depot.nix.runExecline.local "testsuite-${name}-successful" { } [ - "importas" - "out" - "out" - # force derivation to rebuild if test case list changes - "ifelse" - [ bins.s6-false ] - [ - bins.printf - "" - (builtins.hashString "sha512" prettyRes) - ] - "if" - [ bins.printf "%s\n" "testsuite ${name} successful!" ] - bins.s6-touch - "$out" - ] - else - depot.nix.runExecline.local "testsuite-${name}-failed" - { - stdin = prettyRes + "\n"; - } [ - "importas" - "out" - "out" - "if" - [ bins.printf "%s\n" "testsuite ${name} failed!" ] - "if" - [ bins.s6-cat ] - "exit" - "1" - ]); - -in -{ - inherit - assertEq - assertThrows - assertDoesNotThrow - it - runTestsuite - ; -} diff --git a/nix/stateMonad/tests/default.nix b/nix/stateMonad/tests/default.nix deleted file mode 100644 index c3cb5c99b..000000000 --- a/nix/stateMonad/tests/default.nix +++ /dev/null @@ -1,110 +0,0 @@ -{ depot, ... }: - -let - inherit (depot.nix.runTestsuite) - runTestsuite - it - assertEq - ; - - inherit (depot.nix.stateMonad) - pure - run - join - fmap - bind - get - set - modify - after - for_ - getAttr - setAttr - modifyAttr - ; - - runStateIndependent = run (throw "This should never be evaluated!"); -in - -runTestsuite "stateMonad" [ - (it "behaves correctly independent of state" [ - (assertEq "pure" (runStateIndependent (pure 21)) 21) - (assertEq "join pure" (runStateIndependent (join (pure (pure 42)))) 42) - (assertEq "fmap pure" (runStateIndependent (fmap (builtins.mul 2) (pure 21))) 42) - (assertEq "bind pure" (runStateIndependent (bind (pure 12) (x: pure x))) 12) - ]) - (it "behaves correctly with an integer state" [ - (assertEq "get" (run 42 get) 42) - (assertEq "after set get" (run 21 (after (set 42) get)) 42) - (assertEq "after modify get" (run 21 (after (modify (builtins.mul 2)) get)) 42) - (assertEq "fmap get" (run 40 (fmap (builtins.add 2) get)) 42) - (assertEq "stateful sum list" - (run 0 (after - (for_ - [ - 15 - 12 - 10 - 5 - ] - (x: modify (builtins.add x))) - get)) - 42) - ]) - (it "behaves correctly with an attr set state" [ - (assertEq "getAttr" (run { foo = 42; } (getAttr "foo")) 42) - (assertEq "after setAttr getAttr" - (run { foo = 21; } (after (setAttr "foo" 42) (getAttr "foo"))) - 42) - (assertEq "after modifyAttr getAttr" - (run { foo = 10.5; } - (after - (modifyAttr "foo" (builtins.mul 4)) - (getAttr "foo"))) - 42) - (assertEq "fmap getAttr" - (run { foo = 21; } (fmap (builtins.mul 2) (getAttr "foo"))) - 42) - (assertEq "after setAttr to insert getAttr" - (run { } (after (setAttr "foo" 42) (getAttr "foo"))) - 42) - (assertEq "insert permutations" - (run - { - a = 2; - b = 3; - c = 5; - } - (after - (bind get - (state: - let - names = builtins.attrNames state; - in - for_ names (name1: - for_ names (name2: - # this is of course a bit silly, but making it more cumbersome - # makes sure the test exercises more of the code. - (bind (getAttr name1) - (value1: - (bind (getAttr name2) - (value2: - setAttr "${name1}_${name2}" (value1 * value2))))))))) - get)) - { - a = 2; - b = 3; - c = 5; - a_a = 4; - a_b = 6; - a_c = 10; - b_a = 6; - b_b = 9; - b_c = 15; - c_c = 25; - c_a = 10; - c_b = 15; - } - ) - ]) -] diff --git a/nix/tag/tests.nix b/nix/tag/tests.nix deleted file mode 100644 index e0085b483..000000000 --- a/nix/tag/tests.nix +++ /dev/null @@ -1,99 +0,0 @@ -{ depot, lib, verifyTag, discr, discrDef, match, matchLam }: - -let - inherit (depot.nix.runTestsuite) - runTestsuite - assertEq - assertThrows - it - ; - - isTag-test = it "checks whether something is a tag" [ - (assertEq "is Tag" - (verifyTag { foo = "bar"; }) - { - isTag = true; - name = "foo"; - val = "bar"; - errmsg = null; - }) - (assertEq "is not Tag" - (removeAttrs (verifyTag { foo = "bar"; baz = 42; }) [ "errmsg" ]) - { - isTag = false; - name = null; - val = null; - }) - ]; - - discr-test = it "can discr things" [ - (assertEq "id" - (discr [ - { a = lib.const true; } - ] "x") - { a = "x"; }) - (assertEq "bools here, ints there" - (discr [ - { bool = lib.isBool; } - { int = lib.isInt; } - ] 25) - { int = 25; }) - (assertEq "bools here, ints there 2" - (discr [ - { bool = lib.isBool; } - { int = lib.isInt; } - ] - true) - { bool = true; }) - (assertEq "fallback to default" - (discrDef "def" [ - { bool = lib.isBool; } - { int = lib.isInt; } - ] "foo") - { def = "foo"; }) - (assertThrows "throws failing to match" - (discr [ - { fish = x: x == 42; } - ] 21)) - ]; - - match-test = it "can match things" [ - (assertEq "match example" - ( - let - success = { res = 42; }; - failure = { err = "no answer"; }; - matcher = { - res = i: i + 1; - err = _: 0; - }; - in - { - one = match success matcher; - two = match failure matcher; - } - ) - { - one = 43; - two = 0; - }) - (assertEq "matchLam & pipe" - (lib.pipe { foo = 42; } [ - (matchLam { - foo = i: if i < 23 then { small = i; } else { big = i; }; - bar = _: { small = 5; }; - }) - (matchLam { - small = i: "yay it was small"; - big = i: "whoo it was big!"; - }) - ]) - "whoo it was big!") - ]; - -in -runTestsuite "tag" [ - isTag-test - discr-test - match-test -] diff --git a/nix/utils/tests/default.nix b/nix/utils/tests/default.nix deleted file mode 100644 index 32dc11f3a..000000000 --- a/nix/utils/tests/default.nix +++ /dev/null @@ -1,98 +0,0 @@ -{ depot, lib, ... }: - -let - inherit (depot.nix.runTestsuite) - runTestsuite - it - assertEq - assertThrows - assertDoesNotThrow - ; - - inherit (depot.nix.utils) - isDirectory - isRegularFile - isSymlink - storePathName - ; - - assertUtilsPred = msg: act: exp: [ - (assertDoesNotThrow "${msg} does not throw" act) - (assertEq msg (builtins.tryEval act).value exp) - ]; - - pathPredicates = it "judges paths correctly" (lib.flatten [ - # isDirectory - (assertUtilsPred "directory isDirectory" - (isDirectory ./directory) - true) - (assertUtilsPred "symlink not isDirectory" - (isDirectory ./symlink-directory) - false) - (assertUtilsPred "file not isDirectory" - (isDirectory ./directory/file) - false) - # isRegularFile - (assertUtilsPred "file isRegularFile" - (isRegularFile ./directory/file) - true) - (assertUtilsPred "symlink not isRegularFile" - (isRegularFile ./symlink-file) - false) - (assertUtilsPred "directory not isRegularFile" - (isRegularFile ./directory) - false) - # isSymlink - (assertUtilsPred "symlink to file isSymlink" - (isSymlink ./symlink-file) - true) - (assertUtilsPred "symlink to directory isSymlink" - (isSymlink ./symlink-directory) - true) - (assertUtilsPred "symlink to symlink isSymlink" - (isSymlink ./symlink-symlink-file) - true) - (assertUtilsPred "symlink to missing file isSymlink" - (isSymlink ./missing) - true) - (assertUtilsPred "directory not isSymlink" - (isSymlink ./directory) - false) - (assertUtilsPred "file not isSymlink" - (isSymlink ./directory/file) - false) - # missing files throw - (assertThrows "isDirectory throws on missing file" - (isDirectory ./does-not-exist)) - (assertThrows "isRegularFile throws on missing file" - (isRegularFile ./does-not-exist)) - (assertThrows "isSymlink throws on missing file" - (isSymlink ./does-not-exist)) - ]); - - magratheaStorePath = - builtins.unsafeDiscardStringContext depot.tools.magrathea.outPath; - - cleanedSource = lib.cleanSource ./.; - - storePathNameTests = it "correctly gets the basename of a store path" [ - (assertEq "base name of a derivation" - (storePathName depot.tools.magrathea) - depot.tools.magrathea.name) - (assertEq "base name of a store path string" - (storePathName magratheaStorePath) - depot.tools.magrathea.name) - (assertEq "base name of a path within a store path" - (storePathName "${magratheaStorePath}/bin/mg") "mg") - (assertEq "base name of a path" - (storePathName ../default.nix) "default.nix") - (assertEq "base name of a cleanSourced path" - (storePathName cleanedSource) - cleanedSource.name) - ]; -in - -runTestsuite "nix.utils" [ - pathPredicates - storePathNameTests -] diff --git a/nix/writeElispBin/default.nix b/nix/writeElispBin/default.nix deleted file mode 100644 index ee95b1c3a..000000000 --- a/nix/writeElispBin/default.nix +++ /dev/null @@ -1,20 +0,0 @@ -{ depot, pkgs, ... }: - -{ name, src, deps ? (_: [ ]), emacs ? pkgs.emacs-nox }: - -let - inherit (pkgs) emacsPackages emacsPackagesFor; - inherit (builtins) isString toFile; - - finalEmacs = (emacsPackagesFor emacs).emacsWithPackages deps; - - srcFile = - if isString src - then toFile "${name}.el" src - else src; - -in -depot.nix.writeScriptBin name '' - #!/bin/sh - ${finalEmacs}/bin/emacs --batch --no-site-file --script ${srcFile} $@ -'' diff --git a/nix/writeExecline/default.nix b/nix/writeExecline/default.nix deleted file mode 100644 index 5169b0138..000000000 --- a/nix/writeExecline/default.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ pkgs, depot, ... }: - -# Write an execline script, represented as nested nix lists. -# Everything is escaped correctly. -# https://skarnet.org/software/execline/ - -# TODO(Profpatsch) upstream into nixpkgs - -name: -{ - # "var": substitute readNArgs variables and start $@ - # from the (readNArgs+1)th argument - # "var-full": substitute readNArgs variables and start $@ from $0 - # "env": don’t substitute, set # and 0…n environment vaariables, where n=$# - # "none": don’t substitute or set any positional arguments - # "env-no-push": like "env", but bypass the push-phase. Not recommended. - argMode ? "var" -, # Number of arguments to be substituted as variables (passed to "var"/"-s" or "var-full"/"-S" - readNArgs ? 0 -, -}: -# Nested list of lists of commands. -# Inner lists are translated to execline blocks. -argList: - -let - env = - if argMode == "var" then "s${toString readNArgs}" - else if argMode == "var-full" then "S${toString readNArgs}" - else if argMode == "env" then "" - else if argMode == "none" then "P" - else if argMode == "env-no-push" then "p" - else abort ''"${toString argMode}" is not a valid argMode, use one of "var", "var-full", "env", "none", "env-no-push".''; - -in -depot.nix.writeScript name '' - #!${pkgs.execline}/bin/execlineb -W${env} - ${depot.nix.escapeExecline argList} -'' diff --git a/nix/writeScript/default.nix b/nix/writeScript/default.nix deleted file mode 100644 index 1f53b4e4f..000000000 --- a/nix/writeScript/default.nix +++ /dev/null @@ -1,35 +0,0 @@ -{ pkgs, depot, ... }: - -# Write the given string to $out -# and make it executable. - -let - bins = depot.nix.getBins pkgs.s6-portable-utils [ - "s6-cat" - "s6-chmod" - ]; - -in -name: -# string of the executable script that is put in $out -script: - -depot.nix.runExecline name -{ - stdin = script; - derivationArgs = { - preferLocalBuild = true; - allowSubstitutes = false; - }; -} [ - "importas" - "out" - "out" - # this pipes stdout of s6-cat to $out - # and s6-cat redirects from stdin to stdout - "if" - [ "redirfd" "-w" "1" "$out" bins.s6-cat ] - bins.s6-chmod - "0755" - "$out" -] diff --git a/nix/writeScriptBin/default.nix b/nix/writeScriptBin/default.nix deleted file mode 100644 index ed26cf197..000000000 --- a/nix/writeScriptBin/default.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ depot, ... }: - -# Like writeScript, -# but put the script into `$out/bin/${name}`. - -name: -script: - -depot.nix.binify { - exe = (depot.nix.writeScript name script); - inherit name; -} diff --git a/nix/writers/default.nix b/nix/writers/default.nix deleted file mode 100644 index e849f1cb0..000000000 --- a/nix/writers/default.nix +++ /dev/null @@ -1,112 +0,0 @@ -{ depot, pkgs, lib, ... }: - -let - bins = depot.nix.getBins pkgs.s6-portable-utils [ "s6-ln" "s6-ls" "s6-touch" ] - ; - - linkTo = name: path: depot.nix.runExecline.local name { } [ - "importas" - "out" - "out" - bins.s6-ln - "-s" - path - "$out" - ]; - - # Build a rust executable, $out is the executable. - rustSimple = args@{ name, ... }: src: - linkTo name "${rustSimpleBin args src}/bin/${name}"; - - # Like `rustSimple`, but put the binary in `$out/bin/`. - rustSimpleBin = - { name - , dependencies ? [ ] - , doCheck ? true - }: src: - (if doCheck then testRustSimple else pkgs.lib.id) - (pkgs.buildRustCrate ({ - pname = name; - version = "1.0.0"; - crateName = name; - crateBin = [ name ]; - dependencies = dependencies; - src = pkgs.runCommandLocal "write-main.rs" - { - src = src; - passAsFile = [ "src" ]; - } '' - mkdir -p $out/src/bin - cp "$srcPath" $out/src/bin/${name}.rs - find $out - ''; - })); - - # Build a rust library, that can be used as dependency to `rustSimple`. - # Wrapper around `pkgs.buildRustCrate`, takes all its arguments. - rustSimpleLib = - { name - , dependencies ? [ ] - , doCheck ? true - , - }: src: - (if doCheck then testRustSimple else pkgs.lib.id) - (pkgs.buildRustCrate ({ - pname = name; - version = "1.0.0"; - crateName = name; - dependencies = dependencies; - src = pkgs.runCommandLocal "write-lib.rs" - { - src = src; - passAsFile = [ "src" ]; - } '' - mkdir -p $out/src - cp "$srcPath" $out/src/lib.rs - find $out - ''; - })); - - /* Takes a `buildRustCrate` derivation as an input, - * builds it with `{ buildTests = true; }` and runs - * all tests found in its `tests` dir. If they are - * all successful, `$out` will point to the crate - * built with `{ buildTests = false; }`, otherwise - * it will fail to build. - * - * See also `nix.drvSeqL` which is used to implement - * this behavior. - */ - testRustSimple = rustDrv: - let - crate = buildTests: rustDrv.override { inherit buildTests; }; - tests = depot.nix.runExecline.local "${rustDrv.name}-tests-run" { } [ - "importas" - "out" - "out" - "if" - [ - "pipeline" - [ bins.s6-ls "${crate true}/tests" ] - "forstdin" - "-o0" - "test" - "importas" - "test" - "test" - "${crate true}/tests/$test" - ] - bins.s6-touch - "$out" - ]; - in - depot.nix.drvSeqL [ tests ] (crate false); - -in -{ - inherit - rustSimple - rustSimpleBin - rustSimpleLib - ; -} diff --git a/nix/writers/tests/rust.nix b/nix/writers/tests/rust.nix deleted file mode 100644 index 232a2dc60..000000000 --- a/nix/writers/tests/rust.nix +++ /dev/null @@ -1,76 +0,0 @@ -{ depot, pkgs, ... }: - -let - inherit (depot.nix.writers) - rustSimple - rustSimpleLib - rustSimpleBin - ; - - inherit (pkgs) - coreutils - ; - - run = drv: depot.nix.runExecline.local "run-${drv.name}" { } [ - "if" - [ drv ] - "importas" - "out" - "out" - "${coreutils}/bin/touch" - "$out" - ]; - - rustTransitiveLib = rustSimpleLib - { - name = "transitive"; - } '' - pub fn transitive(s: &str) -> String { - let mut new = s.to_string(); - new.push_str(" 1 2 3"); - new - } - - #[cfg(test)] - mod tests { - use super::*; - - #[test] - fn test_transitive() { - assert_eq!(transitive("foo").as_str(), "foo 1 2 3") - } - } - ''; - - rustTestLib = rustSimpleLib - { - name = "test_lib"; - dependencies = [ rustTransitiveLib ]; - } '' - extern crate transitive; - use transitive::{transitive}; - pub fn test() -> String { - transitive("test") - } - ''; - - rustWithLib = run (rustSimple - { - name = "rust-with-lib"; - dependencies = [ rustTestLib ]; - } '' - extern crate test_lib; - - fn main() { - assert_eq!(test_lib::test(), String::from("test 1 2 3")); - } - ''); - - -in -depot.nix.readTree.drvTargets { - inherit - rustTransitiveLib - rustWithLib - ; -} diff --git a/nix/yants/README.md b/nix/yants/README.md deleted file mode 100644 index 98e6642e2..000000000 --- a/nix/yants/README.md +++ /dev/null @@ -1,88 +0,0 @@ -yants -===== - -This is a tiny type-checker for data in Nix, written in Nix. - -# Features - -* Checking of primitive types (`int`, `string` etc.) -* Checking polymorphic types (`option`, `list`, `either`) -* Defining & checking struct/record types -* Defining & matching enum types -* Defining & matching sum types -* Defining function signatures (including curried functions) -* Types are composable! `option string`! `list (either int (option float))`! -* Type errors also compose! - -Currently lacking: - -* Any kind of inference -* Convenient syntax for attribute-set function signatures - -## Primitives & simple polymorphism - -![simple](/about/nix/yants/screenshots/simple.png) - -## Structs - -![structs](/about/nix/yants/screenshots/structs.png) - -## Nested structs! - -![nested structs](/about/nix/yants/screenshots/nested-structs.png) - -## Enums! - -![enums](/about/nix/yants/screenshots/enums.png) - -## Functions! - -![functions](/about/nix/yants/screenshots/functions.png) - -# Usage - -Yants can be imported from its `default.nix`. A single attribute (`lib`) can be -passed, which will otherwise be imported from ``. - -TIP: You do not need to clone the entire TVL repository to use Yants! -You can clone just this project through josh: `git clone -https://code.tvl.fyi/depot.git:/nix/yants.git` - -Examples for the most common import methods would be: - -1. Import into scope with `with`: - ```nix - with (import ./default.nix {}); - # ... Nix code that uses yants ... - ``` - -2. Import as a named variable: - ```nix - let yants = import ./default.nix {}; - in yants.string "foo" # or other uses ... - ```` - -3. Overlay into `pkgs.lib`: - ```nix - # wherever you import your package set (e.g. from ): - import { - overlays = [ - (self: super: { - lib = super.lib // { yants = import ./default.nix { inherit (super) lib; }; }; - }) - ]; - } - - # yants now lives at lib.yants, besides the other library functions! - ``` - -Please see my [Nix one-pager](https://github.com/tazjin/nix-1p) for more generic -information about the Nix language and what the above constructs mean. - -# Stability - -The current API of Yants is **not yet** considered stable, but it works fine and -should continue to do so even if used at an older version. - -Yants' tests use Nix versions above 2.2 - compatibility with older versions is -not guaranteed. diff --git a/nix/yants/default.nix b/nix/yants/default.nix deleted file mode 100644 index cb9fc0828..000000000 --- a/nix/yants/default.nix +++ /dev/null @@ -1,368 +0,0 @@ -# Copyright 2019 Google LLC -# SPDX-License-Identifier: Apache-2.0 -# -# Provides a "type-system" for Nix that provides various primitive & -# polymorphic types as well as the ability to define & check records. -# -# All types (should) compose as expected. - -{ lib ? (import { }).lib, ... }: - -with builtins; let - prettyPrint = lib.generators.toPretty { }; - - # typedef' :: struct { - # name = string; - # checkType = function; (a -> result) - # checkToBool = option function; (result -> bool) - # toError = option function; (a -> result -> string) - # def = option any; - # match = option function; - # } -> type - # -> (a -> b) - # -> (b -> bool) - # -> (a -> b -> string) - # -> type - # - # This function creates an attribute set that acts as a type. - # - # It receives a type name, a function that is used to perform a - # check on an arbitrary value, a function that can translate the - # return of that check to a boolean that informs whether the value - # is type-conformant, and a function that can construct error - # messages from the check result. - # - # This function is the low-level primitive used to create types. For - # many cases the higher-level 'typedef' function is more appropriate. - typedef' = - { name - , checkType - , checkToBool ? (result: result.ok) - , toError ? (_: result: result.err) - , def ? null - , match ? null - }: { - inherit name checkToBool toError; - - # check :: a -> bool - # - # This function is used to determine whether a given type is - # conformant. - check = value: checkToBool (checkType value); - - # checkType :: a -> struct { ok = bool; err = option string; } - # - # This function checks whether the passed value is type conformant - # and returns an optional type error string otherwise. - inherit checkType; - - # __functor :: a -> a - # - # This function checks whether the passed value is type conformant - # and throws an error if it is not. - # - # The name of this function is a special attribute in Nix that - # makes it possible to execute a type attribute set like a normal - # function. - __functor = self: value: - let result = self.checkType value; - in if checkToBool result then value - else throw (toError value result); - }; - - typeError = type: val: - "expected type '${type}', but value '${prettyPrint val}' is of type '${typeOf val}'"; - - # typedef :: string -> (a -> bool) -> type - # - # typedef is the simplified version of typedef' which uses a default - # error message constructor. - typedef = name: check: typedef' { - inherit name; - checkType = v: - let res = check v; - in { - ok = res; - } // (lib.optionalAttrs (!res) { - err = typeError name v; - }); - }; - - checkEach = name: t: l: foldl' - (acc: e: - let - res = t.checkType e; - isT = t.checkToBool res; - in - { - ok = acc.ok && isT; - err = - if isT - then acc.err - else acc.err + "${prettyPrint e}: ${t.toError e res}\n"; - }) - { ok = true; err = "expected type ${name}, but found:\n"; } - l; -in -lib.fix (self: { - # Primitive types - any = typedef "any" (_: true); - unit = typedef "unit" (v: v == { }); - int = typedef "int" isInt; - bool = typedef "bool" isBool; - float = typedef "float" isFloat; - string = typedef "string" isString; - path = typedef "path" (x: typeOf x == "path"); - drv = typedef "derivation" (x: isAttrs x && x ? "type" && x.type == "derivation"); - function = typedef "function" (x: isFunction x || (isAttrs x && x ? "__functor" - && isFunction x.__functor)); - - # Type for types themselves. Useful when defining polymorphic types. - type = typedef "type" (x: - isAttrs x - && hasAttr "name" x && self.string.check x.name - && hasAttr "checkType" x && self.function.check x.checkType - && hasAttr "checkToBool" x && self.function.check x.checkToBool - && hasAttr "toError" x && self.function.check x.toError - ); - - # Polymorphic types - option = t: typedef' rec { - name = "option<${t.name}>"; - checkType = v: - let res = t.checkType v; - in { - ok = isNull v || (self.type t).checkToBool res; - err = "expected type ${name}, but value does not conform to '${t.name}': " - + t.toError v res; - }; - }; - - eitherN = tn: typedef "either<${concatStringsSep ", " (map (x: x.name) tn)}>" - (x: any (t: (self.type t).check x) tn); - - either = t1: t2: self.eitherN [ t1 t2 ]; - - list = t: typedef' rec { - name = "list<${t.name}>"; - - checkType = v: - if isList v - then checkEach name (self.type t) v - else { - ok = false; - err = typeError name v; - }; - }; - - attrs = t: typedef' rec { - name = "attrs<${t.name}>"; - - checkType = v: - if isAttrs v - then checkEach name (self.type t) (attrValues v) - else { - ok = false; - err = typeError name v; - }; - }; - - # Structs / record types - # - # Checks that all fields match their declared types, no optional - # fields are missing and no unexpected fields occur in the struct. - # - # Anonymous structs are supported (e.g. for nesting) by omitting the - # name. - # - # TODO: Support open records? - struct = - # Struct checking is more involved than the simpler types above. - # To make the actual type definition more readable, several - # helpers are defined below. - let - # checkField checks an individual field of the struct against - # its definition and creates a typecheck result. These results - # are aggregated during the actual checking. - checkField = def: name: value: - let result = def.checkType value; in rec { - ok = def.checkToBool result; - err = - if !ok && isNull value - then "missing required ${def.name} field '${name}'\n" - else "field '${name}': ${def.toError value result}\n"; - }; - - # checkExtraneous determines whether a (closed) struct contains - # any fields that are not part of the definition. - checkExtraneous = def: has: acc: - if (length has) == 0 then acc - else if (hasAttr (head has) def) - then checkExtraneous def (tail has) acc - else - checkExtraneous def (tail has) { - ok = false; - err = acc.err + "unexpected struct field '${head has}'\n"; - }; - - # checkStruct combines all structure checks and creates one - # typecheck result from them - checkStruct = def: value: - let - init = { ok = true; err = ""; }; - extraneous = checkExtraneous def (attrNames value) init; - - checkedFields = map - (n: - let v = if hasAttr n value then value."${n}" else null; - in checkField def."${n}" n v) - (attrNames def); - - combined = foldl' - (acc: res: { - ok = acc.ok && res.ok; - err = if !res.ok then acc.err + res.err else acc.err; - }) - init - checkedFields; - in - { - ok = combined.ok && extraneous.ok; - err = combined.err + extraneous.err; - }; - - struct' = name: def: typedef' { - inherit name def; - checkType = value: - if isAttrs value - then (checkStruct (self.attrs self.type def) value) - else { ok = false; err = typeError name value; }; - - toError = _: result: "expected '${name}'-struct, but found:\n" + result.err; - }; - in - arg: if isString arg then (struct' arg) else (struct' "anon" arg); - - # Enums & pattern matching - enum = - let - plain = name: def: typedef' { - inherit name def; - - checkType = (x: isString x && elem x def); - checkToBool = x: x; - toError = value: _: "'${prettyPrint value} is not a member of enum ${name}"; - }; - enum' = name: def: lib.fix (e: (plain name def) // { - match = x: actions: deepSeq (map e (attrNames actions)) ( - let - actionKeys = attrNames actions; - missing = foldl' (m: k: if (elem k actionKeys) then m else m ++ [ k ]) [ ] def; - in - if (length missing) > 0 - then throw "Missing match action for members: ${prettyPrint missing}" - else actions."${e x}" - ); - }); - in - arg: if isString arg then (enum' arg) else (enum' "anon" arg); - - # Sum types - # - # The representation of a sum type is an attribute set with only one - # value, where the key of the value denotes the variant of the type. - sum = - let - plain = name: def: typedef' { - inherit name def; - checkType = (x: - let variant = elemAt (attrNames x) 0; - in if isAttrs x && length (attrNames x) == 1 && hasAttr variant def - then - let - t = def."${variant}"; - v = x."${variant}"; - res = t.checkType v; - in - if t.checkToBool res - then { ok = true; } - else { - ok = false; - err = "while checking '${name}' variant '${variant}': " - + t.toError v res; - } - else { ok = false; err = typeError name x; } - ); - }; - sum' = name: def: lib.fix (s: (plain name def) // { - match = x: actions: - let - variant = deepSeq (s x) (elemAt (attrNames x) 0); - actionKeys = attrNames actions; - defKeys = attrNames def; - missing = foldl' (m: k: if (elem k actionKeys) then m else m ++ [ k ]) [ ] defKeys; - in - if (length missing) > 0 - then throw "Missing match action for variants: ${prettyPrint missing}" - else actions."${variant}" x."${variant}"; - }); - in - arg: if isString arg then (sum' arg) else (sum' "anon" arg); - - # Typed function definitions - # - # These definitions wrap the supplied function in type-checking - # forms that are evaluated when the function is called. - # - # Note that typed functions themselves are not types and can not be - # used to check values for conformity. - defun = - let - mkFunc = sig: f: { - inherit sig; - __toString = self: foldl' (s: t: "${s} -> ${t.name}") - "λ :: ${(head self.sig).name}" - (tail self.sig); - __functor = _: f; - }; - - defun' = sig: func: - if length sig > 2 - then mkFunc sig (x: defun' (tail sig) (func ((head sig) x))) - else mkFunc sig (x: ((head (tail sig)) (func ((head sig) x)))); - - in - sig: func: - if length sig < 2 - then (throw "Signature must at least have two types (a -> b)") - else defun' sig func; - - # Restricting types - # - # `restrict` wraps a type `t`, and uses a predicate `pred` to further - # restrict the values, giving the restriction a descriptive `name`. - # - # First, the wrapped type definition is checked (e.g. int) and then the - # value is checked with the predicate, so the predicate can already - # depend on the value being of the wrapped type. - restrict = name: pred: t: - let restriction = "${t.name}[${name}]"; in typedef' { - name = restriction; - checkType = v: - let res = t.checkType v; - in - if !(t.checkToBool res) - then res - else - let - iok = pred v; - in - if isBool iok then { - ok = iok; - err = "${prettyPrint v} does not conform to restriction '${restriction}'"; - } else - # use throw here to avoid spamming the build log - throw "restriction '${restriction}' predicate returned unexpected value '${prettyPrint iok}' instead of boolean"; - }; - -}) diff --git a/nix/yants/screenshots/enums.png b/nix/yants/screenshots/enums.png deleted file mode 100644 index 71673e7ab..000000000 Binary files a/nix/yants/screenshots/enums.png and /dev/null differ diff --git a/nix/yants/screenshots/functions.png b/nix/yants/screenshots/functions.png deleted file mode 100644 index 30ed50f83..000000000 Binary files a/nix/yants/screenshots/functions.png and /dev/null differ diff --git a/nix/yants/screenshots/nested-structs.png b/nix/yants/screenshots/nested-structs.png deleted file mode 100644 index 6b03ed65c..000000000 Binary files a/nix/yants/screenshots/nested-structs.png and /dev/null differ diff --git a/nix/yants/screenshots/simple.png b/nix/yants/screenshots/simple.png deleted file mode 100644 index 05a302cc6..000000000 Binary files a/nix/yants/screenshots/simple.png and /dev/null differ diff --git a/nix/yants/screenshots/structs.png b/nix/yants/screenshots/structs.png deleted file mode 100644 index fcbcf6415..000000000 Binary files a/nix/yants/screenshots/structs.png and /dev/null differ diff --git a/nix/yants/tests/default.nix b/nix/yants/tests/default.nix deleted file mode 100644 index 0c7ec2418..000000000 --- a/nix/yants/tests/default.nix +++ /dev/null @@ -1,158 +0,0 @@ -{ depot, pkgs, ... }: - -with depot.nix.yants; - -# Note: Derivations are not included in the tests below as they cause -# issues with deepSeq. - -let - - inherit (depot.nix.runTestsuite) - runTestsuite - it - assertEq - assertThrows - assertDoesNotThrow - ; - - # this derivation won't throw if evaluated with deepSeq - # unlike most things even remotely related with nixpkgs - trivialDerivation = derivation { - name = "trivial-derivation"; - inherit (pkgs.stdenv) system; - builder = "/bin/sh"; - args = [ "-c" "echo hello > $out" ]; - }; - - testPrimitives = it "checks that all primitive types match" [ - (assertDoesNotThrow "unit type" (unit { })) - (assertDoesNotThrow "int type" (int 15)) - (assertDoesNotThrow "bool type" (bool false)) - (assertDoesNotThrow "float type" (float 13.37)) - (assertDoesNotThrow "string type" (string "Hello!")) - (assertDoesNotThrow "function type" (function (x: x * 2))) - (assertDoesNotThrow "path type" (path /nix)) - (assertDoesNotThrow "derivation type" (drv trivialDerivation)) - ]; - - testPoly = it "checks that polymorphic types work as intended" [ - (assertDoesNotThrow "option type" (option int null)) - (assertDoesNotThrow "list type" (list string [ "foo" "bar" ])) - (assertDoesNotThrow "either type" (either int float 42)) - ]; - - # Test that structures work as planned. - person = struct "person" { - name = string; - age = int; - - contact = option (struct { - email = string; - phone = option string; - }); - }; - - testStruct = it "checks that structures work as intended" [ - (assertDoesNotThrow "person struct" (person { - name = "Brynhjulf"; - age = 42; - contact.email = "brynhjulf@yants.nix"; - })) - ]; - - # Test enum definitions & matching - colour = enum "colour" [ "red" "blue" "green" ]; - colourMatcher = { - red = "It is in fact red!"; - blue = "It should not be blue!"; - green = "It should not be green!"; - }; - - testEnum = it "checks enum definitions and matching" [ - (assertEq "enum is matched correctly" - "It is in fact red!" - (colour.match "red" colourMatcher)) - (assertThrows "out of bounds enum fails" - (colour.match "alpha" (colourMatcher // { - alpha = "This should never happen"; - })) - ) - ]; - - # Test sum type definitions - creature = sum "creature" { - human = struct { - name = string; - age = option int; - }; - - pet = enum "pet" [ "dog" "lizard" "cat" ]; - }; - some-human = creature { - human = { - name = "Brynhjulf"; - age = 42; - }; - }; - - testSum = it "checks sum types definitions and matching" [ - (assertDoesNotThrow "creature sum type" some-human) - (assertEq "sum type is matched correctly" - "It's a human named Brynhjulf" - (creature.match some-human { - human = v: "It's a human named ${v.name}"; - pet = v: "It's not supposed to be a pet!"; - }) - ) - ]; - - # Test curried function definitions - func = defun [ string int string ] - (name: age: "${name} is ${toString age} years old"); - - testFunctions = it "checks function definitions" [ - (assertDoesNotThrow "function application" (func "Brynhjulf" 42)) - ]; - - # Test that all types are types. - assertIsType = name: t: - assertDoesNotThrow "${name} is a type" (type t); - testTypes = it "checks that all types are types" [ - (assertIsType "any" any) - (assertIsType "bool" bool) - (assertIsType "drv" drv) - (assertIsType "float" float) - (assertIsType "int" int) - (assertIsType "string" string) - (assertIsType "path" path) - - (assertIsType "attrs int" (attrs int)) - (assertIsType "eitherN [ ... ]" (eitherN [ int string bool ])) - (assertIsType "either int string" (either int string)) - (assertIsType "enum [ ... ]" (enum [ "foo" "bar" ])) - (assertIsType "list string" (list string)) - (assertIsType "option int" (option int)) - (assertIsType "option (list string)" (option (list string))) - (assertIsType "struct { ... }" (struct { a = int; b = option string; })) - (assertIsType "sum { ... }" (sum { a = int; b = option string; })) - ]; - - testRestrict = it "checks restrict types" [ - (assertDoesNotThrow "< 42" ((restrict "< 42" (i: i < 42) int) 25)) - (assertDoesNotThrow "list length < 3" - ((restrict "not too long" (l: builtins.length l < 3) (list int)) [ 1 2 ])) - (assertDoesNotThrow "list eq 5" - (list (restrict "eq 5" (v: v == 5) any) [ 5 5 5 ])) - ]; - -in -runTestsuite "yants" [ - testPrimitives - testPoly - testStruct - testEnum - testSum - testFunctions - testTypes - testRestrict -] diff --git a/ops/secrets/mkSecrets.nix b/ops/secrets/mkSecrets.nix index e22745b96..dc0ce153f 100644 --- a/ops/secrets/mkSecrets.nix +++ b/ops/secrets/mkSecrets.nix @@ -1,29 +1,12 @@ -# Expose secrets as part of the tree, making it possible to validate -# their paths at eval time. +# Expose secrets as part of the tree, exposing their paths at eval time. # # Note that encrypted secrets end up in the Nix store, but this is # fine since they're publicly available anyways. { depot, lib, ... }: -let - inherit (depot.nix.yants) - attrs - any - either - defun - list - path - restrict - string - struct - ; - ssh-pubkey = restrict "SSH pubkey" (lib.hasPrefix "ssh-") string; - age-pubkey = restrict "age pubkey" (lib.hasPrefix "age") string; - agenixSecret = struct "agenixSecret" { publicKeys = list (either age-pubkey ssh-pubkey); }; -in - -defun [ path (attrs agenixSecret) (attrs any) ] - (path: secrets: +( + path: secrets: depot.nix.readTree.drvTargets # Import each secret into the Nix store - (builtins.mapAttrs (name: _: "${path}/${name}") secrets)) + (builtins.mapAttrs (name: _: "${path}/${name}") secrets) +) diff --git a/ops/secrets/secrets.nix b/ops/secrets/secrets.nix index 54bd2774d..c8a447c49 100644 --- a/ops/secrets/secrets.nix +++ b/ops/secrets/secrets.nix @@ -1,3 +1,5 @@ +# This file is read by agenix standalone, to know which audiences to (re)encrypt secrets to. + let raito = [ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICaw9ihTG7ucB8P38XdalEWev8+q96e2yNm4B+/I9IJp" diff --git a/tools/eprintf.nix b/tools/eprintf.nix deleted file mode 100644 index 933d73ea7..000000000 --- a/tools/eprintf.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ depot, pkgs, ... }: - -let - bins = depot.nix.getBins pkgs.coreutils [ "printf" ]; - - # printf(1), but redirect to stderr -in -depot.nix.writeExecline "eprintf" { } [ - "fdmove" - "-c" - "1" - "2" - bins.printf - "$@" -] diff --git a/tools/rust-crates-advisory/default.nix b/tools/rust-crates-advisory/default.nix index da7eb7544..d85fc3ed0 100644 --- a/tools/rust-crates-advisory/default.nix +++ b/tools/rust-crates-advisory/default.nix @@ -1,14 +1,6 @@ { depot, pkgs, lib, ... }: let - - bins = - depot.nix.getBins pkgs.cargo-audit [ "cargo-audit" ] - // depot.nix.getBins pkgs.jq [ "jq" ] - // depot.nix.getBins pkgs.findutils [ "find" ] - // depot.nix.getBins pkgs.gnused [ "sed" ] - ; - our-crates = lib.filter (v: v ? outPath) (builtins.attrValues depot.third_party.rust-crates); @@ -36,10 +28,10 @@ let exit 100 fi - "${bins.cargo-audit}" audit --json --no-fetch \ + ${pkgs.cargo-audit}/bin/cargo-audit audit --json --no-fetch \ --db "${depot.third_party.rustsec-advisory-db}" \ --file "$2" \ - | "${bins.jq}" --raw-output --join-output \ + | ${pkgs.jq}/bin/jq --raw-output --join-output \ --from-file "${./format-audit-result.jq}" \ --arg maintainers "''${4:-}" \ --argjson checklist "''${3:-false}" \ @@ -56,9 +48,9 @@ let # Find prints the found lockfiles as \t\t\0 while IFS=$'\t' read -r -d $'\0' entryPoint dir lockFile; do - label="$(printf '%s' "$dir" | "${bins.sed}" "s|^$entryPoint|/|")" + label="$(printf '%s' "$dir" | ${pkgs.gnused}/bin/sed "s|^$entryPoint|/|")" "${lock-file-report}" "$label" "$lockFile" || status=1 - done < <("${bins.find}" "$root" -type f -name Cargo.lock -printf '%H\t%h\t%p\0' ) + done < <(${pkgs.findutils}/bin/find "$root" -type f -name Cargo.lock -printf '%H\t%h\t%p\0' ) exit $status '';