Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ tests/sources.sh
# Run the lib.filesystem tests
tests/filesystem.sh

# Run the lib.warnings tests
tests/warnings.sh

# Run the lib.path property tests
path/tests/prop.sh

Expand Down
7 changes: 6 additions & 1 deletion lib/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ let

# misc
asserts = callLibs ./asserts.nix;
warnings = callLibs ./warnings.nix;
debug = callLibs ./debug.nix;
misc = callLibs ./deprecated/misc.nix;

Expand Down Expand Up @@ -401,7 +402,7 @@ let
renameCrossIndexTo
mapCrossIndex
;
inherit (self.derivations) lazyDerivation optionalDrvAttr warnOnInstantiate;
inherit (self.derivations) lazyDerivation optionalDrvAttr;
inherit (self.generators) mkLuaInline;
inherit (self.meta)
addMetaAttrs
Expand Down Expand Up @@ -516,6 +517,10 @@ let
assertMsg
assertOneOf
;
inherit (self.warnings)
warnOnInstantiate
warnAlias
;
inherit (self.debug)
traceIf
traceVal
Expand Down
39 changes: 0 additions & 39 deletions lib/derivations.nix
Original file line number Diff line number Diff line change
Expand Up @@ -212,43 +212,4 @@ in
:::
*/
optionalDrvAttr = cond: value: if cond then value else null;

/**
Wrap a derivation such that instantiating it produces a warning.

All attributes will be wrapped with `lib.warn` except from `.meta`, `.name`,
and `.type` which are used by `nix search`, and `.outputName` which avoids
double warnings with `nix-instantiate` and `nix-build`.

# Inputs

`msg`
: The warning message to emit (via `lib.warn`).

`drv`
: The derivation to wrap.

# Examples
:::{.example}
## `lib.derivations.warnOnInstantiate` usage example

```nix
{
myPackage = warnOnInstantiate "myPackage has been renamed to my-package" my-package;
}
```

:::
*/
warnOnInstantiate =
msg: drv:
let
drvToWrap = removeAttrs drv [
"meta"
"name"
"type"
"outputName"
];
in
drv // mapAttrs (_: lib.warn msg) drvToWrap;
}
2 changes: 1 addition & 1 deletion lib/tests/misc.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
which is `throw`'s and `abort`'s, without error messages.

If you need to test error messages or more complex evaluations, see
`lib/tests/modules.sh`, `lib/tests/sources.sh` or `lib/tests/filesystem.sh` as examples.
`.sh` files in this directory as examples.

To run these tests:

Expand Down
3 changes: 3 additions & 0 deletions lib/tests/test-with-nix.nix
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ pkgs.runCommand "nixpkgs-lib-tests-nix-${nix.version}"
echo "Running lib/tests/network.sh"
TEST_LIB=$PWD/lib bash lib/tests/network.sh

echo "Running lib/tests/warnings.sh"
TEST_LIB=$PWD/lib bash lib/tests/warnings.sh

echo "Running lib/fileset/tests.sh"
TEST_LIB=$PWD/lib bash lib/fileset/tests.sh

Expand Down
32 changes: 32 additions & 0 deletions lib/tests/warnings.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Test cases for `lib.warnings.warnAlias`, see `lib/tests/warnings.sh` for more details
{
pkgs ? import <nixpkgs> { },
}:
let
lib = pkgs.lib;

# renames all attr keys to xDest, e.g. drv -> drvDest
renameDestinations =
destinations: lib.mapAttrs' (name: value: lib.nameValuePair (name + "Dest") value) destinations;
mkAliases =
destinations:
# create a lib.warnAlias for each attribute
(lib.mapAttrs (name: value: lib.warnAlias (name + " alias") value) destinations)
// (renameDestinations destinations);
in
(mkAliases {
drv = pkgs.hello;
attrs = {
a = "b";
};
list = [
1
2
3
];
arbitrary = "abc";
})
// rec {
func = (lib.warnAlias "func alias" (_: funcDest)) "123";
funcDest = "abc";
}
132 changes: 132 additions & 0 deletions lib/tests/warnings.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
#!/usr/bin/env bash

# Tests lib/warnings.nix
# Run:
# [nixpkgs]$ lib/tests/warnings.sh
# or:
# [nixpkgs]$ nix-build lib/tests/release.nix

set -euo pipefail
shopt -s inherit_errexit

die() {
printf ' test case failed: %s\n' "$1" >&2
printf '%s\n' "${@:2}" >&2
exit 1
}

if test -n "${TEST_LIB:-}"; then
NIX_PATH=nixpkgs="$(dirname "$TEST_LIB")"
else
NIX_PATH=nixpkgs="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.."; pwd)"
fi
export NIX_PATH

PREFIX_IMPORT="(import $NIX_PATH/lib/tests/warnings.nix { })"

firstLine() {
echo "$1" | head -n 1
}

# Completely tests a type for warnAlias function
testAliasCase() {
local attributeName=$1
echo "Testing attribute '$attributeName'..."

local eval_arg
# --eval on derivations tries to evaluate all attributes recursively, which
# always results in some kind of error, which is not what we want at all
if [ "$attributeName" != "drv" ]; then
eval_arg="1"
fi

# Evaluate attribute and checks for warning using regex
echo " Evaluating for warning..."
if result=$(
NIX_ABORT_ON_WARN=1 \
nix-instantiate ${eval_arg:+"--eval"} --strict \
--expr "$PREFIX_IMPORT.$attributeName" \
2>&1
); then
die "'$attributeName' did not produce warning, but it was expected to:" "$result"
fi

local expectedWarningRegex="^trace: evaluation warning: $attributeName alias$"
if [[ ! $(firstLine "$result") =~ $expectedWarningRegex ]]; then
die "Result is '$(firstLine "$result")', but '$expectedWarningRegex' was expected" "$result"
fi

# Evaluate attribute for value, and check it with the value it is aliasing to
# but do not error on warning
echo " Evaluating for value..."
if ! result_attr=$(
nix-instantiate ${eval_arg:+"--eval"} --strict \
--expr "$PREFIX_IMPORT.$attributeName" \
2>/dev/null
); then
die "'$attributeName' failed to evaluate for value, but it was expected to succeed" "$result"
fi
# Evaluating the original value to check it with alias
echo " Getting expected value..."
if ! expected_attr=$(
NIX_ABORT_ON_WARN=1 \
nix-instantiate ${eval_arg:+"--eval"} --strict \
--expr "$PREFIX_IMPORT.${attributeName}Dest" \
2>/dev/null
); then
die "'${attributeName}Dest' failed to evaluate with message, but it was expected to succeed" "$result"
fi

if [[ "$result_attr" != "$expected_attr" ]]; then
die "'$attributeName' expected to be '$expected_attr', but it is '$result_attr'"
fi
echo " Done testing $attributeName!"
}

expectSuccess() {
local expr=$1
local expectedResultRegex=$2
echo "Testing '$expr'..."
if ! result=$(
NIX_ABORT_ON_WARN=1 \
nix-instantiate --eval --strict \
--expr "with (import <nixpkgs> { }); $expr" \
2>&1
); then
die "'$expr' failed to evaluate, but it was expected to succeed:" "$result"
fi
if [[ ! $(firstLine "$result") =~ $expectedResultRegex ]]; then
die "Mismatched regex; expected '$expectedResultRegex' but result is:" "$result"
fi
}

expectFailure() {
local expr=$1
local expectedErrorRegex=$2
echo "Testing '$expr'..."
if result=$(
NIX_ABORT_ON_WARN=1 \
nix-instantiate --eval --strict \
--expr "with (import <nixpkgs> { }); $expr" \
2>&1
); then
die "'$expr' evaluated successfully, but it was expected to fail:" "$result"
fi
if [[ ! $(firstLine "$result") =~ $expectedErrorRegex ]]; then
die "Mismatched regex; expected '$expectedErrorRegex' but result is:" "$result"
fi
}

# tests for warnAlias
testAliasCase drv
testAliasCase attrs
testAliasCase func
testAliasCase list
testAliasCase arbitrary

# tests for warnOnInstantiate
expectFailure 'lib.warnOnInstantiate "error message" hello' '^trace: evaluation warning: error message$'
expectSuccess '(lib.warnOnInstantiate "error message" hello).meta' 'description = "Program that produces a familiar, friendly greeting";'
expectSuccess '(lib.warnOnInstantiate "error message" hello).name' '^"hello-2.12.2"$'
expectSuccess '(lib.warnOnInstantiate "error message" hello).type' '^"derivation"$'
expectSuccess '(lib.warnOnInstantiate "error message" hello).outputName' '^"out"$'
97 changes: 97 additions & 0 deletions lib/warnings.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{ lib }:

let
inherit (lib)
isAttrs
isDerivation
isFunction
isList
mapAttrs
removeAttrs
warn
;
in
rec {
/**
Raise warning on an arbitrary value, when it is accessed
and alias it to something else.

# Inputs

`msg`
: The warning message to emit (via `lib.warn`).

`v`
: The value to wrap. Can be derivation/attribute set/function/list.

# Examples
:::{.example}
## `lib.derivations.warnAlias` usage example

```nix
{
myFunc = warnAlias "myFunc has been renamed to my-func" my-func;
}
```

:::
*/
warnAlias =
msg: v:
if isDerivation v then
warnOnInstantiate msg v
else if isAttrs v then
mapAttrs (_: warn msg) v
else if isFunction v then
arg: warn msg (v arg)
else if isList v then
map (warn msg) v
else
# Can’t do better than this, and a `throw` would be more
# disruptive for users…
#
# `nix search` flags up warnings already, so hopefully this won’t
# make things much worse until we have proper CI for aliases,
# especially since aliases of paths and numbers are presumably
# not common.
warn msg v;

/**
Wrap a derivation such that instantiating it produces a warning.

All attributes will be wrapped with `lib.warn` except from `.meta`, `.name`,
and `.type` which are used by `nix search`, and `.outputName` which avoids
double warnings with `nix-instantiate` and `nix-build`.

# Inputs

`msg`
: The warning message to emit (via `lib.warn`).

`drv`
: The derivation to wrap.

# Examples
:::{.example}
## `lib.derivations.warnOnInstantiate` usage example

```nix
{
myPackage = warnOnInstantiate "myPackage has been renamed to my-package" my-package;
}
```

:::
*/
warnOnInstantiate =
msg: drv:
let
drvToWrap = removeAttrs drv [
"meta"
"name"
"type"
"outputName"
];
in
drv // mapAttrs (_: lib.warn msg) drvToWrap;
}
Loading
Loading