feat(nix/readTree): give better error message when not a function

When a file is added to the depot tree that is picked up by read-tree,
but it’s not a function like ({...}: {}), `readTree` will fail on the
function application, leading to a bad error message.

We can do slightly better, by checking the type and throwing a nicer
trace message.

`assertMsg` is copied from `nixpkgs/lib/assert.nix`, since at this
point we don’t have a reference to the lib.

There is another evaluation failure that can happen, which is when the
function we try to call does not have dots; however, nix does not
provide any inflection capabilies for checking whether a function
attrset is open (`builtins.functionArgs` only tells us the attrs it
mentions explicitly). Maybe the locality of the error could be
improved somehow.

Change-Id: Ibe38ce78bb56902075f7c31f2eeeb93485b34be3
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2469
Tested-by: BuildkiteCI
Reviewed-by: tazjin <mail@tazj.in>
This commit is contained in:
Profpatsch 2021-01-30 09:25:05 +01:00
parent 0f1a497361
commit 83e81def23
4 changed files with 27 additions and 1 deletions

View file

@ -16,6 +16,11 @@ let
readDir readDir
substring; substring;
assertMsg = pred: msg:
if pred
then true
else builtins.trace msg false;
argsWithPath = args: parts: argsWithPath = args: parts:
let meta.locatedAt = parts; let meta.locatedAt = parts;
in meta // (if isAttrs args then args else args meta); in meta // (if isAttrs args then args else args meta);
@ -38,7 +43,14 @@ let
# The marker is added to every set that was imported directly by # The marker is added to every set that was imported directly by
# readTree. # readTree.
importWithMark = args: path: parts: importWithMark = args: path: parts:
let imported = import path (argsWithPath args parts); let
importedFile = import path;
pathType = builtins.typeOf importedFile;
imported =
assert assertMsg
(pathType == "lambda")
"readTree: trying to import ${toString path}, but its a ${pathType}, you need to make it a function like { depot, pkgs, ... }";
importedFile (argsWithPath args parts);
in if (isAttrs imported) in if (isAttrs imported)
then imported // (marker parts) then imported // (marker parts)
else imported; else imported;

View file

@ -79,7 +79,17 @@ let
(import ./test-tree-traversal/default-nix/can-be-drv/default.nix {})) (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 {} ./test-wrong-not-a-function).not-a-function)
# cant test for that, assertThrows cant catch this error
# (assertThrows "this file is a function but doesnt have dots"
# (depot.nix.readTree {} ./test-wrong-no-dots).no-dots-in-function)
];
in runTestsuite "readTree" [ in runTestsuite "readTree" [
example example
traversal-logic traversal-logic
wrong
] ]

View file

@ -0,0 +1,3 @@
{}:
"This is a function, but readTree wants to pass a bunch of arguments, and not having dots means we depend on exactly which arguments."

View file

@ -0,0 +1 @@
"This file needs to be a function, otherwise readTree doesnt like it!"