diff options
author | John Ericson <John.Ericson@Obsidian.Systems> | 2023-10-05 12:12:18 -0400 |
---|---|---|
committer | John Ericson <John.Ericson@Obsidian.Systems> | 2023-12-01 12:06:43 -0500 |
commit | 30dcc19d1f30fc203be460134c4578509cce704f (patch) | |
tree | 6cc32609b9984a2c4d5ecc0cac5cf30609e208b9 /tests/functional/flakes | |
parent | 72425212657d795dc215b334b7c8c8cd36d06b72 (diff) |
Put functional tests in `tests/functional`
I think it is bad for these reasons when `tests/` contains a mix of
functional and integration tests
- Concepts is harder to understand, the documentation makes a good
unit vs functional vs integration distinction, but when the
integration tests are just two subdirs within `tests/` this is not
clear.
- Source filtering in the `flake.nix` is more complex. We need to
filter out some of the dirs from `tests/`, rather than simply pick
the dirs we want and take all of them. This is a good sign the
structure of what we are trying to do is not matching the structure
of the files.
With this change we have a clean:
```shell-session
$ git show 'HEAD:tests'
tree HEAD:tests
functional/
installer/
nixos/
```
(cherry picked from commit 68c81c737571794f7246db53fb4774e94fcf4b7e)
Diffstat (limited to 'tests/functional/flakes')
-rw-r--r-- | tests/functional/flakes/absolute-paths.sh | 17 | ||||
-rw-r--r-- | tests/functional/flakes/build-paths.sh | 96 | ||||
-rw-r--r-- | tests/functional/flakes/bundle.sh | 32 | ||||
-rw-r--r-- | tests/functional/flakes/check.sh | 91 | ||||
-rw-r--r-- | tests/functional/flakes/circular.sh | 49 | ||||
-rw-r--r-- | tests/functional/flakes/common.sh | 70 | ||||
-rw-r--r-- | tests/functional/flakes/config.sh | 42 | ||||
-rw-r--r-- | tests/functional/flakes/flake-in-submodule.sh | 52 | ||||
-rw-r--r-- | tests/functional/flakes/flakes.sh | 511 | ||||
-rw-r--r-- | tests/functional/flakes/follow-paths.sh | 232 | ||||
-rw-r--r-- | tests/functional/flakes/init.sh | 87 | ||||
-rw-r--r-- | tests/functional/flakes/inputs.sh | 80 | ||||
-rw-r--r-- | tests/functional/flakes/mercurial.sh | 43 | ||||
-rw-r--r-- | tests/functional/flakes/run.sh | 29 | ||||
-rw-r--r-- | tests/functional/flakes/search-root.sh | 50 | ||||
-rw-r--r-- | tests/functional/flakes/show.sh | 87 | ||||
-rw-r--r-- | tests/functional/flakes/unlocked-override.sh | 30 |
17 files changed, 1598 insertions, 0 deletions
diff --git a/tests/functional/flakes/absolute-paths.sh b/tests/functional/flakes/absolute-paths.sh new file mode 100644 index 000000000..e7bfba12d --- /dev/null +++ b/tests/functional/flakes/absolute-paths.sh @@ -0,0 +1,17 @@ +source ./common.sh + +requireGit + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 + +createGitRepo $flake1Dir +cat > $flake1Dir/flake.nix <<EOF +{ + outputs = { self }: { x = builtins.readFile $(pwd)/absolute-paths.sh; }; +} +EOF +git -C $flake1Dir add flake.nix +git -C $flake1Dir commit -m Initial + +nix eval --impure --json $flake1Dir#x diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh new file mode 100644 index 000000000..ff012e1b3 --- /dev/null +++ b/tests/functional/flakes/build-paths.sh @@ -0,0 +1,96 @@ +source ./common.sh + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 + +mkdir -p $flake1Dir $flake2Dir + +writeSimpleFlake $flake2Dir +tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake2 +hash=$(nix hash path $flake2Dir) + +dep=$(nix store add-path ./common.sh) + +cat > $flake1Dir/flake.nix <<EOF +{ + inputs.flake2.url = "file://$TEST_ROOT/flake.tar.gz"; + + outputs = { self, flake2 }: { + + a1 = builtins.fetchTarball { + #type = "tarball"; + url = "file://$TEST_ROOT/flake.tar.gz"; + sha256 = "$hash"; + }; + + a2 = ./foo; + + a3 = ./.; + + a4 = self.outPath; + + # FIXME + a5 = self; + + a6 = flake2.outPath; + + # FIXME + a7 = "\${flake2}/config.nix"; + + # This is only allowed in impure mode. + a8 = builtins.storePath $dep; + + a9 = "$dep"; + + drvCall = with import ./config.nix; mkDerivation { + name = "simple"; + builder = ./simple.builder.sh; + PATH = ""; + goodPath = path; + }; + + a10 = builtins.unsafeDiscardOutputDependency self.drvCall.drvPath; + + a11 = self.drvCall.drvPath; + + a12 = self.drvCall.outPath; + + a13 = "\${self.drvCall.drvPath}\${self.drvCall.outPath}"; + }; +} +EOF + +cp ../simple.nix ../simple.builder.sh ../config.nix $flake1Dir/ + +echo bar > $flake1Dir/foo + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a1 +[[ -e $TEST_ROOT/result/simple.nix ]] + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a2 +[[ $(cat $TEST_ROOT/result) = bar ]] + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a3 + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a4 + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a6 +[[ -e $TEST_ROOT/result/simple.nix ]] + +nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a8 +diff common.sh $TEST_ROOT/result + +expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a9 \ + | grepQuiet "has 0 entries in its context. It should only have exactly one entry" + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a10 +[[ $(readlink -e $TEST_ROOT/result) = *simple.drv ]] + +expectStderr 1 nix build --json --out-link $TEST_ROOT/result $flake1Dir#a11 \ + | grepQuiet "has a context which refers to a complete source and binary closure" + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a12 +[[ -e $TEST_ROOT/result/hello ]] + +expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a13 \ + | grepQuiet "has 2 entries in its context. It should only have exactly one entry" diff --git a/tests/functional/flakes/bundle.sh b/tests/functional/flakes/bundle.sh new file mode 100644 index 000000000..67bbb05ac --- /dev/null +++ b/tests/functional/flakes/bundle.sh @@ -0,0 +1,32 @@ +source common.sh + +cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME + +cd $TEST_HOME + +cat <<EOF > flake.nix +{ + outputs = {self}: { + bundlers.$system = rec { + simple = drv: + if drv?type && drv.type == "derivation" + then drv + else self.packages.$system.default; + default = simple; + }; + packages.$system.default = import ./simple.nix; + apps.$system.default = { + type = "app"; + program = "\${import ./simple.nix}/hello"; + }; + }; +} +EOF + +nix build .# +nix bundle --bundler .# .# +nix bundle --bundler .#bundlers.$system.default .#packages.$system.default +nix bundle --bundler .#bundlers.$system.simple .#packages.$system.default + +nix bundle --bundler .#bundlers.$system.default .#apps.$system.default +nix bundle --bundler .#bundlers.$system.simple .#apps.$system.default diff --git a/tests/functional/flakes/check.sh b/tests/functional/flakes/check.sh new file mode 100644 index 000000000..0433e5335 --- /dev/null +++ b/tests/functional/flakes/check.sh @@ -0,0 +1,91 @@ +source common.sh + +flakeDir=$TEST_ROOT/flake3 +mkdir -p $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + overlay = final: prev: { + }; + }; +} +EOF + +nix flake check $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + overlay = finalll: prev: { + }; + }; +} +EOF + +(! nix flake check $flakeDir) + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self, ... }: { + overlays.x86_64-linux.foo = final: prev: { + }; + }; +} +EOF + +checkRes=$(nix flake check $flakeDir 2>&1 && fail "nix flake check --all-systems should have failed" || true) +echo "$checkRes" | grepQuiet "error: overlay is not a function, but a set instead" + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + nixosModules.foo = { + a.b.c = 123; + foo = true; + }; + }; +} +EOF + +nix flake check $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + nixosModules.foo = assert false; { + a.b.c = 123; + foo = true; + }; + }; +} +EOF + +(! nix flake check $flakeDir) + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + nixosModule = { config, pkgs, ... }: { + a.b.c = 123; + }; + }; +} +EOF + +nix flake check $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + packages.system-1.default = "foo"; + packages.system-2.default = "bar"; + }; +} +EOF + +nix flake check $flakeDir + +checkRes=$(nix flake check --all-systems --keep-going $flakeDir 2>&1 && fail "nix flake check --all-systems should have failed" || true) +echo "$checkRes" | grepQuiet "packages.system-1.default" +echo "$checkRes" | grepQuiet "packages.system-2.default" diff --git a/tests/functional/flakes/circular.sh b/tests/functional/flakes/circular.sh new file mode 100644 index 000000000..09cd02edf --- /dev/null +++ b/tests/functional/flakes/circular.sh @@ -0,0 +1,49 @@ +# Test circular flake dependencies. +source ./common.sh + +requireGit + +flakeA=$TEST_ROOT/flakeA +flakeB=$TEST_ROOT/flakeB + +createGitRepo $flakeA +createGitRepo $flakeB + +cat > $flakeA/flake.nix <<EOF +{ + inputs.b.url = git+file://$flakeB; + inputs.b.inputs.a.follows = "/"; + + outputs = { self, b }: { + foo = 123 + b.bar; + xyzzy = 1000; + }; +} +EOF + +git -C $flakeA add flake.nix + +cat > $flakeB/flake.nix <<EOF +{ + inputs.a.url = git+file://$flakeA; + + outputs = { self, a }: { + bar = 456 + a.xyzzy; + }; +} +EOF + +git -C $flakeB add flake.nix +git -C $flakeB commit -a -m 'Foo' + +[[ $(nix eval $flakeA#foo) = 1579 ]] +[[ $(nix eval $flakeA#foo) = 1579 ]] + +sed -i $flakeB/flake.nix -e 's/456/789/' +git -C $flakeB commit -a -m 'Foo' + +[[ $(nix eval --update-input b $flakeA#foo) = 1912 ]] + +# Test list-inputs with circular dependencies +nix flake metadata $flakeA + diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh new file mode 100644 index 000000000..427abcdde --- /dev/null +++ b/tests/functional/flakes/common.sh @@ -0,0 +1,70 @@ +source ../common.sh + +registry=$TEST_ROOT/registry.json + +writeSimpleFlake() { + local flakeDir="$1" + cat > $flakeDir/flake.nix <<EOF +{ + description = "Bla bla"; + + outputs = inputs: rec { + packages.$system = rec { + foo = import ./simple.nix; + default = foo; + }; + packages.someOtherSystem = rec { + foo = import ./simple.nix; + default = foo; + }; + + # To test "nix flake init". + legacyPackages.$system.hello = import ./simple.nix; + }; +} +EOF + + cp ../simple.nix ../simple.builder.sh ../config.nix $flakeDir/ +} + +createSimpleGitFlake() { + local flakeDir="$1" + writeSimpleFlake $flakeDir + git -C $flakeDir add flake.nix simple.nix simple.builder.sh config.nix + git -C $flakeDir commit -m 'Initial' +} + +writeDependentFlake() { + local flakeDir="$1" + cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self, flake1 }: { + packages.$system.default = flake1.packages.$system.default; + expr = assert builtins.pathExists ./flake.lock; 123; + }; +} +EOF +} + +writeTrivialFlake() { + local flakeDir="$1" + cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + expr = 123; + }; +} +EOF +} + +createGitRepo() { + local repo="$1" + local extraArgs="${2-}" + + rm -rf $repo $repo.tmp + mkdir -p $repo + + git -C $repo init $extraArgs + git -C $repo config user.email "foobar@example.com" + git -C $repo config user.name "Foobar" +} diff --git a/tests/functional/flakes/config.sh b/tests/functional/flakes/config.sh new file mode 100644 index 000000000..d1941a6be --- /dev/null +++ b/tests/functional/flakes/config.sh @@ -0,0 +1,42 @@ +source common.sh + +cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME + +cd $TEST_HOME + +rm -f post-hook-ran +cat <<EOF > echoing-post-hook.sh +#!/bin/sh + +echo "ThePostHookRan as \$0" > $PWD/post-hook-ran +EOF +chmod +x echoing-post-hook.sh + +cat <<EOF > flake.nix +{ + nixConfig.post-build-hook = ./echoing-post-hook.sh; + nixConfig.allow-dirty = false; # See #5621 + + outputs = a: { + packages.$system.default = import ./simple.nix; + }; +} +EOF + +# Without --accept-flake-config, the post hook should not run. +nix build < /dev/null +(! [[ -f post-hook-ran ]]) +clearStore + +nix build --accept-flake-config +test -f post-hook-ran || fail "The post hook should have ran" + +# Make sure that the path to the post hook doesn’t change if we change +# something in the flake. +# Otherwise the user would have to re-validate the setting each time. +mv post-hook-ran previous-post-hook-run +echo "# Dummy comment" >> flake.nix +clearStore +nix build --accept-flake-config +diff -q post-hook-ran previous-post-hook-run || \ + fail "Both post hook runs should report the same filename" diff --git a/tests/functional/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh new file mode 100644 index 000000000..21a4b52de --- /dev/null +++ b/tests/functional/flakes/flake-in-submodule.sh @@ -0,0 +1,52 @@ +source common.sh + +# Tests that: +# - flake.nix may reside inside of a git submodule +# - the flake can access content outside of the submodule +# +# rootRepo +# ├── root.nix +# └── submodule +# ├── flake.nix +# └── sub.nix + + +requireGit + +clearStore + +# Submodules can't be fetched locally by default. +# See fetchGitSubmodules.sh +export XDG_CONFIG_HOME=$TEST_HOME/.config +git config --global protocol.file.allow always + + +rootRepo=$TEST_ROOT/rootRepo +subRepo=$TEST_ROOT/submodule + + +createGitRepo $subRepo +cat > $subRepo/flake.nix <<EOF +{ + outputs = { self }: { + sub = import ./sub.nix; + root = import ../root.nix; + }; +} +EOF +echo '"expression in submodule"' > $subRepo/sub.nix +git -C $subRepo add flake.nix sub.nix +git -C $subRepo commit -m Initial + +createGitRepo $rootRepo + +git -C $rootRepo submodule init +git -C $rootRepo submodule add $subRepo submodule +echo '"expression in root repo"' > $rootRepo/root.nix +git -C $rootRepo add root.nix +git -C $rootRepo commit -m "Add root.nix" + +# Flake can live inside a submodule and can be accessed via ?dir=submodule +[[ $(nix eval --json git+file://$rootRepo\?submodules=1\&dir=submodule#sub ) = '"expression in submodule"' ]] +# The flake can access content outside of the submodule +[[ $(nix eval --json git+file://$rootRepo\?submodules=1\&dir=submodule#root ) = '"expression in root repo"' ]] diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh new file mode 100644 index 000000000..128f759ea --- /dev/null +++ b/tests/functional/flakes/flakes.sh @@ -0,0 +1,511 @@ +source ./common.sh + +requireGit + +clearStore +rm -rf $TEST_HOME/.cache $TEST_HOME/.config + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 +flake3Dir=$TEST_ROOT/flake3 +flake5Dir=$TEST_ROOT/flake5 +flake7Dir=$TEST_ROOT/flake7 +nonFlakeDir=$TEST_ROOT/nonFlake +badFlakeDir=$TEST_ROOT/badFlake +flakeGitBare=$TEST_ROOT/flakeGitBare + +for repo in $flake1Dir $flake2Dir $flake3Dir $flake7Dir $nonFlakeDir; do + # Give one repo a non-main initial branch. + extraArgs= + if [[ $repo == $flake2Dir ]]; then + extraArgs="--initial-branch=main" + fi + + createGitRepo "$repo" "$extraArgs" +done + +createSimpleGitFlake $flake1Dir + +cat > $flake2Dir/flake.nix <<EOF +{ + description = "Fnord"; + + outputs = { self, flake1 }: rec { + packages.$system.bar = flake1.packages.$system.foo; + }; +} +EOF + +git -C $flake2Dir add flake.nix +git -C $flake2Dir commit -m 'Initial' + +cat > $flake3Dir/flake.nix <<EOF +{ + description = "Fnord"; + + outputs = { self, flake2 }: rec { + packages.$system.xyzzy = flake2.packages.$system.bar; + + checks = { + xyzzy = packages.$system.xyzzy; + }; + }; +} +EOF + +cat > $flake3Dir/default.nix <<EOF +{ x = 123; } +EOF + +git -C $flake3Dir add flake.nix default.nix +git -C $flake3Dir commit -m 'Initial' + +cat > $nonFlakeDir/README.md <<EOF +FNORD +EOF + +git -C $nonFlakeDir add README.md +git -C $nonFlakeDir commit -m 'Initial' + +# Construct a custom registry, additionally test the --registry flag +nix registry add --registry $registry flake1 git+file://$flake1Dir +nix registry add --registry $registry flake2 git+file://$flake2Dir +nix registry add --registry $registry flake3 git+file://$flake3Dir +nix registry add --registry $registry flake4 flake3 +nix registry add --registry $registry nixpkgs flake1 + +# Test 'nix registry list'. +[[ $(nix registry list | wc -l) == 5 ]] +nix registry list | grep '^global' +nix registry list | grepInverse '^user' # nothing in user registry + +# Test 'nix flake metadata'. +nix flake metadata flake1 +nix flake metadata flake1 | grepQuiet 'Locked URL:.*flake1.*' + +# Test 'nix flake metadata' on a local flake. +(cd $flake1Dir && nix flake metadata) | grepQuiet 'URL:.*flake1.*' +(cd $flake1Dir && nix flake metadata .) | grepQuiet 'URL:.*flake1.*' +nix flake metadata $flake1Dir | grepQuiet 'URL:.*flake1.*' + +# Test 'nix flake metadata --json'. +json=$(nix flake metadata flake1 --json | jq .) +[[ $(echo "$json" | jq -r .description) = 'Bla bla' ]] +[[ -d $(echo "$json" | jq -r .path) ]] +[[ $(echo "$json" | jq -r .lastModified) = $(git -C $flake1Dir log -n1 --format=%ct) ]] +hash1=$(echo "$json" | jq -r .revision) + +echo foo > $flake1Dir/foo +git -C $flake1Dir add $flake1Dir/foo +[[ $(nix flake metadata flake1 --json --refresh | jq -r .dirtyRevision) == "$hash1-dirty" ]] + +echo -n '# foo' >> $flake1Dir/flake.nix +flake1OriginalCommit=$(git -C $flake1Dir rev-parse HEAD) +git -C $flake1Dir commit -a -m 'Foo' +flake1NewCommit=$(git -C $flake1Dir rev-parse HEAD) +hash2=$(nix flake metadata flake1 --json --refresh | jq -r .revision) +[[ $(nix flake metadata flake1 --json --refresh | jq -r .dirtyRevision) == "null" ]] +[[ $hash1 != $hash2 ]] + +# Test 'nix build' on a flake. +nix build -o $TEST_ROOT/result flake1#foo +[[ -e $TEST_ROOT/result/hello ]] + +# Test packages.default. +nix build -o $TEST_ROOT/result flake1 +[[ -e $TEST_ROOT/result/hello ]] + +nix build -o $TEST_ROOT/result $flake1Dir +nix build -o $TEST_ROOT/result git+file://$flake1Dir + +# Check that store symlinks inside a flake are not interpreted as flakes. +nix build -o $flake1Dir/result git+file://$flake1Dir +nix path-info $flake1Dir/result + +# 'getFlake' on an unlocked flakeref should fail in pure mode, but +# succeed in impure mode. +(! nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default") +nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default" --impure + +# 'getFlake' on a locked flakeref should succeed even in pure mode. +nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"git+file://$flake1Dir?rev=$hash2\").packages.$system.default" + +# Building a flake with an unlocked dependency should fail in pure mode. +(! nix build -o $TEST_ROOT/result flake2#bar --no-registries) +(! nix build -o $TEST_ROOT/result flake2#bar --no-use-registries) +(! nix eval --expr "builtins.getFlake \"$flake2Dir\"") + +# But should succeed in impure mode. +(! nix build -o $TEST_ROOT/result flake2#bar --impure) +nix build -o $TEST_ROOT/result flake2#bar --impure --no-write-lock-file +nix eval --expr "builtins.getFlake \"$flake2Dir\"" --impure + +# Building a local flake with an unlocked dependency should fail with --no-update-lock-file. +expect 1 nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes' + +# But it should succeed without that flag. +nix build -o $TEST_ROOT/result $flake2Dir#bar --no-write-lock-file +expect 1 nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes' +nix build -o $TEST_ROOT/result $flake2Dir#bar --commit-lock-file +[[ -e $flake2Dir/flake.lock ]] +[[ -z $(git -C $flake2Dir diff main || echo failed) ]] + +# Rerunning the build should not change the lockfile. +nix build -o $TEST_ROOT/result $flake2Dir#bar +[[ -z $(git -C $flake2Dir diff main || echo failed) ]] + +# Building with a lockfile should not require a fetch of the registry. +nix build -o $TEST_ROOT/result --flake-registry file:///no-registry.json $flake2Dir#bar --refresh +nix build -o $TEST_ROOT/result --no-registries $flake2Dir#bar --refresh +nix build -o $TEST_ROOT/result --no-use-registries $flake2Dir#bar --refresh + +# Updating the flake should not change the lockfile. +nix flake lock $flake2Dir +[[ -z $(git -C $flake2Dir diff main || echo failed) ]] + +# Now we should be able to build the flake in pure mode. +nix build -o $TEST_ROOT/result flake2#bar + +# Or without a registry. +nix build -o $TEST_ROOT/result --no-registries git+file://$flake2Dir#bar --refresh +nix build -o $TEST_ROOT/result --no-use-registries git+file://$flake2Dir#bar --refresh + +# Test whether indirect dependencies work. +nix build -o $TEST_ROOT/result $flake3Dir#xyzzy +git -C $flake3Dir add flake.lock + +# Add dependency to flake3. +rm $flake3Dir/flake.nix + +cat > $flake3Dir/flake.nix <<EOF +{ + description = "Fnord"; + + outputs = { self, flake1, flake2 }: rec { + packages.$system.xyzzy = flake2.packages.$system.bar; + packages.$system."sth sth" = flake1.packages.$system.foo; + }; +} +EOF + +git -C $flake3Dir add flake.nix +git -C $flake3Dir commit -m 'Update flake.nix' + +# Check whether `nix build` works with an incomplete lockfile +nix build -o $TEST_ROOT/result $flake3Dir#"sth sth" +nix build -o $TEST_ROOT/result $flake3Dir#"sth%20sth" + +# Check whether it saved the lockfile +[[ -n $(git -C $flake3Dir diff master) ]] + +git -C $flake3Dir add flake.lock + +git -C $flake3Dir commit -m 'Add lockfile' + +# Test whether registry caching works. +nix registry list --flake-registry file://$registry | grepQuiet flake3 +mv $registry $registry.tmp +nix store gc +nix registry list --flake-registry file://$registry --refresh | grepQuiet flake3 +mv $registry.tmp $registry + +# Test whether flakes are registered as GC roots for offline use. +# FIXME: use tarballs rather than git. +rm -rf $TEST_HOME/.cache +nix store gc # get rid of copies in the store to ensure they get fetched to our git cache +_NIX_FORCE_HTTP=1 nix build -o $TEST_ROOT/result git+file://$flake2Dir#bar +mv $flake1Dir $flake1Dir.tmp +mv $flake2Dir $flake2Dir.tmp +nix store gc +_NIX_FORCE_HTTP=1 nix build -o $TEST_ROOT/result git+file://$flake2Dir#bar +_NIX_FORCE_HTTP=1 nix build -o $TEST_ROOT/result git+file://$flake2Dir#bar --refresh +mv $flake1Dir.tmp $flake1Dir +mv $flake2Dir.tmp $flake2Dir + +# Add nonFlakeInputs to flake3. +rm $flake3Dir/flake.nix + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs = { + flake1 = {}; + flake2 = {}; + nonFlake = { + url = git+file://$nonFlakeDir; + flake = false; + }; + nonFlakeFile = { + url = path://$nonFlakeDir/README.md; + flake = false; + }; + nonFlakeFile2 = { + url = "$nonFlakeDir/README.md"; + flake = false; + }; + }; + + description = "Fnord"; + + outputs = inputs: rec { + packages.$system.xyzzy = inputs.flake2.packages.$system.bar; + packages.$system.sth = inputs.flake1.packages.$system.foo; + packages.$system.fnord = + with import ./config.nix; + mkDerivation { + inherit system; + name = "fnord"; + dummy = builtins.readFile (builtins.path { name = "source"; path = ./.; filter = path: type: baseNameOf path == "config.nix"; } + "/config.nix"); + dummy2 = builtins.readFile (builtins.path { name = "source"; path = inputs.flake1; filter = path: type: baseNameOf path == "simple.nix"; } + "/simple.nix"); + buildCommand = '' + cat \${inputs.nonFlake}/README.md > \$out + [[ \$(cat \${inputs.nonFlake}/README.md) = \$(cat \${inputs.nonFlakeFile}) ]] + [[ \${inputs.nonFlakeFile} = \${inputs.nonFlakeFile2} ]] + ''; + }; + }; +} +EOF + +cp ../config.nix $flake3Dir + +git -C $flake3Dir add flake.nix config.nix +git -C $flake3Dir commit -m 'Add nonFlakeInputs' + +# Check whether `nix build` works with a lockfile which is missing a +# nonFlakeInputs. +nix build -o $TEST_ROOT/result $flake3Dir#sth --commit-lock-file + +nix build -o $TEST_ROOT/result flake3#fnord +[[ $(cat $TEST_ROOT/result) = FNORD ]] + +# Check whether flake input fetching is lazy: flake3#sth does not +# depend on flake2, so this shouldn't fail. +rm -rf $TEST_HOME/.cache +clearStore +mv $flake2Dir $flake2Dir.tmp +mv $nonFlakeDir $nonFlakeDir.tmp +nix build -o $TEST_ROOT/result flake3#sth +(! nix build -o $TEST_ROOT/result flake3#xyzzy) +(! nix build -o $TEST_ROOT/result flake3#fnord) +mv $flake2Dir.tmp $flake2Dir +mv $nonFlakeDir.tmp $nonFlakeDir +nix build -o $TEST_ROOT/result flake3#xyzzy flake3#fnord + +# Test doing multiple `lookupFlake`s +nix build -o $TEST_ROOT/result flake4#xyzzy + +# Test 'nix flake update' and --override-flake. +nix flake lock $flake3Dir +[[ -z $(git -C $flake3Dir diff master || echo failed) ]] + +nix flake update $flake3Dir --override-flake flake2 nixpkgs +[[ ! -z $(git -C $flake3Dir diff master || echo failed) ]] + +# Make branch "removeXyzzy" where flake3 doesn't have xyzzy anymore +git -C $flake3Dir checkout -b removeXyzzy +rm $flake3Dir/flake.nix + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs = { + nonFlake = { + url = "$nonFlakeDir"; + flake = false; + }; + }; + + description = "Fnord"; + + outputs = { self, flake1, flake2, nonFlake }: rec { + packages.$system.sth = flake1.packages.$system.foo; + packages.$system.fnord = + with import ./config.nix; + mkDerivation { + inherit system; + name = "fnord"; + buildCommand = '' + cat \${nonFlake}/README.md > \$out + ''; + }; + }; +} +EOF +nix flake lock $flake3Dir +git -C $flake3Dir add flake.nix flake.lock +git -C $flake3Dir commit -m 'Remove packages.xyzzy' +git -C $flake3Dir checkout master + +# Test whether fuzzy-matching works for registry entries. +(! nix build -o $TEST_ROOT/result flake4/removeXyzzy#xyzzy) +nix build -o $TEST_ROOT/result flake4/removeXyzzy#sth + +# Testing the nix CLI +nix registry add flake1 flake3 +[[ $(nix registry list | wc -l) == 6 ]] +nix registry pin flake1 +[[ $(nix registry list | wc -l) == 6 ]] +nix registry pin flake1 flake3 +[[ $(nix registry list | wc -l) == 6 ]] +nix registry remove flake1 +[[ $(nix registry list | wc -l) == 5 ]] + +# Test 'nix registry list' with a disabled global registry. +nix registry add user-flake1 git+file://$flake1Dir +nix registry add user-flake2 git+file://$flake2Dir +[[ $(nix --flake-registry "" registry list | wc -l) == 2 ]] +nix --flake-registry "" registry list | grepQuietInverse '^global' # nothing in global registry +nix --flake-registry "" registry list | grepQuiet '^user' +nix registry remove user-flake1 +nix registry remove user-flake2 +[[ $(nix registry list | wc -l) == 5 ]] + +# Test 'nix flake clone'. +rm -rf $TEST_ROOT/flake1-v2 +nix flake clone flake1 --dest $TEST_ROOT/flake1-v2 +[ -e $TEST_ROOT/flake1-v2/flake.nix ] + +# Test 'follows' inputs. +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.foo = { + type = "indirect"; + id = "flake1"; + }; + inputs.bar.follows = "foo"; + + outputs = { self, foo, bar }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq -c .nodes.root.inputs.bar $flake3Dir/flake.lock) = '["foo"]' ]] + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.bar.follows = "flake2/flake1"; + + outputs = { self, flake2, bar }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq -c .nodes.root.inputs.bar $flake3Dir/flake.lock) = '["flake2","flake1"]' ]] + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.bar.follows = "flake2"; + + outputs = { self, flake2, bar }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq -c .nodes.root.inputs.bar $flake3Dir/flake.lock) = '["flake2"]' ]] + +# Test overriding inputs of inputs. +writeTrivialFlake $flake7Dir +git -C $flake7Dir add flake.nix +git -C $flake7Dir commit -m 'Initial' + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.flake2.inputs.flake1 = { + type = "git"; + url = file://$flake7Dir; + }; + + outputs = { self, flake2 }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq .nodes.flake1.locked.url $flake3Dir/flake.lock) =~ flake7 ]] + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.flake2.inputs.flake1.follows = "foo"; + inputs.foo.url = git+file://$flake7Dir; + + outputs = { self, flake2 }: { + }; +} +EOF + +nix flake update $flake3Dir +[[ $(jq -c .nodes.flake2.inputs.flake1 $flake3Dir/flake.lock) =~ '["foo"]' ]] +[[ $(jq .nodes.foo.locked.url $flake3Dir/flake.lock) =~ flake7 ]] + +# Test git+file with bare repo. +rm -rf $flakeGitBare +git clone --bare $flake1Dir $flakeGitBare +nix build -o $TEST_ROOT/result git+file://$flakeGitBare + +# Test path flakes. +mkdir -p $flake5Dir +writeDependentFlake $flake5Dir +nix flake lock path://$flake5Dir + +# Test tarball flakes. +tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake5 + +nix build -o $TEST_ROOT/result file://$TEST_ROOT/flake.tar.gz + +# Building with a tarball URL containing a SRI hash should also work. +url=$(nix flake metadata --json file://$TEST_ROOT/flake.tar.gz | jq -r .url) +[[ $url =~ sha256- ]] + +nix build -o $TEST_ROOT/result $url + +# Building with an incorrect SRI hash should fail. +expectStderr 102 nix build -o $TEST_ROOT/result "file://$TEST_ROOT/flake.tar.gz?narHash=sha256-qQ2Zz4DNHViCUrp6gTS7EE4+RMqFQtUfWF2UNUtJKS0=" | grep 'NAR hash mismatch' + +# Test --override-input. +git -C $flake3Dir reset --hard +nix flake lock $flake3Dir --override-input flake2/flake1 file://$TEST_ROOT/flake.tar.gz -vvvvv +[[ $(jq .nodes.flake1_2.locked.url $flake3Dir/flake.lock) =~ flake.tar.gz ]] + +nix flake lock $flake3Dir --override-input flake2/flake1 flake1 +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash2 ]] + +nix flake lock $flake3Dir --override-input flake2/flake1 flake1/master/$hash1 +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash1 ]] + +# Test --update-input. +nix flake lock $flake3Dir +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) = $hash1 ]] + +nix flake lock $flake3Dir --update-input flake2/flake1 +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash2 ]] + +# Test 'nix flake metadata --json'. +nix flake metadata $flake3Dir --json | jq . + +# Test flake in store does not evaluate. +rm -rf $badFlakeDir +mkdir $badFlakeDir +echo INVALID > $badFlakeDir/flake.nix +nix store delete $(nix store add-path $badFlakeDir) + +[[ $(nix path-info $(nix store add-path $flake1Dir)) =~ flake1 ]] +[[ $(nix path-info path:$(nix store add-path $flake1Dir)) =~ simple ]] + +# Test fetching flakerefs in the legacy CLI. +[[ $(nix-instantiate --eval flake:flake3 -A x) = 123 ]] +[[ $(nix-instantiate --eval flake:git+file://$flake3Dir -A x) = 123 ]] +[[ $(nix-instantiate -I flake3=flake:flake3 --eval '<flake3>' -A x) = 123 ]] +[[ $(NIX_PATH=flake3=flake:flake3 nix-instantiate --eval '<flake3>' -A x) = 123 ]] + +# Test alternate lockfile paths. +nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2.lock +cmp $flake2Dir/flake.lock $TEST_ROOT/flake2.lock >/dev/null # lockfiles should be identical, since we're referencing flake2's original one + +nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2-overridden.lock --override-input flake1 git+file://$flake1Dir?rev=$flake1OriginalCommit +expectStderr 1 cmp $flake2Dir/flake.lock $TEST_ROOT/flake2-overridden.lock +nix flake metadata $flake2Dir --reference-lock-file $TEST_ROOT/flake2-overridden.lock | grepQuiet $flake1OriginalCommit + +# reference-lock-file can only be used if allow-dirty is set. +expectStderr 1 nix flake metadata $flake2Dir --no-allow-dirty --reference-lock-file $TEST_ROOT/flake2-overridden.lock diff --git a/tests/functional/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh new file mode 100644 index 000000000..dc97027ac --- /dev/null +++ b/tests/functional/flakes/follow-paths.sh @@ -0,0 +1,232 @@ +source ./common.sh + +requireGit + +flakeFollowsA=$TEST_ROOT/follows/flakeA +flakeFollowsB=$TEST_ROOT/follows/flakeA/flakeB +flakeFollowsC=$TEST_ROOT/follows/flakeA/flakeB/flakeC +flakeFollowsD=$TEST_ROOT/follows/flakeA/flakeD +flakeFollowsE=$TEST_ROOT/follows/flakeA/flakeE + +# Test following path flakerefs. +createGitRepo $flakeFollowsA +mkdir -p $flakeFollowsB +mkdir -p $flakeFollowsC +mkdir -p $flakeFollowsD +mkdir -p $flakeFollowsE + +cat > $flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs = { + B = { + url = "path:./flakeB"; + inputs.foobar.follows = "foobar"; + }; + + foobar.url = "path:$flakeFollowsA/flakeE"; + }; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsB/flake.nix <<EOF +{ + description = "Flake B"; + inputs = { + foobar.url = "path:$flakeFollowsA/flakeE"; + goodoo.follows = "C/goodoo"; + C = { + url = "path:./flakeC"; + inputs.foobar.follows = "foobar"; + }; + }; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsC/flake.nix <<EOF +{ + description = "Flake C"; + inputs = { + foobar.url = "path:$flakeFollowsA/flakeE"; + goodoo.follows = "foobar"; + }; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsD/flake.nix <<EOF +{ + description = "Flake D"; + inputs = {}; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsE/flake.nix <<EOF +{ + description = "Flake E"; + inputs = {}; + outputs = { ... }: {}; +} +EOF + +git -C $flakeFollowsA add flake.nix flakeB/flake.nix \ + flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix + +nix flake metadata $flakeFollowsA + +nix flake update $flakeFollowsA + +nix flake lock $flakeFollowsA + +oldLock="$(cat "$flakeFollowsA/flake.lock")" + +# Ensure that locking twice doesn't change anything + +nix flake lock $flakeFollowsA + +newLock="$(cat "$flakeFollowsA/flake.lock")" + +diff <(echo "$newLock") <(echo "$oldLock") + +[[ $(jq -c .nodes.B.inputs.C $flakeFollowsA/flake.lock) = '"C"' ]] +[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '["foobar"]' ]] +[[ $(jq -c .nodes.C.inputs.foobar $flakeFollowsA/flake.lock) = '["B","foobar"]' ]] + +# Ensure removing follows from flake.nix removes them from the lockfile + +cat > $flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs = { + B = { + url = "path:./flakeB"; + }; + D.url = "path:./flakeD"; + }; + outputs = { ... }: {}; +} +EOF + +nix flake lock $flakeFollowsA + +[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '"foobar"' ]] +jq -r -c '.nodes | keys | .[]' $flakeFollowsA/flake.lock | grep "^foobar$" + +# Ensure a relative path is not allowed to go outside the store path +cat > $flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs = { + B.url = "path:../flakeB"; + }; + outputs = { ... }: {}; +} +EOF + +git -C $flakeFollowsA add flake.nix + +expect 1 nix flake lock $flakeFollowsA 2>&1 | grep 'points outside' + +# Non-existant follows should print a warning. +cat >$flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs.B = { + url = "path:./flakeB"; + inputs.invalid.follows = "D"; + inputs.invalid2.url = "path:./flakeD"; + }; + inputs.D.url = "path:./flakeD"; + outputs = { ... }: {}; +} +EOF + +git -C $flakeFollowsA add flake.nix + +nix flake lock "$flakeFollowsA" 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'" +nix flake lock "$flakeFollowsA" 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'" + +# Now test follow path overloading +# This tests a lockfile checking regression https://github.com/NixOS/nix/pull/8819 +# +# We construct the following graph, where p->q means p has input q. +# A double edge means that the edge gets overridden using `follows`. +# +# A +# / \ +# / \ +# v v +# B ==> C --- follows declared in A +# \\ / +# \\/ --- follows declared in B +# v +# D +# +# The message was +# error: input 'B/D' follows a non-existent input 'B/C/D' +# +# Note that for `B` to resolve its follow for `D`, it needs `C/D`, for which it needs to resolve the follow on `C` first. +flakeFollowsOverloadA="$TEST_ROOT/follows/overload/flakeA" +flakeFollowsOverloadB="$TEST_ROOT/follows/overload/flakeA/flakeB" +flakeFollowsOverloadC="$TEST_ROOT/follows/overload/flakeA/flakeB/flakeC" +flakeFollowsOverloadD="$TEST_ROOT/follows/overload/flakeA/flakeB/flakeC/flakeD" + +# Test following path flakerefs. +createGitRepo "$flakeFollowsOverloadA" +mkdir -p "$flakeFollowsOverloadB" +mkdir -p "$flakeFollowsOverloadC" +mkdir -p "$flakeFollowsOverloadD" + +cat > "$flakeFollowsOverloadD/flake.nix" <<EOF +{ + description = "Flake D"; + inputs = {}; + outputs = { ... }: {}; +} +EOF + +cat > "$flakeFollowsOverloadC/flake.nix" <<EOF +{ + description = "Flake C"; + inputs.D.url = "path:./flakeD"; + outputs = { ... }: {}; +} +EOF + +cat > "$flakeFollowsOverloadB/flake.nix" <<EOF +{ + description = "Flake B"; + inputs = { + C = { + url = "path:./flakeC"; + }; + D.follows = "C/D"; + }; + outputs = { ... }: {}; +} +EOF + +# input B/D should be able to be found... +cat > "$flakeFollowsOverloadA/flake.nix" <<EOF +{ + description = "Flake A"; + inputs = { + B = { + url = "path:./flakeB"; + inputs.C.follows = "C"; + }; + C.url = "path:./flakeB/flakeC"; + }; + outputs = { ... }: {}; +} +EOF + +git -C "$flakeFollowsOverloadA" add flake.nix flakeB/flake.nix \ + flakeB/flakeC/flake.nix flakeB/flakeC/flakeD/flake.nix + +nix flake metadata "$flakeFollowsOverloadA" +nix flake update "$flakeFollowsOverloadA" +nix flake lock "$flakeFollowsOverloadA" diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh new file mode 100644 index 000000000..2d4c77ba1 --- /dev/null +++ b/tests/functional/flakes/init.sh @@ -0,0 +1,87 @@ +source ./common.sh + +requireGit + +templatesDir=$TEST_ROOT/templates +flakeDir=$TEST_ROOT/flake +nixpkgsDir=$TEST_ROOT/nixpkgs + +nix registry add --registry $registry templates git+file://$templatesDir +nix registry add --registry $registry nixpkgs git+file://$nixpkgsDir + +createGitRepo $nixpkgsDir +createSimpleGitFlake $nixpkgsDir + +# Test 'nix flake init'. +createGitRepo $templatesDir + +cat > $templatesDir/flake.nix <<EOF +{ + description = "Some templates"; + + outputs = { self }: { + templates = rec { + trivial = { + path = ./trivial; + description = "A trivial flake"; + welcomeText = '' + Welcome to my trivial flake + ''; + }; + default = trivial; + }; + }; +} +EOF + +mkdir $templatesDir/trivial + +cat > $templatesDir/trivial/flake.nix <<EOF +{ + description = "A flake for building Hello World"; + + outputs = { self, nixpkgs }: { + packages.$system = rec { + hello = nixpkgs.legacyPackages.$system.hello; + default = hello; + }; + }; +} +EOF +echo a > $templatesDir/trivial/a +echo b > $templatesDir/trivial/b + +git -C $templatesDir add flake.nix trivial/ +git -C $templatesDir commit -m 'Initial' + +nix flake check templates +nix flake show templates +nix flake show templates --json | jq + +createGitRepo $flakeDir +(cd $flakeDir && nix flake init) +(cd $flakeDir && nix flake init) # check idempotence +git -C $flakeDir add flake.nix +nix flake check $flakeDir +nix flake show $flakeDir +nix flake show $flakeDir --json | jq +git -C $flakeDir commit -a -m 'Initial' + +# Test 'nix flake init' with benign conflicts +createGitRepo "$flakeDir" +echo a > $flakeDir/a +(cd $flakeDir && nix flake init) # check idempotence + +# Test 'nix flake init' with conflicts +createGitRepo "$flakeDir" +echo b > $flakeDir/a +pushd $flakeDir +(! nix flake init) |& grep "refusing to overwrite existing file '$flakeDir/a'" +popd +git -C $flakeDir commit -a -m 'Changed' + +# Test 'nix flake new'. +rm -rf $flakeDir +nix flake new -t templates#trivial $flakeDir +nix flake new -t templates#trivial $flakeDir # check idempotence +nix flake check $flakeDir diff --git a/tests/functional/flakes/inputs.sh b/tests/functional/flakes/inputs.sh new file mode 100644 index 000000000..80620488a --- /dev/null +++ b/tests/functional/flakes/inputs.sh @@ -0,0 +1,80 @@ +source ./common.sh + +requireGit + + +test_subdir_self_path() { + baseDir=$TEST_ROOT/$RANDOM + flakeDir=$baseDir/b-low + mkdir -p $flakeDir + writeSimpleFlake $baseDir + writeSimpleFlake $flakeDir + + echo all good > $flakeDir/message + cat > $flakeDir/flake.nix <<EOF +{ + outputs = inputs: rec { + packages.$system = rec { + default = + assert builtins.readFile ./message == "all good\n"; + assert builtins.readFile (inputs.self + "/message") == "all good\n"; + import ./simple.nix; + }; + }; +} +EOF + ( + nix build $baseDir?dir=b-low --no-link + ) +} +test_subdir_self_path + + +test_git_subdir_self_path() { + repoDir=$TEST_ROOT/repo-$RANDOM + createGitRepo $repoDir + flakeDir=$repoDir/b-low + mkdir -p $flakeDir + writeSimpleFlake $repoDir + writeSimpleFlake $flakeDir + + echo all good > $flakeDir/message + cat > $flakeDir/flake.nix <<EOF +{ + outputs = inputs: rec { + packages.$system = rec { + default = + assert builtins.readFile ./message == "all good\n"; + assert builtins.readFile (inputs.self + "/message") == "all good\n"; + assert inputs.self.outPath == inputs.self.sourceInfo.outPath + "/b-low"; + import ./simple.nix; + }; + }; +} +EOF + ( + cd $flakeDir + git add . + git commit -m init + # nix build + ) + + clientDir=$TEST_ROOT/client-$RANDOM + mkdir -p $clientDir + cat > $clientDir/flake.nix <<EOF +{ + inputs.inp = { + type = "git"; + url = "file://$repoDir"; + dir = "b-low"; + }; + + outputs = inputs: rec { + packages = inputs.inp.packages; + }; +} +EOF + nix build $clientDir --no-link + +} +test_git_subdir_self_path diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh new file mode 100644 index 000000000..0622c79b7 --- /dev/null +++ b/tests/functional/flakes/mercurial.sh @@ -0,0 +1,43 @@ +source ./common.sh + +[[ $(type -p hq) ]] || skipTest "Mercurial not installed" + +flake1Dir=$TEST_ROOT/flake-hg1 +mkdir -p $flake1Dir +writeSimpleFlake $flake1Dir +hg init $flake1Dir + +nix registry add --registry $registry flake1 hg+file://$flake1Dir + +flake2Dir=$TEST_ROOT/flake-hg2 +mkdir -p $flake2Dir +writeDependentFlake $flake2Dir +hg init $flake2Dir + +hg add $flake1Dir/* +hg commit --config ui.username=foobar@example.org $flake1Dir -m 'Initial commit' + +hg add $flake2Dir/flake.nix +hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Initial commit' + +nix build -o $TEST_ROOT/result hg+file://$flake2Dir +[[ -e $TEST_ROOT/result/hello ]] + +(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) + +nix eval hg+file://$flake2Dir#expr + +nix eval hg+file://$flake2Dir#expr + +(! nix eval hg+file://$flake2Dir#expr --no-allow-dirty) + +(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) + +hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Add lock file' + +nix flake metadata --json hg+file://$flake2Dir --refresh | jq -e -r .revision +nix flake metadata --json hg+file://$flake2Dir +[[ $(nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revCount) = 1 ]] + +nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-registries --no-allow-dirty +nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-use-registries --no-allow-dirty diff --git a/tests/functional/flakes/run.sh b/tests/functional/flakes/run.sh new file mode 100644 index 000000000..9fa51d1c7 --- /dev/null +++ b/tests/functional/flakes/run.sh @@ -0,0 +1,29 @@ +source ../common.sh + +clearStore +rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local +cp ../shell-hello.nix ../config.nix $TEST_HOME +cd $TEST_HOME + +cat <<EOF > flake.nix +{ + outputs = {self}: { + packages.$system.pkgAsPkg = (import ./shell-hello.nix).hello; + packages.$system.appAsApp = self.packages.$system.appAsApp; + + apps.$system.pkgAsApp = self.packages.$system.pkgAsPkg; + apps.$system.appAsApp = { + type = "app"; + program = "\${(import ./shell-hello.nix).hello}/bin/hello"; + }; + }; +} +EOF +nix run --no-write-lock-file .#appAsApp +nix run --no-write-lock-file .#pkgAsPkg + +! nix run --no-write-lock-file .#pkgAsApp || fail "'nix run' shouldn’t accept an 'app' defined under 'packages'" +! nix run --no-write-lock-file .#appAsPkg || fail "elements of 'apps' should be of type 'app'" + +clearStore + diff --git a/tests/functional/flakes/search-root.sh b/tests/functional/flakes/search-root.sh new file mode 100644 index 000000000..d8586dc8a --- /dev/null +++ b/tests/functional/flakes/search-root.sh @@ -0,0 +1,50 @@ +source common.sh + +clearStore + +writeSimpleFlake $TEST_HOME +cd $TEST_HOME +mkdir -p foo/subdir + +echo '{ outputs = _: {}; }' > foo/flake.nix +cat <<EOF > flake.nix +{ + inputs.foo.url = "$PWD/foo"; + outputs = a: { + packages.$system = rec { + test = import ./simple.nix; + default = test; + }; + }; +} +EOF +mkdir subdir +pushd subdir + +success=("" . .# .#test ../subdir ../subdir#test "$PWD") +failure=("path:$PWD") + +for i in "${success[@]}"; do + nix build $i || fail "flake should be found by searching up directories" +done + +for i in "${failure[@]}"; do + ! nix build $i || fail "flake should not search up directories when using 'path:'" +done + +popd + +nix build --override-input foo . || fail "flake should search up directories when not an installable" + +sed "s,$PWD/foo,$PWD/foo/subdir,g" -i flake.nix +! nix build || fail "flake should not search upwards when part of inputs" + +if [[ -n $(type -p git) ]]; then + pushd subdir + git init + for i in "${success[@]}" "${failure[@]}"; do + ! nix build $i || fail "flake should not search past a git repository" + done + rm -rf .git + popd +fi diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh new file mode 100644 index 000000000..a3d300552 --- /dev/null +++ b/tests/functional/flakes/show.sh @@ -0,0 +1,87 @@ +source ./common.sh + +flakeDir=$TEST_ROOT/flake +mkdir -p "$flakeDir" + +writeSimpleFlake "$flakeDir" +cd "$flakeDir" + + +# By default: Only show the packages content for the current system and no +# legacyPackages at all +nix flake show --json > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.packages.someOtherSystem.default == {}; +assert show_output.packages.${builtins.currentSystem}.default.name == "simple"; +assert show_output.legacyPackages.${builtins.currentSystem} == {}; +true +' + +# With `--all-systems`, show the packages for all systems +nix flake show --json --all-systems > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.packages.someOtherSystem.default.name == "simple"; +assert show_output.legacyPackages.${builtins.currentSystem} == {}; +true +' + +# With `--legacy`, show the legacy packages +nix flake show --json --legacy > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.legacyPackages.${builtins.currentSystem}.hello.name == "simple"; +true +' + +# Test that attributes are only reported when they have actual content +cat >flake.nix <<EOF +{ + description = "Bla bla"; + + outputs = inputs: rec { + apps.$system = { }; + checks.$system = { }; + devShells.$system = { }; + legacyPackages.$system = { }; + packages.$system = { }; + packages.someOtherSystem = { }; + + formatter = { }; + nixosConfigurations = { }; + nixosModules = { }; + }; +} +EOF +nix flake show --json --all-systems > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output == { }; +true +' + +# Test that attributes with errors are handled correctly. +# nixpkgs.legacyPackages is a particularly prominent instance of this. +cat >flake.nix <<EOF +{ + outputs = inputs: { + legacyPackages.$system = { + AAAAAASomeThingsFailToEvaluate = throw "nooo"; + simple = import ./simple.nix; + }; + }; +} +EOF +nix flake show --json --legacy --all-systems > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.legacyPackages.${builtins.currentSystem}.AAAAAASomeThingsFailToEvaluate == { }; +assert show_output.legacyPackages.${builtins.currentSystem}.simple.name == "simple"; +true +' diff --git a/tests/functional/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh new file mode 100644 index 000000000..8abc8b7d3 --- /dev/null +++ b/tests/functional/flakes/unlocked-override.sh @@ -0,0 +1,30 @@ +source ./common.sh + +requireGit + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 + +createGitRepo $flake1Dir +cat > $flake1Dir/flake.nix <<EOF +{ + outputs = { self }: { x = import ./x.nix; }; +} +EOF +echo 123 > $flake1Dir/x.nix +git -C $flake1Dir add flake.nix x.nix +git -C $flake1Dir commit -m Initial + +createGitRepo $flake2Dir +cat > $flake2Dir/flake.nix <<EOF +{ + outputs = { self, flake1 }: { x = flake1.x; }; +} +EOF +git -C $flake2Dir add flake.nix + +[[ $(nix eval --json $flake2Dir#x --override-input flake1 $TEST_ROOT/flake1) = 123 ]] + +echo 456 > $flake1Dir/x.nix + +[[ $(nix eval --json $flake2Dir#x --override-input flake1 $TEST_ROOT/flake1) = 456 ]] |