1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-28 17:51:15 +02:00

Merge pull request #31 from DeterminateSystems/sync-2.28.2

Sync with upstream 2.28.2
This commit is contained in:
Eelco Dolstra 2025-04-17 15:35:51 +00:00 committed by GitHub
commit a8979e05b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
96 changed files with 1060 additions and 397 deletions

View file

@ -1 +1 @@
2.28.1 2.28.2

View file

@ -115,7 +115,7 @@ It creates an [attribute set] representing the string context, which can be insp
## Clearing string contexts ## Clearing string contexts
[`buitins.unsafeDiscardStringContext`](./builtins.md#builtins-unsafeDiscardStringContext) will make a copy of a string, but with an empty string context. [`builtins.unsafeDiscardStringContext`](./builtins.md#builtins-unsafeDiscardStringContext) will make a copy of a string, but with an empty string context.
The returned string can be used in more ways, e.g. by operators that require the string context to be empty. The returned string can be used in more ways, e.g. by operators that require the string context to be empty.
The requirement to explicitly discard the string context in such use cases helps ensure that string context elements are not lost by mistake. The requirement to explicitly discard the string context in such use cases helps ensure that string context elements are not lost by mistake.
The "unsafe" marker is only there to remind that Nix normally guarantees that dependencies are tracked, whereas the returned string has lost them. The "unsafe" marker is only there to remind that Nix normally guarantees that dependencies are tracked, whereas the returned string has lost them.

View file

@ -38,6 +38,15 @@
Curl created sockets without setting `FD_CLOEXEC`/`SOCK_CLOEXEC`. This could previously cause connections to remain open forever when using commands like `nix shell`. This change sets the `FD_CLOEXEC` flag using a `CURLOPT_SOCKOPTFUNCTION` callback. Curl created sockets without setting `FD_CLOEXEC`/`SOCK_CLOEXEC`. This could previously cause connections to remain open forever when using commands like `nix shell`. This change sets the `FD_CLOEXEC` flag using a `CURLOPT_SOCKOPTFUNCTION` callback.
- Add BLAKE3 hash algorithm [#12379](https://github.com/NixOS/nix/pull/12379)
Nix now supports the BLAKE3 hash algorithm as an experimental feature (`blake3-hashes`):
```console
# nix hash file ./file --type blake3 --extra-experimental-features blake3-hashes
blake3-34P4p+iZXcbbyB1i4uoF7eWCGcZHjmaRn6Y7QdynLwU=
```
# Contributors # Contributors
This release was made possible by the following 21 contributors: This release was made possible by the following 21 contributors:

View file

@ -2,6 +2,8 @@
# vim: set filetype=bash: # vim: set filetype=bash:
#!nix shell .#changelog-d --command bash #!nix shell .#changelog-d --command bash
set -euo pipefail
# --- CONFIGURATION --- # --- CONFIGURATION ---
# This does double duty for # This does double duty for

View file

@ -237,12 +237,13 @@ void StorePathCommand::run(ref<Store> store, StorePaths && storePaths)
MixProfile::MixProfile() MixProfile::MixProfile()
{ {
addFlag( addFlag({
{.longName = "profile", .longName = "profile",
.description = "The profile to operate on.", .description = "The profile to operate on.",
.labels = {"path"}, .labels = {"path"},
.handler = {&profile}, .handler = {&profile},
.completer = completePath}); .completer = completePath,
});
} }
void MixProfile::updateProfile(const StorePath & storePath) void MixProfile::updateProfile(const StorePath & storePath)

View file

@ -62,7 +62,7 @@ MixEvalArgs::MixEvalArgs()
.description = "Pass the value *expr* as the argument *name* to Nix functions.", .description = "Pass the value *expr* as the argument *name* to Nix functions.",
.category = category, .category = category,
.labels = {"name", "expr"}, .labels = {"name", "expr"},
.handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr{expr}}); }} .handler = {[&](std::string name, std::string expr) { autoArgs.insert_or_assign(name, AutoArg{AutoArgExpr{expr}}); }},
}); });
addFlag({ addFlag({
@ -79,7 +79,7 @@ MixEvalArgs::MixEvalArgs()
.category = category, .category = category,
.labels = {"name", "path"}, .labels = {"name", "path"},
.handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile{path}}); }}, .handler = {[&](std::string name, std::string path) { autoArgs.insert_or_assign(name, AutoArg{AutoArgFile{path}}); }},
.completer = completePath .completer = completePath,
}); });
addFlag({ addFlag({
@ -104,7 +104,7 @@ MixEvalArgs::MixEvalArgs()
.labels = {"path"}, .labels = {"path"},
.handler = {[&](std::string s) { .handler = {[&](std::string s) {
lookupPath.elements.emplace_back(LookupPath::Elem::parse(s)); lookupPath.elements.emplace_back(LookupPath::Elem::parse(s));
}} }},
}); });
addFlag({ addFlag({
@ -130,7 +130,7 @@ MixEvalArgs::MixEvalArgs()
}}, }},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, openStore(), prefix); completeFlakeRef(completions, openStore(), prefix);
}} }},
}); });
addFlag({ addFlag({

View file

@ -64,21 +64,21 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {[&]() { .handler = {[&]() {
lockFlags.recreateLockFile = true; lockFlags.recreateLockFile = true;
warn("'--recreate-lock-file' is deprecated and will be removed in a future version; use 'nix flake update' instead."); warn("'--recreate-lock-file' is deprecated and will be removed in a future version; use 'nix flake update' instead.");
}} }},
}); });
addFlag({ addFlag({
.longName = "no-update-lock-file", .longName = "no-update-lock-file",
.description = "Do not allow any updates to the flake's lock file.", .description = "Do not allow any updates to the flake's lock file.",
.category = category, .category = category,
.handler = {&lockFlags.updateLockFile, false} .handler = {&lockFlags.updateLockFile, false},
}); });
addFlag({ addFlag({
.longName = "no-write-lock-file", .longName = "no-write-lock-file",
.description = "Do not write the flake's newly generated lock file.", .description = "Do not write the flake's newly generated lock file.",
.category = category, .category = category,
.handler = {&lockFlags.writeLockFile, false} .handler = {&lockFlags.writeLockFile, false},
}); });
addFlag({ addFlag({
@ -94,14 +94,14 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {[&]() { .handler = {[&]() {
lockFlags.useRegistries = false; lockFlags.useRegistries = false;
warn("'--no-registries' is deprecated; use '--no-use-registries'"); warn("'--no-registries' is deprecated; use '--no-use-registries'");
}} }},
}); });
addFlag({ addFlag({
.longName = "commit-lock-file", .longName = "commit-lock-file",
.description = "Commit changes to the flake's lock file.", .description = "Commit changes to the flake's lock file.",
.category = category, .category = category,
.handler = {&lockFlags.commitLockFile, true} .handler = {&lockFlags.commitLockFile, true},
}); });
addFlag({ addFlag({
@ -121,7 +121,7 @@ MixFlakeOptions::MixFlakeOptions()
}}, }},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeInputAttrPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix); completeFlakeInputAttrPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
}} }},
}); });
addFlag({ addFlag({
@ -141,7 +141,7 @@ MixFlakeOptions::MixFlakeOptions()
} else if (n == 1) { } else if (n == 1) {
completeFlakeRef(completions, getEvalState()->store, prefix); completeFlakeRef(completions, getEvalState()->store, prefix);
} }
}} }},
}); });
addFlag({ addFlag({
@ -152,7 +152,7 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {[&](std::string lockFilePath) { .handler = {[&](std::string lockFilePath) {
lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))}; lockFlags.referenceLockFilePath = {getFSSourceAccessor(), CanonPath(absPath(lockFilePath))};
}}, }},
.completer = completePath .completer = completePath,
}); });
addFlag({ addFlag({
@ -163,7 +163,7 @@ MixFlakeOptions::MixFlakeOptions()
.handler = {[&](std::string lockFilePath) { .handler = {[&](std::string lockFilePath) {
lockFlags.outputLockFilePath = lockFilePath; lockFlags.outputLockFilePath = lockFilePath;
}}, }},
.completer = completePath .completer = completePath,
}); });
addFlag({ addFlag({
@ -190,7 +190,7 @@ MixFlakeOptions::MixFlakeOptions()
}}, }},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, getEvalState()->store, prefix); completeFlakeRef(completions, getEvalState()->store, prefix);
}} }},
}); });
} }
@ -206,7 +206,7 @@ SourceExprCommand::SourceExprCommand()
.category = installablesCategory, .category = installablesCategory,
.labels = {"file"}, .labels = {"file"},
.handler = {&file}, .handler = {&file},
.completer = completePath .completer = completePath,
}); });
addFlag({ addFlag({
@ -214,7 +214,7 @@ SourceExprCommand::SourceExprCommand()
.description = "Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the Nix expression *expr*.", .description = "Interpret [*installables*](@docroot@/command-ref/new-cli/nix.md#installables) as attribute paths relative to the Nix expression *expr*.",
.category = installablesCategory, .category = installablesCategory,
.labels = {"expr"}, .labels = {"expr"},
.handler = {&expr} .handler = {&expr},
}); });
} }
@ -831,7 +831,7 @@ RawInstallablesCommand::RawInstallablesCommand()
addFlag({ addFlag({
.longName = "stdin", .longName = "stdin",
.description = "Read installables from the standard input. No default installable applied.", .description = "Read installables from the standard input. No default installable applied.",
.handler = {&readFromStdIn, true} .handler = {&readFromStdIn, true},
}); });
expectArgs({ expectArgs({

View file

@ -20,7 +20,6 @@ headers = [config_pub_h] + files(
'gc-small-vector.hh', 'gc-small-vector.hh',
'get-drvs.hh', 'get-drvs.hh',
'json-to-value.hh', 'json-to-value.hh',
# internal: 'lexer-helpers.hh',
'nixexpr.hh', 'nixexpr.hh',
'parser-state.hh', 'parser-state.hh',
'primops.hh', 'primops.hh',

View file

@ -65,7 +65,7 @@ struct DocComment {
struct AttrName struct AttrName
{ {
Symbol symbol; Symbol symbol;
Expr * expr; Expr * expr = nullptr;
AttrName(Symbol s) : symbol(s) {}; AttrName(Symbol s) : symbol(s) {};
AttrName(Expr * e) : expr(e) {}; AttrName(Expr * e) : expr(e) {};
}; };
@ -159,7 +159,7 @@ struct ExprVar : Expr
`nullptr`: Not from a `with`. `nullptr`: Not from a `with`.
Valid pointer: the nearest, innermost `with` expression to query first. */ Valid pointer: the nearest, innermost `with` expression to query first. */
ExprWith * fromWith; ExprWith * fromWith = nullptr;
/* In the former case, the value is obtained by going `level` /* In the former case, the value is obtained by going `level`
levels up from the current environment and getting the levels up from the current environment and getting the
@ -167,7 +167,7 @@ struct ExprVar : Expr
value is obtained by getting the attribute named `name` from value is obtained by getting the attribute named `name` from
the set stored in the environment that is `level` levels up the set stored in the environment that is `level` levels up
from the current one.*/ from the current one.*/
Level level; Level level = 0;
Displacement displ = 0; Displacement displ = 0;
ExprVar(Symbol name) : name(name) { }; ExprVar(Symbol name) : name(name) { };

View file

@ -1,7 +1,4 @@
#include "lexer-tab.hh" #include "lexer-helpers.hh"
#include "parser-tab.hh"
#include "nix/expr/lexer-helpers.hh"
void nix::lexer::internal::initLoc(YYLTYPE * loc) void nix::lexer::internal::initLoc(YYLTYPE * loc)
{ {

View file

@ -25,8 +25,7 @@
#endif #endif
#include "nix/expr/nixexpr.hh" #include "nix/expr/nixexpr.hh"
#include "parser-tab.hh" #include "lexer-helpers.hh"
#include "nix/expr/lexer-helpers.hh"
namespace nix { namespace nix {
struct LexerState; struct LexerState;

View file

@ -14,6 +14,7 @@ overrides:
fetchTreeFinal: fetchTreeFinal:
let let
inherit (builtins) mapAttrs;
lockFile = builtins.fromJSON lockFileStr; lockFile = builtins.fromJSON lockFileStr;
@ -35,19 +36,26 @@ let
(resolveInput lockFile.nodes.${nodeName}.inputs.${builtins.head path}) (resolveInput lockFile.nodes.${nodeName}.inputs.${builtins.head path})
(builtins.tail path); (builtins.tail path);
allNodes = builtins.mapAttrs ( allNodes = mapAttrs (
key: node: key: node:
let let
parentNode = allNodes.${getInputByPath lockFile.root node.parent}; parentNode = allNodes.${getInputByPath lockFile.root node.parent};
flakeDir =
let
dir = overrides.${key}.dir or node.locked.path or "";
parentDir = parentNode.flakeDir;
in
if node ? parent then parentDir + ("/" + dir) else dir;
sourceInfo = sourceInfo =
if overrides ? ${key} then if overrides ? ${key} then
overrides.${key}.sourceInfo overrides.${key}.sourceInfo
else if node.locked.type == "path" && builtins.substring 0 1 node.locked.path != "/" then else if node.locked.type == "path" && builtins.substring 0 1 node.locked.path != "/" then
parentNode.sourceInfo parentNode.sourceInfo
// { // {
outPath = parentNode.outPath + ("/" + node.locked.path); outPath = parentNode.sourceInfo.outPath + ("/" + flakeDir);
} }
else else
# FIXME: remove obsolete node.info. # FIXME: remove obsolete node.info.
@ -60,7 +68,7 @@ let
flake = import (outPath + "/flake.nix"); flake = import (outPath + "/flake.nix");
inputs = builtins.mapAttrs (inputName: inputSpec: allNodes.${resolveInput inputSpec}) ( inputs = mapAttrs (inputName: inputSpec: allNodes.${resolveInput inputSpec}.result) (
node.inputs or { } node.inputs or { }
); );
@ -85,12 +93,17 @@ let
}; };
in in
{
result =
if node.flake or true then if node.flake or true then
assert builtins.isFunction flake.outputs; assert builtins.isFunction flake.outputs;
result result
else else
sourceInfo sourceInfo;
inherit flakeDir sourceInfo;
}
) lockFile.nodes; ) lockFile.nodes;
in in
allNodes.${lockFile.root} allNodes.${lockFile.root}.result

View file

@ -39,7 +39,7 @@ FlakeRef FlakeRef::resolve(
ref<Store> store, ref<Store> store,
const fetchers::RegistryFilter & filter) const const fetchers::RegistryFilter & filter) const
{ {
auto [input2, extraAttrs] = lookupInRegistries(store, input); auto [input2, extraAttrs] = lookupInRegistries(store, input, filter);
return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir)); return FlakeRef(std::move(input2), fetchers::maybeGetStrAttr(extraAttrs, "dir").value_or(subdir));
} }

View file

@ -57,7 +57,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
if (hasPrefix(s.first, prefix)) if (hasPrefix(s.first, prefix))
completions.add(s.first, fmt("Set the `%s` setting.", s.first)); completions.add(s.first, fmt("Set the `%s` setting.", s.first));
} }
} },
}); });
addFlag({ addFlag({
@ -75,7 +75,7 @@ MixCommonArgs::MixCommonArgs(const std::string & programName)
.labels = Strings{"jobs"}, .labels = Strings{"jobs"},
.handler = {[=](std::string s) { .handler = {[=](std::string s) {
settings.set("max-jobs", s); settings.set("max-jobs", s);
}} }},
}); });
std::string cat = "Options to override configuration settings"; std::string cat = "Options to override configuration settings";

View file

@ -231,7 +231,7 @@ LegacyArgs::LegacyArgs(const std::string & programName,
.handler = {[=](std::string s) { .handler = {[=](std::string s) {
auto n = string2IntWithUnitPrefix<uint64_t>(s); auto n = string2IntWithUnitPrefix<uint64_t>(s);
settings.set(dest, std::to_string(n)); settings.set(dest, std::to_string(n));
}} }},
}); });
}; };

View file

@ -1 +0,0 @@
../../../../tests/functional/derivation/advanced-attributes-defaults.drv

View file

@ -1 +0,0 @@
../../../../tests/functional/derivation/advanced-attributes-structured-attrs-defaults.drv

View file

@ -1 +0,0 @@
../../../../tests/functional/derivation/advanced-attributes-structured-attrs.drv

View file

@ -1,41 +0,0 @@
{
"args": [
"-c",
"echo hello > $out"
],
"builder": "/bin/bash",
"env": {
"__json": "{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"],\"disallowedRequisites\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"],\"allowedRequisites\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}",
"bin": "/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin",
"dev": "/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev",
"out": "/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs"
},
"inputDrvs": {
"/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv": {
"dynamicOutputs": {},
"outputs": [
"out"
]
},
"/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv": {
"dynamicOutputs": {},
"outputs": [
"out"
]
}
},
"inputSrcs": [],
"name": "advanced-attributes-structured-attrs",
"outputs": {
"bin": {
"path": "/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin"
},
"dev": {
"path": "/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev"
},
"out": {
"path": "/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs"
}
},
"system": "my-system"
}

View file

@ -1 +0,0 @@
../../../../tests/functional/derivation/advanced-attributes.drv

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ca/advanced-attributes-defaults.drv

View file

@ -0,0 +1,25 @@
{
"args": [
"-c",
"echo hello > $out"
],
"builder": "/bin/bash",
"env": {
"builder": "/bin/bash",
"name": "advanced-attributes-defaults",
"out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9",
"outputHashAlgo": "sha256",
"outputHashMode": "recursive",
"system": "my-system"
},
"inputDrvs": {},
"inputSrcs": [],
"name": "advanced-attributes-defaults",
"outputs": {
"out": {
"hashAlgo": "sha256",
"method": "nar"
}
},
"system": "my-system"
}

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ca/advanced-attributes-structured-attrs-defaults.drv

View file

@ -0,0 +1,26 @@
{
"args": [
"-c",
"echo hello > $out"
],
"builder": "/bin/bash",
"env": {
"__json": "{\"builder\":\"/bin/bash\",\"name\":\"advanced-attributes-structured-attrs-defaults\",\"outputHashAlgo\":\"sha256\",\"outputHashMode\":\"recursive\",\"outputs\":[\"out\",\"dev\"],\"system\":\"my-system\"}",
"dev": "/02qcpld1y6xhs5gz9bchpxaw0xdhmsp5dv88lh25r2ss44kh8dxz",
"out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"
},
"inputDrvs": {},
"inputSrcs": [],
"name": "advanced-attributes-structured-attrs-defaults",
"outputs": {
"dev": {
"hashAlgo": "sha256",
"method": "nar"
},
"out": {
"hashAlgo": "sha256",
"method": "nar"
}
},
"system": "my-system"
}

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ca/advanced-attributes-structured-attrs.drv

View file

@ -0,0 +1,46 @@
{
"args": [
"-c",
"echo hello > $out"
],
"builder": "/bin/bash",
"env": {
"__json": "{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g\"],\"disallowedRequisites\":[\"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9\"],\"allowedRequisites\":[\"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z\"]}},\"outputHashAlgo\":\"sha256\",\"outputHashMode\":\"recursive\",\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}",
"bin": "/04f3da1kmbr67m3gzxikmsl4vjz5zf777sv6m14ahv22r65aac9m",
"dev": "/02qcpld1y6xhs5gz9bchpxaw0xdhmsp5dv88lh25r2ss44kh8dxz",
"out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"
},
"inputDrvs": {
"/nix/store/j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
},
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
}
},
"inputSrcs": [],
"name": "advanced-attributes-structured-attrs",
"outputs": {
"bin": {
"hashAlgo": "sha256",
"method": "nar"
},
"dev": {
"hashAlgo": "sha256",
"method": "nar"
},
"out": {
"hashAlgo": "sha256",
"method": "nar"
}
},
"system": "my-system"
}

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ca/advanced-attributes.drv

View file

@ -0,0 +1,52 @@
{
"args": [
"-c",
"echo hello > $out"
],
"builder": "/bin/bash",
"env": {
"__darwinAllowLocalNetworking": "1",
"__impureHostDeps": "/usr/bin/ditto",
"__noChroot": "1",
"__sandboxProfile": "sandcastle",
"allowSubstitutes": "",
"allowedReferences": "/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9",
"allowedRequisites": "/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z",
"builder": "/bin/bash",
"disallowedReferences": "/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g",
"disallowedRequisites": "/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8",
"impureEnvVars": "UNICORN",
"name": "advanced-attributes",
"out": "/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9",
"outputHashAlgo": "sha256",
"outputHashMode": "recursive",
"preferLocalBuild": "1",
"requiredSystemFeatures": "rainbow uid-range",
"system": "my-system"
},
"inputDrvs": {
"/nix/store/j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
},
"/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
}
},
"inputSrcs": [],
"name": "advanced-attributes",
"outputs": {
"out": {
"hashAlgo": "sha256",
"method": "nar"
}
},
"system": "my-system"
}

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ia/advanced-attributes-defaults.drv

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ia/advanced-attributes-structured-attrs-defaults.drv

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ia/advanced-attributes-structured-attrs.drv

View file

@ -0,0 +1,43 @@
{
"args": [
"-c",
"echo hello > $out"
],
"builder": "/bin/bash",
"env": {
"__json": "{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar\"],\"disallowedRequisites\":[\"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo\"],\"allowedRequisites\":[\"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}",
"bin": "/nix/store/qjjj3zrlimpjbkk686m052b3ks9iz2sl-advanced-attributes-structured-attrs-bin",
"dev": "/nix/store/lpz5grl48v93pdadavyg5is1rqvfdipf-advanced-attributes-structured-attrs-dev",
"out": "/nix/store/nzvz1bmh1g89a5dkpqcqan0av7q3hgv3-advanced-attributes-structured-attrs"
},
"inputDrvs": {
"/nix/store/afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
},
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
}
},
"inputSrcs": [],
"name": "advanced-attributes-structured-attrs",
"outputs": {
"bin": {
"path": "/nix/store/qjjj3zrlimpjbkk686m052b3ks9iz2sl-advanced-attributes-structured-attrs-bin"
},
"dev": {
"path": "/nix/store/lpz5grl48v93pdadavyg5is1rqvfdipf-advanced-attributes-structured-attrs-dev"
},
"out": {
"path": "/nix/store/nzvz1bmh1g89a5dkpqcqan0av7q3hgv3-advanced-attributes-structured-attrs"
}
},
"system": "my-system"
}

View file

@ -0,0 +1 @@
../../../../../tests/functional/derivation/ia/advanced-attributes.drv

View file

@ -0,0 +1,49 @@
{
"args": [
"-c",
"echo hello > $out"
],
"builder": "/bin/bash",
"env": {
"__darwinAllowLocalNetworking": "1",
"__impureHostDeps": "/usr/bin/ditto",
"__noChroot": "1",
"__sandboxProfile": "sandcastle",
"allowSubstitutes": "",
"allowedReferences": "/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo",
"allowedRequisites": "/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev",
"builder": "/bin/bash",
"disallowedReferences": "/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar",
"disallowedRequisites": "/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev",
"impureEnvVars": "UNICORN",
"name": "advanced-attributes",
"out": "/nix/store/swkj0mrq0cq3dfli95v4am0427mi2hxf-advanced-attributes",
"preferLocalBuild": "1",
"requiredSystemFeatures": "rainbow uid-range",
"system": "my-system"
},
"inputDrvs": {
"/nix/store/afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
},
"/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv": {
"dynamicOutputs": {},
"outputs": [
"dev",
"out"
]
}
},
"inputSrcs": [],
"name": "advanced-attributes",
"outputs": {
"out": {
"path": "/nix/store/swkj0mrq0cq3dfli95v4am0427mi2hxf-advanced-attributes"
}
},
"system": "my-system"
}

View file

@ -18,68 +18,93 @@ using nlohmann::json;
class DerivationAdvancedAttrsTest : public CharacterizationTest, public LibStoreTest class DerivationAdvancedAttrsTest : public CharacterizationTest, public LibStoreTest
{ {
std::filesystem::path unitTestData = getUnitTestData() / "derivation"; protected:
std::filesystem::path unitTestData = getUnitTestData() / "derivation" / "ia";
public: public:
std::filesystem::path goldenMaster(std::string_view testStem) const override std::filesystem::path goldenMaster(std::string_view testStem) const override
{ {
return unitTestData / testStem; return unitTestData / testStem;
} }
/**
* We set these in tests rather than the regular globals so we don't have
* to worry about race conditions if the tests run concurrently.
*/
ExperimentalFeatureSettings mockXpSettings;
}; };
class CaDerivationAdvancedAttrsTest : public DerivationAdvancedAttrsTest
{
void SetUp() override
{
unitTestData = getUnitTestData() / "derivation" / "ca";
mockXpSettings.set("experimental-features", "ca-derivations");
}
};
template<class Fixture>
class DerivationAdvancedAttrsBothTest : public Fixture
{};
using BothFixtures = ::testing::Types<DerivationAdvancedAttrsTest, CaDerivationAdvancedAttrsTest>;
TYPED_TEST_SUITE(DerivationAdvancedAttrsBothTest, BothFixtures);
#define TEST_ATERM_JSON(STEM, NAME) \ #define TEST_ATERM_JSON(STEM, NAME) \
TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_from_json) \ TYPED_TEST(DerivationAdvancedAttrsBothTest, Derivation_##STEM##_from_json) \
{ \ { \
readTest(NAME ".json", [&](const auto & encoded_) { \ this->readTest(NAME ".json", [&](const auto & encoded_) { \
auto encoded = json::parse(encoded_); \ auto encoded = json::parse(encoded_); \
/* Use DRV file instead of C++ literal as source of truth. */ \ /* Use DRV file instead of C++ literal as source of truth. */ \
auto aterm = readFile(goldenMaster(NAME ".drv")); \ auto aterm = readFile(this->goldenMaster(NAME ".drv")); \
auto expected = parseDerivation(*store, std::move(aterm), NAME); \ auto expected = parseDerivation(*this->store, std::move(aterm), NAME, this->mockXpSettings); \
Derivation got = Derivation::fromJSON(*store, encoded); \ Derivation got = Derivation::fromJSON(*this->store, encoded, this->mockXpSettings); \
EXPECT_EQ(got, expected); \ EXPECT_EQ(got, expected); \
}); \ }); \
} \ } \
\ \
TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_to_json) \ TYPED_TEST(DerivationAdvancedAttrsBothTest, Derivation_##STEM##_to_json) \
{ \ { \
writeTest( \ this->writeTest( \
NAME ".json", \ NAME ".json", \
[&]() -> json { \ [&]() -> json { \
/* Use DRV file instead of C++ literal as source of truth. */ \ /* Use DRV file instead of C++ literal as source of truth. */ \
auto aterm = readFile(goldenMaster(NAME ".drv")); \ auto aterm = readFile(this->goldenMaster(NAME ".drv")); \
return parseDerivation(*store, std::move(aterm), NAME).toJSON(*store); \ return parseDerivation(*this->store, std::move(aterm), NAME, this->mockXpSettings) \
.toJSON(*this->store); \
}, \ }, \
[](const auto & file) { return json::parse(readFile(file)); }, \ [](const auto & file) { return json::parse(readFile(file)); }, \
[](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \ [](const auto & file, const auto & got) { return writeFile(file, got.dump(2) + "\n"); }); \
} \ } \
\ \
TEST_F(DerivationAdvancedAttrsTest, Derivation_##STEM##_from_aterm) \ TYPED_TEST(DerivationAdvancedAttrsBothTest, Derivation_##STEM##_from_aterm) \
{ \ { \
readTest(NAME ".drv", [&](auto encoded) { \ this->readTest(NAME ".drv", [&](auto encoded) { \
/* Use JSON file instead of C++ literal as source of truth. */ \ /* Use JSON file instead of C++ literal as source of truth. */ \
auto json = json::parse(readFile(goldenMaster(NAME ".json"))); \ auto json = json::parse(readFile(this->goldenMaster(NAME ".json"))); \
auto expected = Derivation::fromJSON(*store, json); \ auto expected = Derivation::fromJSON(*this->store, json, this->mockXpSettings); \
auto got = parseDerivation(*store, std::move(encoded), NAME); \ auto got = parseDerivation(*this->store, std::move(encoded), NAME, this->mockXpSettings); \
EXPECT_EQ(got.toJSON(*store), expected.toJSON(*store)); \ EXPECT_EQ(got.toJSON(*this->store), expected.toJSON(*this->store)); \
EXPECT_EQ(got, expected); \ EXPECT_EQ(got, expected); \
}); \ }); \
} \ } \
\ \
/* No corresponding write test, because we need to read the drv to write the json file */ /* No corresponding write test, because we need to read the drv to write the json file */
TEST_ATERM_JSON(advancedAttributes_defaults, "advanced-attributes-defaults");
TEST_ATERM_JSON(advancedAttributes, "advanced-attributes-defaults"); TEST_ATERM_JSON(advancedAttributes, "advanced-attributes-defaults");
TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attributes-structured-attrs"); TEST_ATERM_JSON(advancedAttributes_defaults, "advanced-attributes");
TEST_ATERM_JSON(advancedAttributes_structuredAttrs, "advanced-attributes-structured-attrs-defaults"); TEST_ATERM_JSON(advancedAttributes_structuredAttrs, "advanced-attributes-structured-attrs-defaults");
TEST_ATERM_JSON(advancedAttributes_structuredAttrs_defaults, "advanced-attributes-structured-attrs");
#undef TEST_ATERM_JSON #undef TEST_ATERM_JSON
TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_defaults) TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_defaults)
{ {
readTest("advanced-attributes-defaults.drv", [&](auto encoded) { this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*store, std::move(encoded), "foo"); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*store, got, NoRepair, true); auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got); ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
@ -101,25 +126,50 @@ TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_defaults)
EXPECT_EQ(checksForAllOutputs.disallowedReferences, StringSet{}); EXPECT_EQ(checksForAllOutputs.disallowedReferences, StringSet{});
EXPECT_EQ(checksForAllOutputs.disallowedRequisites, StringSet{}); EXPECT_EQ(checksForAllOutputs.disallowedRequisites, StringSet{});
} }
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet()); EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.canBuildLocally(*store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*store, got), false);
EXPECT_EQ(options.substitutesAllowed(), true); EXPECT_EQ(options.substitutesAllowed(), true);
EXPECT_EQ(options.useUidRange(got), false); EXPECT_EQ(options.useUidRange(got), false);
}); });
}; };
TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes) TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_defaults)
{ {
readTest("advanced-attributes.drv", [&](auto encoded) { this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*store, std::move(encoded), "foo"); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*store, got, NoRepair, true); auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got); ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
StringSet systemFeatures{"rainbow", "uid-range"}; EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{});
});
};
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_defaults)
{
this->readTest("advanced-attributes-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"});
});
};
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes)
{
this->readTest("advanced-attributes.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
EXPECT_TRUE(!parsedDrv.hasStructuredAttrs()); EXPECT_TRUE(!parsedDrv.hasStructuredAttrs());
@ -128,34 +178,88 @@ TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes)
EXPECT_EQ(options.impureHostDeps, StringSet{"/usr/bin/ditto"}); EXPECT_EQ(options.impureHostDeps, StringSet{"/usr/bin/ditto"});
EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"}); EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"});
EXPECT_EQ(options.allowLocalNetworking, true); EXPECT_EQ(options.allowLocalNetworking, true);
EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
EXPECT_EQ(options.substitutesAllowed(), false);
EXPECT_EQ(options.useUidRange(got), true);
});
};
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes)
{
this->readTest("advanced-attributes.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
{ {
auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks); auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks);
ASSERT_TRUE(checksForAllOutputs_ != nullptr); ASSERT_TRUE(checksForAllOutputs_ != nullptr);
auto & checksForAllOutputs = *checksForAllOutputs_; auto & checksForAllOutputs = *checksForAllOutputs_;
EXPECT_EQ( EXPECT_EQ(
checksForAllOutputs.allowedReferences, StringSet{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); checksForAllOutputs.allowedReferences, StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"});
EXPECT_EQ( EXPECT_EQ(
checksForAllOutputs.allowedRequisites, StringSet{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"}); checksForAllOutputs.allowedRequisites,
StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"});
EXPECT_EQ( EXPECT_EQ(
checksForAllOutputs.disallowedReferences, StringSet{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); checksForAllOutputs.disallowedReferences, StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"});
EXPECT_EQ( EXPECT_EQ(
checksForAllOutputs.disallowedRequisites, StringSet{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"}); checksForAllOutputs.disallowedRequisites,
StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"});
} }
StringSet systemFeatures{"rainbow", "uid-range"};
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures); EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
EXPECT_EQ(options.canBuildLocally(*store, got), false);
EXPECT_EQ(options.willBuildLocally(*store, got), false);
EXPECT_EQ(options.substitutesAllowed(), false);
EXPECT_EQ(options.useUidRange(got), true);
}); });
}; };
TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttrs_defaults) TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes)
{ {
readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) { this->readTest("advanced-attributes.drv", [&](auto encoded) {
auto got = parseDerivation(*store, std::move(encoded), "foo"); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*store, got, NoRepair, true); auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
{
auto * checksForAllOutputs_ = std::get_if<0>(&options.outputChecks);
ASSERT_TRUE(checksForAllOutputs_ != nullptr);
auto & checksForAllOutputs = *checksForAllOutputs_;
EXPECT_EQ(
checksForAllOutputs.allowedReferences,
StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"});
EXPECT_EQ(
checksForAllOutputs.allowedRequisites,
StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"});
EXPECT_EQ(
checksForAllOutputs.disallowedReferences,
StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"});
EXPECT_EQ(
checksForAllOutputs.disallowedRequisites,
StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"});
}
StringSet systemFeatures{"rainbow", "uid-range"};
systemFeatures.insert("ca-derivations");
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
});
};
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs_defaults)
{
this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got); ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
@ -176,25 +280,50 @@ TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttr
EXPECT_EQ(checksPerOutput.size(), 0); EXPECT_EQ(checksPerOutput.size(), 0);
} }
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet()); EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.canBuildLocally(*store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*store, got), false);
EXPECT_EQ(options.substitutesAllowed(), true); EXPECT_EQ(options.substitutesAllowed(), true);
EXPECT_EQ(options.useUidRange(got), false); EXPECT_EQ(options.useUidRange(got), false);
}); });
}; };
TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttrs) TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults)
{ {
readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) { this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*store, std::move(encoded), "foo"); auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*store, got, NoRepair, true); auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got); ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv); DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
StringSet systemFeatures{"rainbow", "uid-range"}; EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{});
});
};
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs_defaults)
{
this->readTest("advanced-attributes-structured-attrs-defaults.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
EXPECT_EQ(options.getRequiredSystemFeatures(got), StringSet{"ca-derivations"});
});
};
TYPED_TEST(DerivationAdvancedAttrsBothTest, advancedAttributes_structuredAttrs)
{
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
EXPECT_TRUE(parsedDrv.hasStructuredAttrs()); EXPECT_TRUE(parsedDrv.hasStructuredAttrs());
@ -204,25 +333,6 @@ TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttr
EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"}); EXPECT_EQ(options.impureEnvVars, StringSet{"UNICORN"});
EXPECT_EQ(options.allowLocalNetworking, true); EXPECT_EQ(options.allowLocalNetworking, true);
{
{
auto output_ = get(std::get<1>(options.outputChecks), "out");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(output.allowedReferences, StringSet{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"});
EXPECT_EQ(output.allowedRequisites, StringSet{"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"});
}
{
auto output_ = get(std::get<1>(options.outputChecks), "bin");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(output.disallowedReferences, StringSet{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"});
EXPECT_EQ(output.disallowedRequisites, StringSet{"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"});
}
{ {
auto output_ = get(std::get<1>(options.outputChecks), "dev"); auto output_ = get(std::get<1>(options.outputChecks), "dev");
ASSERT_TRUE(output_); ASSERT_TRUE(output_);
@ -231,14 +341,88 @@ TEST_F(DerivationAdvancedAttrsTest, Derivation_advancedAttributes_structuredAttr
EXPECT_EQ(output.maxSize, 789); EXPECT_EQ(output.maxSize, 789);
EXPECT_EQ(output.maxClosureSize, 5909); EXPECT_EQ(output.maxClosureSize, 5909);
} }
}
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures); EXPECT_EQ(options.canBuildLocally(*this->store, got), false);
EXPECT_EQ(options.canBuildLocally(*store, got), false); EXPECT_EQ(options.willBuildLocally(*this->store, got), false);
EXPECT_EQ(options.willBuildLocally(*store, got), false);
EXPECT_EQ(options.substitutesAllowed(), false); EXPECT_EQ(options.substitutesAllowed(), false);
EXPECT_EQ(options.useUidRange(got), true); EXPECT_EQ(options.useUidRange(got), true);
}); });
}; };
TEST_F(DerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
{
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
{
{
auto output_ = get(std::get<1>(options.outputChecks), "out");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(output.allowedReferences, StringSet{"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"});
EXPECT_EQ(output.allowedRequisites, StringSet{"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"});
}
{
auto output_ = get(std::get<1>(options.outputChecks), "bin");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(output.disallowedReferences, StringSet{"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"});
EXPECT_EQ(
output.disallowedRequisites, StringSet{"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"});
}
}
StringSet systemFeatures{"rainbow", "uid-range"};
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
});
};
TEST_F(CaDerivationAdvancedAttrsTest, advancedAttributes_structuredAttrs)
{
this->readTest("advanced-attributes-structured-attrs.drv", [&](auto encoded) {
auto got = parseDerivation(*this->store, std::move(encoded), "foo", this->mockXpSettings);
auto drvPath = writeDerivation(*this->store, got, NoRepair, true);
ParsedDerivation parsedDrv(drvPath, got);
DerivationOptions options = DerivationOptions::fromParsedDerivation(parsedDrv);
{
{
auto output_ = get(std::get<1>(options.outputChecks), "out");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(output.allowedReferences, StringSet{"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"});
EXPECT_EQ(output.allowedRequisites, StringSet{"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"});
}
{
auto output_ = get(std::get<1>(options.outputChecks), "bin");
ASSERT_TRUE(output_);
auto & output = *output_;
EXPECT_EQ(
output.disallowedReferences, StringSet{"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"});
EXPECT_EQ(
output.disallowedRequisites, StringSet{"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"});
}
}
StringSet systemFeatures{"rainbow", "uid-range"};
systemFeatures.insert("ca-derivations");
EXPECT_EQ(options.getRequiredSystemFeatures(got), systemFeatures);
});
};
} }

View file

@ -73,6 +73,32 @@ TEST(machines, getMachinesWithSemicolonSeparator) {
EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl")))); EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl"))));
} }
TEST(machines, getMachinesWithCommentsAndSemicolonSeparator) {
auto actual = Machine::parseConfig({},
"# This is a comment ; this is still that comment\n"
"nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl\n"
"# This is also a comment ; this also is still that comment\n"
"nix@scabby.labs.cs.uu.nl\n");
EXPECT_THAT(actual, SizeIs(3));
EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))));
EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl"))));
EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scabby.labs.cs.uu.nl"))));
}
TEST(machines, getMachinesWithFunnyWhitespace) {
auto actual = Machine::parseConfig({},
" # commment ; comment\n"
" nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl \n"
"\n \n"
"\n ;;; \n"
"\n ; ; \n"
"nix@scabby.labs.cs.uu.nl\n\n");
EXPECT_THAT(actual, SizeIs(3));
EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scratchy.labs.cs.uu.nl"))));
EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@itchy.labs.cs.uu.nl"))));
EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, AuthorityMatches("nix@scabby.labs.cs.uu.nl"))));
}
TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) { TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) {
auto actual = Machine::parseConfig({}, auto actual = Machine::parseConfig({},
"nix@scratchy.labs.cs.uu.nl i686-linux " "nix@scratchy.labs.cs.uu.nl i686-linux "

View file

@ -68,7 +68,6 @@ DerivationOptions DerivationOptions::fromParsedDerivation(const ParsedDerivation
throw Error("attribute '%s' must be a list of strings", name); throw Error("attribute '%s' must be a list of strings", name);
res.insert(j->get<std::string>()); res.insert(j->get<std::string>());
} }
checks.disallowedRequisites = res;
return res; return res;
} }
return {}; return {};

View file

@ -1368,7 +1368,7 @@ Derivation Derivation::fromJSON(
for (auto & [outputName, output] : getObject(valueAt(json, "outputs"))) { for (auto & [outputName, output] : getObject(valueAt(json, "outputs"))) {
res.outputs.insert_or_assign( res.outputs.insert_or_assign(
outputName, outputName,
DerivationOutput::fromJSON(store, res.name, outputName, output)); DerivationOutput::fromJSON(store, res.name, outputName, output, xpSettings));
} }
} catch (Error & e) { } catch (Error & e) {
e.addTrace({}, "while reading key 'outputs'"); e.addTrace({}, "while reading key 'outputs'");

View file

@ -22,10 +22,8 @@
#include <curl/curl.h> #include <curl/curl.h>
#include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include <iostream>
#include <queue> #include <queue>
#include <random> #include <random>
#include <thread> #include <thread>
@ -95,7 +93,7 @@ struct curlFileTransfer : public FileTransfer
: fileTransfer(fileTransfer) : fileTransfer(fileTransfer)
, request(request) , request(request)
, act(*logger, lvlTalkative, actFileTransfer, , act(*logger, lvlTalkative, actFileTransfer,
request.post ? "" : fmt(request.data ? "uploading '%s'" : "downloading '%s'", request.uri), fmt("%sing '%s'", request.verb(), request.uri),
{request.uri}, request.parentAct) {request.uri}, request.parentAct)
, callback(std::move(callback)) , callback(std::move(callback))
, finalSink([this](std::string_view data) { , finalSink([this](std::string_view data) {
@ -272,19 +270,11 @@ struct curlFileTransfer : public FileTransfer
return getInterrupted(); return getInterrupted();
} }
int silentProgressCallback(curl_off_t dltotal, curl_off_t dlnow)
{
return getInterrupted();
}
static int progressCallbackWrapper(void * userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) static int progressCallbackWrapper(void * userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{ {
return ((TransferItem *) userp)->progressCallback(dltotal, dlnow); auto & item = *static_cast<TransferItem *>(userp);
} auto isUpload = bool(item.request.data);
return item.progressCallback(isUpload ? ultotal : dltotal, isUpload ? ulnow : dlnow);
static int silentProgressCallbackWrapper(void * userp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
return ((TransferItem *) userp)->silentProgressCallback(dltotal, dlnow);
} }
static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr) static int debugCallback(CURL * handle, curl_infotype type, char * data, size_t size, void * userptr)
@ -353,9 +343,6 @@ struct curlFileTransfer : public FileTransfer
curl_easy_setopt(req, CURLOPT_HEADERFUNCTION, TransferItem::headerCallbackWrapper); curl_easy_setopt(req, CURLOPT_HEADERFUNCTION, TransferItem::headerCallbackWrapper);
curl_easy_setopt(req, CURLOPT_HEADERDATA, this); curl_easy_setopt(req, CURLOPT_HEADERDATA, this);
if (request.post)
curl_easy_setopt(req, CURLOPT_XFERINFOFUNCTION, silentProgressCallbackWrapper);
else
curl_easy_setopt(req, CURLOPT_XFERINFOFUNCTION, progressCallbackWrapper); curl_easy_setopt(req, CURLOPT_XFERINFOFUNCTION, progressCallbackWrapper);
curl_easy_setopt(req, CURLOPT_XFERINFODATA, this); curl_easy_setopt(req, CURLOPT_XFERINFODATA, this);
curl_easy_setopt(req, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(req, CURLOPT_NOPROGRESS, 0);
@ -449,7 +436,6 @@ struct curlFileTransfer : public FileTransfer
if (httpStatus == 304 && result.etag == "") if (httpStatus == 304 && result.etag == "")
result.etag = request.expectedETag; result.etag = request.expectedETag;
if (!request.post)
act.progress(result.bodySize, result.bodySize); act.progress(result.bodySize, result.bodySize);
done = true; done = true;
callback(std::move(result)); callback(std::move(result));
@ -539,6 +525,8 @@ struct curlFileTransfer : public FileTransfer
warn("%s; retrying from offset %d in %d ms", exc.what(), writtenToSink, ms); warn("%s; retrying from offset %d in %d ms", exc.what(), writtenToSink, ms);
else else
warn("%s; retrying in %d ms", exc.what(), ms); warn("%s; retrying in %d ms", exc.what(), ms);
decompressionSink.reset();
errorSink.reset();
embargo = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); embargo = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms);
fileTransfer.enqueueItem(shared_from_this()); fileTransfer.enqueueItem(shared_from_this());
} }
@ -791,10 +779,6 @@ struct curlFileTransfer : public FileTransfer
S3Helper s3Helper(profile, region, scheme, endpoint); S3Helper s3Helper(profile, region, scheme, endpoint);
Activity act(*logger, lvlTalkative, actFileTransfer,
fmt("downloading '%s'", request.uri),
{request.uri}, request.parentAct);
// FIXME: implement ETag // FIXME: implement ETag
auto s3Res = s3Helper.getObject(bucketName, key); auto s3Res = s3Helper.getObject(bucketName, key);
FileTransferResult res; FileTransferResult res;

View file

@ -280,21 +280,21 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s
.aliases = aliases, .aliases = aliases,
.description = "Enable sandboxing.", .description = "Enable sandboxing.",
.category = category, .category = category,
.handler = {[this]() { override(smEnabled); }} .handler = {[this]() { override(smEnabled); }},
}); });
args.addFlag({ args.addFlag({
.longName = "no-" + name, .longName = "no-" + name,
.aliases = aliases, .aliases = aliases,
.description = "Disable sandboxing.", .description = "Disable sandboxing.",
.category = category, .category = category,
.handler = {[this]() { override(smDisabled); }} .handler = {[this]() { override(smDisabled); }},
}); });
args.addFlag({ args.addFlag({
.longName = "relaxed-" + name, .longName = "relaxed-" + name,
.aliases = aliases, .aliases = aliases,
.description = "Enable sandboxing, but allow builds to disable it.", .description = "Enable sandboxing, but allow builds to disable it.",
.category = category, .category = category,
.handler = {[this]() { override(smRelaxed); }} .handler = {[this]() { override(smRelaxed); }},
}); });
} }

View file

@ -77,7 +77,7 @@ struct FileTransferRequest
FileTransferRequest(std::string_view uri) FileTransferRequest(std::string_view uri)
: uri(uri), parentAct(getCurActivity()) { } : uri(uri), parentAct(getCurActivity()) { }
std::string verb() std::string verb() const
{ {
return data ? "upload" : "download"; return data ? "upload" : "download";
} }

View file

@ -1,6 +1,5 @@
include_dirs += include_directories('../..') include_dirs += include_directories('../..')
headers += files( headers += files(
'fchmodat2-compat.hh',
'personality.hh', 'personality.hh',
) )

View file

@ -105,13 +105,15 @@ ref<Store> Machine::openStore() const
static std::vector<std::string> expandBuilderLines(const std::string & builders) static std::vector<std::string> expandBuilderLines(const std::string & builders)
{ {
std::vector<std::string> result; std::vector<std::string> result;
for (auto line : tokenizeString<std::vector<std::string>>(builders, "\n;")) { for (auto line : tokenizeString<std::vector<std::string>>(builders, "\n")) {
trim(line);
line.erase(std::find(line.begin(), line.end(), '#'), line.end()); line.erase(std::find(line.begin(), line.end(), '#'), line.end());
if (line.empty()) continue; for (auto entry : tokenizeString<std::vector<std::string>>(line, ";")) {
entry = trim(entry);
if (line[0] == '@') { if (entry.empty()) {
const std::string path = trim(std::string(line, 1)); // skip blank entries
} else if (entry[0] == '@') {
const std::string path = trim(std::string_view{entry}.substr(1));
std::string text; std::string text;
try { try {
text = readFile(path); text = readFile(path);
@ -119,14 +121,15 @@ static std::vector<std::string> expandBuilderLines(const std::string & builders)
if (e.errNo != ENOENT) if (e.errNo != ENOENT)
throw; throw;
debug("cannot find machines file '%s'", path); debug("cannot find machines file '%s'", path);
}
const auto lines = expandBuilderLines(text);
result.insert(end(result), begin(lines), end(lines));
continue; continue;
} }
result.emplace_back(line); const auto entrys = expandBuilderLines(text);
result.insert(end(result), begin(entrys), end(entrys));
} else {
result.emplace_back(entry);
}
}
} }
return result; return result;
} }

View file

@ -160,7 +160,10 @@ ref<Aws::Client::ClientConfiguration> S3Helper::makeConfig(
S3Helper::FileTransferResult S3Helper::getObject( S3Helper::FileTransferResult S3Helper::getObject(
const std::string & bucketName, const std::string & key) const std::string & bucketName, const std::string & key)
{ {
debug("fetching 's3://%s/%s'...", bucketName, key); std::string uri = "s3://" + bucketName + "/" + key;
Activity act(*logger, lvlTalkative, actFileTransfer,
fmt("downloading '%s'", uri),
Logger::Fields{uri}, getCurActivity());
auto request = auto request =
Aws::S3::Model::GetObjectRequest() Aws::S3::Model::GetObjectRequest()
@ -171,6 +174,22 @@ S3Helper::FileTransferResult S3Helper::getObject(
return Aws::New<std::stringstream>("STRINGSTREAM"); return Aws::New<std::stringstream>("STRINGSTREAM");
}); });
size_t bytesDone = 0;
size_t bytesExpected = 0;
request.SetDataReceivedEventHandler([&](const Aws::Http::HttpRequest * req, Aws::Http::HttpResponse * resp, long long l) {
if (!bytesExpected && resp->HasHeader("Content-Length")) {
if (auto length = string2Int<size_t>(resp->GetHeader("Content-Length"))) {
bytesExpected = *length;
}
}
bytesDone += l;
act.progress(bytesDone, bytesExpected);
});
request.SetContinueRequestHandler([](const Aws::Http::HttpRequest*) {
return !isInterrupted();
});
FileTransferResult res; FileTransferResult res;
auto now1 = std::chrono::steady_clock::now(); auto now1 = std::chrono::steady_clock::now();
@ -180,6 +199,8 @@ S3Helper::FileTransferResult S3Helper::getObject(
auto result = checkAws(fmt("AWS error fetching '%s'", key), auto result = checkAws(fmt("AWS error fetching '%s'", key),
client->GetObject(request)); client->GetObject(request));
act.progress(result.GetContentLength(), result.GetContentLength());
res.data = decompress(result.GetContentEncoding(), res.data = decompress(result.GetContentEncoding(),
dynamic_cast<std::stringstream &>(result.GetBody()).str()); dynamic_cast<std::stringstream &>(result.GetBody()).str());
@ -307,11 +328,35 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
std::shared_ptr<TransferManager> transferManager; std::shared_ptr<TransferManager> transferManager;
std::once_flag transferManagerCreated; std::once_flag transferManagerCreated;
struct AsyncContext : public Aws::Client::AsyncCallerContext
{
mutable std::mutex mutex;
mutable std::condition_variable cv;
const Activity & act;
void notify() const
{
cv.notify_one();
}
void wait() const
{
std::unique_lock<std::mutex> lk(mutex);
cv.wait(lk);
}
AsyncContext(const Activity & act) : act(act) {}
};
void uploadFile(const std::string & path, void uploadFile(const std::string & path,
std::shared_ptr<std::basic_iostream<char>> istream, std::shared_ptr<std::basic_iostream<char>> istream,
const std::string & mimeType, const std::string & mimeType,
const std::string & contentEncoding) const std::string & contentEncoding)
{ {
std::string uri = "s3://" + bucketName + "/" + path;
Activity act(*logger, lvlTalkative, actFileTransfer,
fmt("uploading '%s'", uri),
Logger::Fields{uri}, getCurActivity());
istream->seekg(0, istream->end); istream->seekg(0, istream->end);
auto size = istream->tellg(); auto size = istream->tellg();
istream->seekg(0, istream->beg); istream->seekg(0, istream->beg);
@ -330,16 +375,25 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
transferConfig.bufferSize = bufferSize; transferConfig.bufferSize = bufferSize;
transferConfig.uploadProgressCallback = transferConfig.uploadProgressCallback =
[](const TransferManager *transferManager, [](const TransferManager * transferManager,
const std::shared_ptr<const TransferHandle> const std::shared_ptr<const TransferHandle> & transferHandle)
&transferHandle)
{ {
//FIXME: find a way to properly abort the multipart upload. auto context = std::dynamic_pointer_cast<const AsyncContext>(transferHandle->GetContext());
//checkInterrupt(); size_t bytesDone = transferHandle->GetBytesTransferred();
debug("upload progress ('%s'): '%d' of '%d' bytes", size_t bytesTotal = transferHandle->GetBytesTotalSize();
transferHandle->GetKey(), try {
transferHandle->GetBytesTransferred(), checkInterrupt();
transferHandle->GetBytesTotalSize()); context->act.progress(bytesDone, bytesTotal);
} catch (...) {
context->notify();
}
};
transferConfig.transferStatusUpdatedCallback =
[](const TransferManager * transferManager,
const std::shared_ptr<const TransferHandle> & transferHandle)
{
auto context = std::dynamic_pointer_cast<const AsyncContext>(transferHandle->GetContext());
context->notify();
}; };
transferManager = TransferManager::Create(transferConfig); transferManager = TransferManager::Create(transferConfig);
@ -353,29 +407,51 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
if (contentEncoding != "") if (contentEncoding != "")
throw Error("setting a content encoding is not supported with S3 multi-part uploads"); throw Error("setting a content encoding is not supported with S3 multi-part uploads");
auto context = std::make_shared<AsyncContext>(act);
std::shared_ptr<TransferHandle> transferHandle = std::shared_ptr<TransferHandle> transferHandle =
transferManager->UploadFile( transferManager->UploadFile(
istream, bucketName, path, mimeType, istream, bucketName, path, mimeType,
Aws::Map<Aws::String, Aws::String>(), Aws::Map<Aws::String, Aws::String>(),
nullptr /*, contentEncoding */); context /*, contentEncoding */);
TransferStatus status = transferHandle->GetStatus();
while (status == TransferStatus::IN_PROGRESS || status == TransferStatus::NOT_STARTED) {
if (!isInterrupted()) {
context->wait();
} else {
transferHandle->Cancel();
transferHandle->WaitUntilFinished(); transferHandle->WaitUntilFinished();
}
status = transferHandle->GetStatus();
}
act.progress(transferHandle->GetBytesTransferred(), transferHandle->GetBytesTotalSize());
if (transferHandle->GetStatus() == TransferStatus::FAILED) if (status == TransferStatus::FAILED)
throw Error("AWS error: failed to upload 's3://%s/%s': %s", throw Error("AWS error: failed to upload 's3://%s/%s': %s",
bucketName, path, transferHandle->GetLastError().GetMessage()); bucketName, path, transferHandle->GetLastError().GetMessage());
if (transferHandle->GetStatus() != TransferStatus::COMPLETED) if (status != TransferStatus::COMPLETED)
throw Error("AWS error: transfer status of 's3://%s/%s' in unexpected state", throw Error("AWS error: transfer status of 's3://%s/%s' in unexpected state",
bucketName, path); bucketName, path);
} else { } else {
act.progress(0, size);
auto request = auto request =
Aws::S3::Model::PutObjectRequest() Aws::S3::Model::PutObjectRequest()
.WithBucket(bucketName) .WithBucket(bucketName)
.WithKey(path); .WithKey(path);
size_t bytesSent = 0;
request.SetDataSentEventHandler([&](const Aws::Http::HttpRequest * req, long long l) {
bytesSent += l;
act.progress(bytesSent, size);
});
request.SetContinueRequestHandler([](const Aws::Http::HttpRequest*) {
return !isInterrupted();
});
request.SetContentType(mimeType); request.SetContentType(mimeType);
if (contentEncoding != "") if (contentEncoding != "")
@ -385,6 +461,8 @@ struct S3BinaryCacheStoreImpl : virtual S3BinaryCacheStoreConfig, public virtual
auto result = checkAws(fmt("AWS error uploading '%s'", path), auto result = checkAws(fmt("AWS error uploading '%s'", path),
s3Helper.client->PutObject(request)); s3Helper.client->PutObject(request));
act.progress(size, size);
} }
auto now2 = std::chrono::steady_clock::now(); auto now2 = std::chrono::steady_clock::now();

View file

@ -42,7 +42,7 @@
/* Includes required for chroot support. */ /* Includes required for chroot support. */
#ifdef __linux__ #ifdef __linux__
# include "nix/store/fchmodat2-compat.hh" # include "linux/fchmodat2-compat.hh"
# include <sys/ioctl.h> # include <sys/ioctl.h>
# include <net/if.h> # include <net/if.h>
# include <netinet/ip.h> # include <netinet/ip.h>

View file

@ -1,41 +0,0 @@
#pragma once
#include "nix/util/fs-sink.hh"
namespace nix::test {
/**
* A `FileSystemObjectSink` that traces calls, writing to stderr.
*/
class TracingFileSystemObjectSink : public virtual FileSystemObjectSink
{
FileSystemObjectSink & sink;
public:
TracingFileSystemObjectSink(FileSystemObjectSink & sink)
: sink(sink)
{
}
void createDirectory(const CanonPath & path) override;
void createRegularFile(const CanonPath & path, std::function<void(CreateRegularFileSink &)> fn) override;
void createSymlink(const CanonPath & path, const std::string & target) override;
};
/**
* A `ExtendedFileSystemObjectSink` that traces calls, writing to stderr.
*/
class TracingExtendedFileSystemObjectSink : public TracingFileSystemObjectSink, public ExtendedFileSystemObjectSink
{
ExtendedFileSystemObjectSink & sink;
public:
TracingExtendedFileSystemObjectSink(ExtendedFileSystemObjectSink & sink)
: TracingFileSystemObjectSink(sink)
, sink(sink)
{
}
void createHardlink(const CanonPath & path, const CanonPath & target) override;
};
}

View file

@ -1,34 +0,0 @@
#include <iostream>
#include "nix/tracing-file-system-object-sink.hh"
namespace nix::test {
void TracingFileSystemObjectSink::createDirectory(const CanonPath & path)
{
std::cerr << "createDirectory(" << path << ")\n";
sink.createDirectory(path);
}
void TracingFileSystemObjectSink::createRegularFile(
const CanonPath & path, std::function<void(CreateRegularFileSink &)> fn)
{
std::cerr << "createRegularFile(" << path << ")\n";
sink.createRegularFile(path, [&](CreateRegularFileSink & crf) {
// We could wrap this and trace about the chunks of data and such
fn(crf);
});
}
void TracingFileSystemObjectSink::createSymlink(const CanonPath & path, const std::string & target)
{
std::cerr << "createSymlink(" << path << ", target: " << target << ")\n";
sink.createSymlink(path, target);
}
void TracingExtendedFileSystemObjectSink::createHardlink(const CanonPath & path, const CanonPath & target)
{
std::cerr << "createHardlink(" << path << ", target: " << target << ")\n";
sink.createHardlink(path, target);
}
} // namespace nix::test

View file

@ -59,6 +59,7 @@ sources = files(
'json-utils.cc', 'json-utils.cc',
'logging.cc', 'logging.cc',
'lru-cache.cc', 'lru-cache.cc',
'monitorfdhup.cc',
'nix_api_util.cc', 'nix_api_util.cc',
'pool.cc', 'pool.cc',
'position.cc', 'position.cc',

View file

@ -1,5 +1,5 @@
#include "util.hh" #include "nix/util/util.hh"
#include "monitor-fd.hh" #include "nix/util/monitor-fd.hh"
#include <sys/file.h> #include <sys/file.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>

View file

@ -26,6 +26,11 @@ static inline bool getInterrupted();
*/ */
void setInterruptThrown(); void setInterruptThrown();
/**
* @note Does nothing on Windows
*/
static inline bool isInterrupted();
/** /**
* @note Does nothing on Windows * @note Does nothing on Windows
*/ */

View file

@ -85,17 +85,22 @@ static inline bool getInterrupted()
return unix::_isInterrupted; return unix::_isInterrupted;
} }
static inline bool isInterrupted()
{
using namespace unix;
return _isInterrupted || (interruptCheck && interruptCheck());
}
/** /**
* Throw `Interrupted` exception if the process has been interrupted. * Throw `Interrupted` exception if the process has been interrupted.
* *
* Call this in long-running loops and between slow operations to terminate * Call this in long-running loops and between slow operations to terminate
* them as needed. * them as needed.
*/ */
void inline checkInterrupt() inline void checkInterrupt()
{ {
using namespace unix; if (isInterrupted())
if (_isInterrupted || (interruptCheck && interruptCheck())) unix::_interrupted();
_interrupted();
} }
/** /**

View file

@ -22,7 +22,12 @@ inline void setInterruptThrown()
/* Do nothing for now */ /* Do nothing for now */
} }
void inline checkInterrupt() static inline bool isInterrupted()
{
/* Do nothing for now */
}
inline void checkInterrupt()
{ {
/* Do nothing for now */ /* Do nothing for now */
} }

View file

@ -55,7 +55,7 @@ struct CmdBuild : InstallablesCommand, MixDryRun, MixJSON, MixProfile
.description = "Use *path* as prefix for the symlinks to the build results. It defaults to `result`.", .description = "Use *path* as prefix for the symlinks to the build results. It defaults to `result`.",
.labels = {"path"}, .labels = {"path"},
.handler = {&outLink}, .handler = {&outLink},
.completer = completePath .completer = completePath,
}); });
addFlag({ addFlag({

View file

@ -24,7 +24,7 @@ struct CmdBundle : InstallableValueCommand
.handler = {&bundler}, .handler = {&bundler},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, getStore(), prefix); completeFlakeRef(completions, getStore(), prefix);
}} }},
}); });
addFlag({ addFlag({
@ -33,7 +33,7 @@ struct CmdBundle : InstallableValueCommand
.description = "Override the name of the symlink to the build result. It defaults to the base name of the app.", .description = "Override the name of the symlink to the build result. It defaults to the base name of the app.",
.labels = {"path"}, .labels = {"path"},
.handler = {&outLink}, .handler = {&outLink},
.completer = completePath .completer = completePath,
}); });
} }

View file

@ -21,7 +21,7 @@ struct CmdCopy : virtual CopyCommand, virtual BuiltPathsCommand, MixProfile
.description = "Create symlinks prefixed with *path* to the top-level store paths fetched from the source store.", .description = "Create symlinks prefixed with *path* to the top-level store paths fetched from the source store.",
.labels = {"path"}, .labels = {"path"},
.handler = {&outLink}, .handler = {&outLink},
.completer = completePath .completer = completePath,
}); });
addFlag({ addFlag({

View file

@ -21,7 +21,7 @@ struct CmdShowDerivation : InstallablesCommand
.longName = "recursive", .longName = "recursive",
.shortName = 'r', .shortName = 'r',
.description = "Include the dependencies of the specified derivations.", .description = "Include the dependencies of the specified derivations.",
.handler = {&recursive, true} .handler = {&recursive, true},
}); });
} }

View file

@ -334,7 +334,7 @@ struct Common : InstallableCommand, MixProfile
.labels = {"installable", "outputs-dir"}, .labels = {"installable", "outputs-dir"},
.handler = {[&](std::string installable, std::string outputsDir) { .handler = {[&](std::string installable, std::string outputsDir) {
redirects.push_back({installable, outputsDir}); redirects.push_back({installable, outputsDir});
}} }},
}); });
} }
@ -524,7 +524,7 @@ struct CmdDevelop : Common, MixEnvironment
.handler = {[&](std::vector<std::string> ss) { .handler = {[&](std::vector<std::string> ss) {
if (ss.empty()) throw UsageError("--command requires at least one argument"); if (ss.empty()) throw UsageError("--command requires at least one argument");
command = ss; command = ss;
}} }},
}); });
addFlag({ addFlag({

View file

@ -38,8 +38,8 @@ struct CmdShell : InstallablesCommand, MixEnvironment
CmdShell() CmdShell()
{ {
addFlag( addFlag({
{.longName = "command", .longName = "command",
.shortName = 'c', .shortName = 'c',
.description = "Command and arguments to be executed, defaulting to `$SHELL`", .description = "Command and arguments to be executed, defaulting to `$SHELL`",
.labels = {"command", "args"}, .labels = {"command", "args"},
@ -47,7 +47,8 @@ struct CmdShell : InstallablesCommand, MixEnvironment
if (ss.empty()) if (ss.empty())
throw UsageError("--command requires at least one argument"); throw UsageError("--command requires at least one argument");
command = ss; command = ss;
}}}); }},
});
} }
std::string description() override std::string description() override

View file

@ -90,7 +90,7 @@ public:
.handler={&flakeUrl}, .handler={&flakeUrl},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeRef(completions, getStore(), prefix); completeFlakeRef(completions, getStore(), prefix);
}} }},
}); });
expectArgs({ expectArgs({
.label="inputs", .label="inputs",
@ -111,7 +111,7 @@ public:
}}, }},
.completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) { .completer = {[&](AddCompletions & completions, size_t, std::string_view prefix) {
completeFlakeInputAttrPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix); completeFlakeInputAttrPath(completions, getEvalState(), getFlakeRefsForCompletion(), prefix);
}} }},
}); });
/* Remove flags that don't make sense. */ /* Remove flags that don't make sense. */
@ -336,12 +336,12 @@ struct CmdFlakeCheck : FlakeCommand
addFlag({ addFlag({
.longName = "no-build", .longName = "no-build",
.description = "Do not build checks.", .description = "Do not build checks.",
.handler = {&build, false} .handler = {&build, false},
}); });
addFlag({ addFlag({
.longName = "all-systems", .longName = "all-systems",
.description = "Check the outputs for all systems.", .description = "Check the outputs for all systems.",
.handler = {&checkAllSystems, true} .handler = {&checkAllSystems, true},
}); });
} }
@ -874,7 +874,7 @@ struct CmdFlakeInitCommon : virtual Args, EvalCommand
defaultTemplateAttrPathsPrefixes, defaultTemplateAttrPathsPrefixes,
defaultTemplateAttrPaths, defaultTemplateAttrPaths,
prefix); prefix);
}} }},
}); });
} }
@ -1034,7 +1034,7 @@ struct CmdFlakeClone : FlakeCommand
.shortName = 'f', .shortName = 'f',
.description = "Clone the flake to path *dest*.", .description = "Clone the flake to path *dest*.",
.labels = {"path"}, .labels = {"path"},
.handler = {&destDir} .handler = {&destDir},
}); });
} }
@ -1057,7 +1057,7 @@ struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
.longName = "to", .longName = "to",
.description = "URI of the destination Nix store", .description = "URI of the destination Nix store",
.labels = {"store-uri"}, .labels = {"store-uri"},
.handler = {&dstUri} .handler = {&dstUri},
}); });
} }
@ -1137,12 +1137,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
addFlag({ addFlag({
.longName = "legacy", .longName = "legacy",
.description = "Show the contents of the `legacyPackages` output.", .description = "Show the contents of the `legacyPackages` output.",
.handler = {&showLegacy, true} .handler = {&showLegacy, true},
}); });
addFlag({ addFlag({
.longName = "all-systems", .longName = "all-systems",
.description = "Show the contents of outputs for all systems.", .description = "Show the contents of outputs for all systems.",
.handler = {&showAllSystems, true} .handler = {&showAllSystems, true},
}); });
} }
@ -1443,7 +1443,7 @@ struct CmdFlakePrefetch : FlakeCommand, MixJSON
.description = "Create symlink named *path* to the resulting store path.", .description = "Create symlink named *path* to the resulting store path.",
.labels = {"path"}, .labels = {"path"},
.handler = {&outLink}, .handler = {&outLink},
.completer = completePath .completer = completePath,
}); });
} }

View file

@ -7,7 +7,7 @@ project('nix', 'cpp',
'errorlogs=true', # Please print logs for tests that fail 'errorlogs=true', # Please print logs for tests that fail
'localstatedir=/nix/var', 'localstatedir=/nix/var',
], ],
meson_version : '>= 1.1', meson_version : '>= 1.4',
license : 'LGPL-2.1-or-later', license : 'LGPL-2.1-or-later',
) )

View file

@ -275,7 +275,7 @@ struct CmdStorePrefetchFile : StoreCommand, MixJSON
.longName = "name", .longName = "name",
.description = "Override the name component of the resulting store path. It defaults to the base name of *url*.", .description = "Override the name component of the resulting store path. It defaults to the base name of *url*.",
.labels = {"name"}, .labels = {"name"},
.handler = {&name} .handler = {&name},
}); });
addFlag({ addFlag({
@ -284,7 +284,7 @@ struct CmdStorePrefetchFile : StoreCommand, MixJSON
.labels = {"hash"}, .labels = {"hash"},
.handler = {[&](std::string s) { .handler = {[&](std::string s) {
expectedHash = Hash::parseAny(s, hashAlgo); expectedHash = Hash::parseAny(s, hashAlgo);
}} }},
}); });
addFlag(flag::hashAlgo("hash-type", &hashAlgo)); addFlag(flag::hashAlgo("hash-type", &hashAlgo));

View file

@ -104,7 +104,7 @@ struct CmdSign : StorePathsCommand
.description = "File containing the secret signing key.", .description = "File containing the secret signing key.",
.labels = {"file"}, .labels = {"file"},
.handler = {&secretKeyFile}, .handler = {&secretKeyFile},
.completer = completePath .completer = completePath,
}); });
} }

View file

@ -16,7 +16,7 @@ struct CmdStoreDelete : StorePathsCommand
addFlag({ addFlag({
.longName = "ignore-liveness", .longName = "ignore-liveness",
.description = "Do not check whether the paths are reachable from a root.", .description = "Do not check whether the paths are reachable from a root.",
.handler = {&options.ignoreLiveness, true} .handler = {&options.ignoreLiveness, true},
}); });
} }

View file

@ -17,7 +17,7 @@ struct CmdStoreGC : StoreCommand, MixDryRun
.longName = "max", .longName = "max",
.description = "Stop after freeing *n* bytes of disk space.", .description = "Stop after freeing *n* bytes of disk space.",
.labels = {"n"}, .labels = {"n"},
.handler = {&options.maxFreed} .handler = {&options.maxFreed},
}); });
} }

View file

@ -23,14 +23,14 @@ struct CmdUpgradeNix : MixDryRun, StoreCommand
.shortName = 'p', .shortName = 'p',
.description = "The path to the Nix profile to upgrade.", .description = "The path to the Nix profile to upgrade.",
.labels = {"profile-dir"}, .labels = {"profile-dir"},
.handler = {&profileDir} .handler = {&profileDir},
}); });
addFlag({ addFlag({
.longName = "nix-store-paths-url", .longName = "nix-store-paths-url",
.description = "The URL of the file that contains the store paths of the latest Nix release.", .description = "The URL of the file that contains the store paths of the latest Nix release.",
.labels = {"url"}, .labels = {"url"},
.handler = {&(std::string&) settings.upgradeNixStorePathUrl} .handler = {&(std::string&) settings.upgradeNixStorePathUrl},
}); });
} }

View file

@ -37,7 +37,7 @@ struct CmdVerify : StorePathsCommand
.shortName = 's', .shortName = 's',
.description = "Use signatures from the specified store.", .description = "Use signatures from the specified store.",
.labels = {"store-uri"}, .labels = {"store-uri"},
.handler = {[&](std::string s) { substituterUris.push_back(s); }} .handler = {[&](std::string s) { substituterUris.push_back(s); }},
}); });
addFlag({ addFlag({
@ -45,7 +45,7 @@ struct CmdVerify : StorePathsCommand
.shortName = 'n', .shortName = 'n',
.description = "Require that each path is signed by at least *n* different keys.", .description = "Require that each path is signed by at least *n* different keys.",
.labels = {"n"}, .labels = {"n"},
.handler = {&sigsNeeded} .handler = {&sigsNeeded},
}); });
} }

View file

@ -6,6 +6,6 @@ file=build-hook-ca-floating.nix
enableFeatures "ca-derivations" enableFeatures "ca-derivations"
CONTENT_ADDRESSED=true NIX_TESTS_CA_BY_DEFAULT=true
source build-remote.sh source build-remote.sh

View file

@ -13,7 +13,7 @@ unset NIX_STATE_DIR
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; } function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
EXTRA_SYSTEM_FEATURES=() EXTRA_SYSTEM_FEATURES=()
if [[ -n "${CONTENT_ADDRESSED-}" ]]; then if [[ -n "${NIX_TESTS_CA_BY_DEFAULT-}" ]]; then
EXTRA_SYSTEM_FEATURES=("ca-derivations") EXTRA_SYSTEM_FEATURES=("ca-derivations")
fi fi

View file

@ -0,0 +1,6 @@
#!/usr/bin/env bash
export NIX_TESTS_CA_BY_DEFAULT=1
cd ..
source derivation-advanced-attributes.sh

View file

@ -8,10 +8,11 @@ suites += {
'name': 'ca', 'name': 'ca',
'deps': [], 'deps': [],
'tests': [ 'tests': [
'build-cache.sh',
'build-with-garbage-path.sh', 'build-with-garbage-path.sh',
'build.sh', 'build.sh',
'build-cache.sh',
'concurrent-builds.sh', 'concurrent-builds.sh',
'derivation-advanced-attributes.sh',
'derivation-json.sh', 'derivation-json.sh',
'duplicate-realisation-in-closure.sh', 'duplicate-realisation-in-closure.sh',
'eval-store.sh', 'eval-store.sh',

View file

@ -2,6 +2,6 @@
source common.sh source common.sh
CONTENT_ADDRESSED=true NIX_TESTS_CA_BY_DEFAULT=true
cd .. cd ..
source ./nix-shell.sh source ./nix-shell.sh

View file

@ -12,11 +12,19 @@ badExitCode=0
store="$TEST_ROOT/store" store="$TEST_ROOT/store"
if [[ -z "${NIX_TESTS_CA_BY_DEFAULT:-}" ]]; then
drvDir=ia
flags=(--arg contentAddress false)
else
drvDir=ca
flags=(--arg contentAddress true --extra-experimental-features ca-derivations)
fi
for nixFile in derivation/*.nix; do for nixFile in derivation/*.nix; do
drvPath=$(env -u NIX_STORE nix-instantiate --store "$store" --pure-eval --expr "$(< "$nixFile")") drvPath=$(env -u NIX_STORE nix-instantiate --store "$store" --pure-eval "${flags[@]}" --expr "$(< "$nixFile")")
testName=$(basename "$nixFile" .nix) testName=$(basename "$nixFile" .nix)
got="${store}${drvPath}" got="${store}${drvPath}"
expected="derivation/$testName.drv" expected="derivation/${drvDir}/${testName}.drv"
diffAndAcceptInner "$testName" "$got" "$expected" diffAndAcceptInner "$testName" "$got" "$expected"
done done

View file

@ -1,6 +1,24 @@
derivation { { contentAddress }:
name = "advanced-attributes-defaults";
let
caArgs =
if contentAddress then
{
__contentAddressed = true;
outputHashMode = "recursive";
outputHashAlgo = "sha256";
}
else
{ };
derivation' = args: derivation (caArgs // args);
system = "my-system"; system = "my-system";
in
derivation' {
inherit system;
name = "advanced-attributes-defaults";
builder = "/bin/bash"; builder = "/bin/bash";
args = [ args = [
"-c" "-c"

View file

@ -1,6 +1,24 @@
derivation { { contentAddress }:
name = "advanced-attributes-structured-attrs-defaults";
let
caArgs =
if contentAddress then
{
__contentAddressed = true;
outputHashMode = "recursive";
outputHashAlgo = "sha256";
}
else
{ };
derivation' = args: derivation (caArgs // args);
system = "my-system"; system = "my-system";
in
derivation' {
inherit system;
name = "advanced-attributes-structured-attrs-defaults";
builder = "/bin/bash"; builder = "/bin/bash";
args = [ args = [
"-c" "-c"

View file

@ -1 +0,0 @@
Derive([("bin","/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin","",""),("dev","/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev","",""),("out","/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs","","")],[("/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv",["out"]),("/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv",["out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"],\"disallowedRequisites\":[\"/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"],\"allowedRequisites\":[\"/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}"),("bin","/nix/store/pbzb48v0ycf80jgligcp4n8z0rblna4n-advanced-attributes-structured-attrs-bin"),("dev","/nix/store/7xapi8jv7flcz1qq8jhw55ar8ag8hldh-advanced-attributes-structured-attrs-dev"),("out","/nix/store/mpq3l1l1qc2yr50q520g08kprprwv79f-advanced-attributes-structured-attrs")])

View file

@ -1,6 +1,21 @@
{ contentAddress }:
let let
caArgs =
if contentAddress then
{
__contentAddressed = true;
outputHashMode = "recursive";
outputHashAlgo = "sha256";
}
else
{ };
derivation' = args: derivation (caArgs // args);
system = "my-system"; system = "my-system";
foo = derivation {
foo = derivation' {
inherit system; inherit system;
name = "foo"; name = "foo";
builder = "/bin/bash"; builder = "/bin/bash";
@ -8,8 +23,13 @@ let
"-c" "-c"
"echo foo > $out" "echo foo > $out"
]; ];
outputs = [
"out"
"dev"
];
}; };
bar = derivation {
bar = derivation' {
inherit system; inherit system;
name = "bar"; name = "bar";
builder = "/bin/bash"; builder = "/bin/bash";
@ -17,9 +37,14 @@ let
"-c" "-c"
"echo bar > $out" "echo bar > $out"
]; ];
outputs = [
"out"
"dev"
];
}; };
in in
derivation { derivation' {
inherit system; inherit system;
name = "advanced-attributes-structured-attrs"; name = "advanced-attributes-structured-attrs";
builder = "/bin/bash"; builder = "/bin/bash";
@ -41,11 +66,11 @@ derivation {
outputChecks = { outputChecks = {
out = { out = {
allowedReferences = [ foo ]; allowedReferences = [ foo ];
allowedRequisites = [ foo ]; allowedRequisites = [ foo.dev ];
}; };
bin = { bin = {
disallowedReferences = [ bar ]; disallowedReferences = [ bar ];
disallowedRequisites = [ bar ]; disallowedRequisites = [ bar.dev ];
}; };
dev = { dev = {
maxSize = 789; maxSize = 789;

View file

@ -1 +0,0 @@
Derive([("out","/nix/store/33a6fdmn8q9ih9d7npbnrxn2q56a4l8q-advanced-attributes","","")],[("/nix/store/4xm4wccqsvagz9gjksn24s7rip2fdy7v-foo.drv",["out"]),("/nix/store/plsq5jbr5nhgqwcgb2qxw7jchc09dnl8-bar.drv",["out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__darwinAllowLocalNetworking","1"),("__impureHostDeps","/usr/bin/ditto"),("__noChroot","1"),("__sandboxProfile","sandcastle"),("allowSubstitutes",""),("allowedReferences","/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"),("allowedRequisites","/nix/store/3c08bzb71z4wiag719ipjxr277653ynp-foo"),("builder","/bin/bash"),("disallowedReferences","/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"),("disallowedRequisites","/nix/store/7rhsm8i393hm1wcsmph782awg1hi2f7x-bar"),("impureEnvVars","UNICORN"),("name","advanced-attributes"),("out","/nix/store/33a6fdmn8q9ih9d7npbnrxn2q56a4l8q-advanced-attributes"),("preferLocalBuild","1"),("requiredSystemFeatures","rainbow uid-range"),("system","my-system")])

View file

@ -1,6 +1,21 @@
{ contentAddress }:
let let
caArgs =
if contentAddress then
{
__contentAddressed = true;
outputHashMode = "recursive";
outputHashAlgo = "sha256";
}
else
{ };
derivation' = args: derivation (caArgs // args);
system = "my-system"; system = "my-system";
foo = derivation {
foo = derivation' {
inherit system; inherit system;
name = "foo"; name = "foo";
builder = "/bin/bash"; builder = "/bin/bash";
@ -8,8 +23,13 @@ let
"-c" "-c"
"echo foo > $out" "echo foo > $out"
]; ];
outputs = [
"out"
"dev"
];
}; };
bar = derivation {
bar = derivation' {
inherit system; inherit system;
name = "bar"; name = "bar";
builder = "/bin/bash"; builder = "/bin/bash";
@ -17,9 +37,14 @@ let
"-c" "-c"
"echo bar > $out" "echo bar > $out"
]; ];
outputs = [
"out"
"dev"
];
}; };
in in
derivation { derivation' {
inherit system; inherit system;
name = "advanced-attributes"; name = "advanced-attributes";
builder = "/bin/bash"; builder = "/bin/bash";
@ -33,9 +58,9 @@ derivation {
impureEnvVars = [ "UNICORN" ]; impureEnvVars = [ "UNICORN" ];
__darwinAllowLocalNetworking = true; __darwinAllowLocalNetworking = true;
allowedReferences = [ foo ]; allowedReferences = [ foo ];
allowedRequisites = [ foo ]; allowedRequisites = [ foo.dev ];
disallowedReferences = [ bar ]; disallowedReferences = [ bar ];
disallowedRequisites = [ bar ]; disallowedRequisites = [ bar.dev ];
requiredSystemFeatures = [ requiredSystemFeatures = [
"rainbow" "rainbow"
"uid-range" "uid-range"

View file

@ -0,0 +1 @@
Derive([("out","","r:sha256","")],[],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("builder","/bin/bash"),("name","advanced-attributes-defaults"),("out","/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"),("outputHashAlgo","sha256"),("outputHashMode","recursive"),("system","my-system")])

View file

@ -0,0 +1 @@
Derive([("dev","","r:sha256",""),("out","","r:sha256","")],[],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"builder\":\"/bin/bash\",\"name\":\"advanced-attributes-structured-attrs-defaults\",\"outputHashAlgo\":\"sha256\",\"outputHashMode\":\"recursive\",\"outputs\":[\"out\",\"dev\"],\"system\":\"my-system\"}"),("dev","/02qcpld1y6xhs5gz9bchpxaw0xdhmsp5dv88lh25r2ss44kh8dxz"),("out","/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9")])

View file

@ -0,0 +1 @@
Derive([("bin","","r:sha256",""),("dev","","r:sha256",""),("out","","r:sha256","")],[("/nix/store/j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",["dev","out"]),("/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",["dev","out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g\"],\"disallowedRequisites\":[\"/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9\"],\"allowedRequisites\":[\"/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z\"]}},\"outputHashAlgo\":\"sha256\",\"outputHashMode\":\"recursive\",\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}"),("bin","/04f3da1kmbr67m3gzxikmsl4vjz5zf777sv6m14ahv22r65aac9m"),("dev","/02qcpld1y6xhs5gz9bchpxaw0xdhmsp5dv88lh25r2ss44kh8dxz"),("out","/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9")])

View file

@ -0,0 +1 @@
Derive([("out","","r:sha256","")],[("/nix/store/j56sf12rxpcv5swr14vsjn5cwm6bj03h-foo.drv",["dev","out"]),("/nix/store/qnml92yh97a6fbrs2m5qg5cqlc8vni58-bar.drv",["dev","out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__darwinAllowLocalNetworking","1"),("__impureHostDeps","/usr/bin/ditto"),("__noChroot","1"),("__sandboxProfile","sandcastle"),("allowSubstitutes",""),("allowedReferences","/164j69y6zir9z0339n8pjigg3rckinlr77bxsavzizdaaljb7nh9"),("allowedRequisites","/0nr45p69vn6izw9446wsh9bng9nndhvn19kpsm4n96a5mycw0s4z"),("builder","/bin/bash"),("disallowedReferences","/0nyw57wm2iicnm9rglvjmbci3ikmcp823czdqdzdcgsnnwqps71g"),("disallowedRequisites","/07f301yqyz8c6wf6bbbavb2q39j4n8kmcly1s09xadyhgy6x2wr8"),("impureEnvVars","UNICORN"),("name","advanced-attributes"),("out","/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"),("outputHashAlgo","sha256"),("outputHashMode","recursive"),("preferLocalBuild","1"),("requiredSystemFeatures","rainbow uid-range"),("system","my-system")])

View file

@ -0,0 +1 @@
Derive([("bin","/nix/store/qjjj3zrlimpjbkk686m052b3ks9iz2sl-advanced-attributes-structured-attrs-bin","",""),("dev","/nix/store/lpz5grl48v93pdadavyg5is1rqvfdipf-advanced-attributes-structured-attrs-dev","",""),("out","/nix/store/nzvz1bmh1g89a5dkpqcqan0av7q3hgv3-advanced-attributes-structured-attrs","","")],[("/nix/store/afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv",["dev","out"]),("/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv",["dev","out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__json","{\"__darwinAllowLocalNetworking\":true,\"__impureHostDeps\":[\"/usr/bin/ditto\"],\"__noChroot\":true,\"__sandboxProfile\":\"sandcastle\",\"allowSubstitutes\":false,\"builder\":\"/bin/bash\",\"impureEnvVars\":[\"UNICORN\"],\"name\":\"advanced-attributes-structured-attrs\",\"outputChecks\":{\"bin\":{\"disallowedReferences\":[\"/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar\"],\"disallowedRequisites\":[\"/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev\"]},\"dev\":{\"maxClosureSize\":5909,\"maxSize\":789},\"out\":{\"allowedReferences\":[\"/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo\"],\"allowedRequisites\":[\"/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev\"]}},\"outputs\":[\"out\",\"bin\",\"dev\"],\"preferLocalBuild\":true,\"requiredSystemFeatures\":[\"rainbow\",\"uid-range\"],\"system\":\"my-system\"}"),("bin","/nix/store/qjjj3zrlimpjbkk686m052b3ks9iz2sl-advanced-attributes-structured-attrs-bin"),("dev","/nix/store/lpz5grl48v93pdadavyg5is1rqvfdipf-advanced-attributes-structured-attrs-dev"),("out","/nix/store/nzvz1bmh1g89a5dkpqcqan0av7q3hgv3-advanced-attributes-structured-attrs")])

View file

@ -0,0 +1 @@
Derive([("out","/nix/store/swkj0mrq0cq3dfli95v4am0427mi2hxf-advanced-attributes","","")],[("/nix/store/afc3vbjbzql750v2lp8gxgaxsajphzih-foo.drv",["dev","out"]),("/nix/store/vj2i49jm2868j2fmqvxm70vlzmzvgv14-bar.drv",["dev","out"])],[],"my-system","/bin/bash",["-c","echo hello > $out"],[("__darwinAllowLocalNetworking","1"),("__impureHostDeps","/usr/bin/ditto"),("__noChroot","1"),("__sandboxProfile","sandcastle"),("allowSubstitutes",""),("allowedReferences","/nix/store/p0hax2lzvjpfc2gwkk62xdglz0fcqfzn-foo"),("allowedRequisites","/nix/store/z0rjzy29v9k5qa4nqpykrbzirj7sd43v-foo-dev"),("builder","/bin/bash"),("disallowedReferences","/nix/store/r5cff30838majxk5mp3ip2diffi8vpaj-bar"),("disallowedRequisites","/nix/store/9b61w26b4avv870dw0ymb6rw4r1hzpws-bar-dev"),("impureEnvVars","UNICORN"),("name","advanced-attributes"),("out","/nix/store/swkj0mrq0cq3dfli95v4am0427mi2hxf-advanced-attributes"),("preferLocalBuild","1"),("requiredSystemFeatures","rainbow uid-range"),("system","my-system")])

View file

@ -220,6 +220,13 @@ nix store gc
nix registry list --flake-registry "file://$registry" --refresh | grepQuiet flake3 nix registry list --flake-registry "file://$registry" --refresh | grepQuiet flake3
mv "$registry.tmp" "$registry" mv "$registry.tmp" "$registry"
# Ensure that locking ignores the user registry.
mkdir -p "$TEST_HOME/.config/nix"
ln -sfn "$registry" "$TEST_HOME/.config/nix/registry.json"
nix flake metadata flake1
expectStderr 1 nix flake update --flake-registry '' --flake "$flake3Dir" | grepQuiet "cannot find flake 'flake:flake1' in the flake registries"
rm "$TEST_HOME/.config/nix/registry.json"
# Test whether flakes are registered as GC roots for offline use. # Test whether flakes are registered as GC roots for offline use.
# FIXME: use tarballs rather than git. # FIXME: use tarballs rather than git.
rm -rf "$TEST_HOME/.cache" rm -rf "$TEST_HOME/.cache"

View file

@ -28,6 +28,7 @@ suites += {
'commit-lock-file-summary.sh', 'commit-lock-file-summary.sh',
'non-flake-inputs.sh', 'non-flake-inputs.sh',
'relative-paths.sh', 'relative-paths.sh',
'relative-paths-lockfile.sh',
'symlink-paths.sh', 'symlink-paths.sh',
'debugger.sh', 'debugger.sh',
'source-paths.sh', 'source-paths.sh',

View file

@ -0,0 +1,73 @@
#!/usr/bin/env bash
source ./common.sh
requireGit
# Test a "vendored" subflake dependency. This is a relative path flake
# which doesn't reference the root flake and has its own lock file.
#
# This might occur in a monorepo for example. The root flake.lock is
# populated from the dependency's flake.lock.
rootFlake="$TEST_ROOT/flake1"
subflake="$rootFlake/sub"
depFlakeA="$TEST_ROOT/depFlakeA"
depFlakeB="$TEST_ROOT/depFlakeB"
rm -rf "$rootFlake"
mkdir -p "$rootFlake" "$subflake" "$depFlakeA" "$depFlakeB"
cat > "$depFlakeA/flake.nix" <<EOF
{
outputs = { self }: {
x = 11;
};
}
EOF
cat > "$depFlakeB/flake.nix" <<EOF
{
outputs = { self }: {
x = 13;
};
}
EOF
[[ $(nix eval "$depFlakeA#x") = 11 ]]
[[ $(nix eval "$depFlakeB#x") = 13 ]]
cat > "$subflake/flake.nix" <<EOF
{
inputs.dep.url = "path:$depFlakeA";
outputs = { self, dep }: {
inherit (dep) x;
y = self.x - 1;
};
}
EOF
cat > "$rootFlake/flake.nix" <<EOF
{
inputs.sub.url = ./sub;
outputs = { self, sub }: {
x = 2;
y = sub.y / self.x;
};
}
EOF
[[ $(nix eval "$subflake#y") = 10 ]]
[[ $(nix eval "$rootFlake#y") = 5 ]]
nix flake update --flake "path:$subflake" --override-input dep "$depFlakeB"
[[ $(nix eval "path:$subflake#y") = 12 ]]
# Expect that changes to sub/flake.lock are propagated to the root flake.
# FIXME: doesn't work at the moment #7730
[[ $(nix eval "$rootFlake#y") = 6 ]] || true
# This will force refresh flake.lock with changes from sub/flake.lock
nix flake update --flake "$rootFlake"
[[ $(nix eval "$rootFlake#y") = 6 ]]

View file

@ -108,3 +108,24 @@ EOF
[[ $(nix eval "$rootFlake#z") = 90 ]] [[ $(nix eval "$rootFlake#z") = 90 ]]
fi fi
# https://github.com/NixOS/nix/pull/10089#discussion_r2041984987
# https://github.com/NixOS/nix/issues/13018
mkdir -p "$TEST_ROOT/issue-13018/example"
(
cd "$TEST_ROOT/issue-13018"
git init
echo '{ outputs = _: { }; }' >flake.nix
cat >example/flake.nix <<EOF
{
inputs.parent.url = ../.;
outputs = { parent, ... }: builtins.seq parent { ok = null; };
}
EOF
git add -N .
cd example
# Important: the error does not trigger for an in-memory lock!
nix flake lock
# would fail:
nix eval .#ok
)

View file

@ -4,7 +4,7 @@ source common.sh
clearStoreIfPossible clearStoreIfPossible
if [[ -n ${CONTENT_ADDRESSED:-} ]]; then if [[ -n ${NIX_TESTS_CA_BY_DEFAULT:-} ]]; then
shellDotNix="$PWD/ca-shell.nix" shellDotNix="$PWD/ca-shell.nix"
else else
shellDotNix="$PWD/shell.nix" shellDotNix="$PWD/shell.nix"

View file

@ -162,15 +162,32 @@ foo + baz
# - Modify the flake # - Modify the flake
# - Re-eval it # - Re-eval it
# - Check that the result has changed # - Check that the result has changed
replResult=$( ( mkfifo repl_fifo
echo "changingThing" nix repl ./flake < repl_fifo > repl_output 2>&1 &
sleep 1 # Leave the repl the time to eval 'foo' repl_pid=$!
exec 3>repl_fifo # Open fifo for writing
echo "changingThing" >&3
for i in $(seq 1 1000); do
if grep -q "beforeChange" repl_output; then
break
fi
cat repl_output
sleep 0.1
done
if [[ "$i" -eq 100 ]]; then
echo "Timed out waiting for beforeChange"
exit 1
fi
sed -i 's/beforeChange/afterChange/' flake/flake.nix sed -i 's/beforeChange/afterChange/' flake/flake.nix
echo ":reload"
echo "changingThing" # Send reload and second command
) | nix repl ./flake) echo ":reload" >&3
echo "$replResult" | grepQuiet -s beforeChange echo "changingThing" >&3
echo "$replResult" | grepQuiet -s afterChange echo "exit" >&3
exec 3>&- # Close fifo
wait $repl_pid # Wait for process to finish
grep -q "afterChange" repl_output
# Test recursive printing and formatting # Test recursive printing and formatting
# Normal output should print attributes in lexicographical order non-recursively # Normal output should print attributes in lexicographical order non-recursively