Merge branch 'read-only-local-store' into overlayfs-store

This commit is contained in:
Ben Radford 2023-06-06 11:09:26 +01:00
commit b852bdb3f8
No known key found for this signature in database
GPG key ID: 9DF5D4640AB888D5
37 changed files with 360 additions and 154 deletions

View file

@ -21,7 +21,7 @@ jobs:
fetch-depth: 0 fetch-depth: 0
- name: Create backport PRs - name: Create backport PRs
# should be kept in sync with `version` # should be kept in sync with `version`
uses: zeebe-io/backport-action@v1.2.0 uses: zeebe-io/backport-action@v1.3.0
with: with:
# Config README: https://github.com/zeebe-io/backport-action#backport-action # Config README: https://github.com/zeebe-io/backport-action#backport-action
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -19,7 +19,10 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with:
# The sandbox would otherwise be disabled by default on Darwin
extra_nix_config: "sandbox = true"
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/cachix-action@v12 - uses: cachix/cachix-action@v12
if: needs.check_secrets.outputs.cachix == 'true' if: needs.check_secrets.outputs.cachix == 'true'
@ -58,7 +61,7 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with: with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- uses: cachix/cachix-action@v12 - uses: cachix/cachix-action@v12
@ -79,7 +82,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with: with:
install_url: '${{needs.installer.outputs.installerURL}}' install_url: '${{needs.installer.outputs.installerURL}}'
install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve" install_options: "--tarball-url-prefix https://${{ env.CACHIX_NAME }}.cachix.org/serve"
@ -106,7 +109,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: cachix/install-nix-action@v20 - uses: cachix/install-nix-action@v21
with: with:
install_url: https://releases.nixos.org/nix/nix-2.13.3/install install_url: https://releases.nixos.org/nix/nix-2.13.3/install
- run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV - run: echo CACHIX_NAME="$(echo $GITHUB_REPOSITORY-install-tests | tr "[A-Z]/" "[a-z]-")" >> $GITHUB_ENV

View file

@ -1 +1 @@
2.16.0 2.17.0

View file

@ -105,6 +105,7 @@
- [CLI guideline](contributing/cli-guideline.md) - [CLI guideline](contributing/cli-guideline.md)
- [Release Notes](release-notes/release-notes.md) - [Release Notes](release-notes/release-notes.md)
- [Release X.Y (202?-??-??)](release-notes/rl-next.md) - [Release X.Y (202?-??-??)](release-notes/rl-next.md)
- [Release 2.16 (2023-05-31)](release-notes/rl-2.16.md)
- [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md) - [Release 2.15 (2023-04-11)](release-notes/rl-2.15.md)
- [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md) - [Release 2.14 (2023-02-28)](release-notes/rl-2.14.md)
- [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md) - [Release 2.13 (2023-01-17)](release-notes/rl-2.13.md)

View file

@ -38,11 +38,9 @@ contains Nix.
> **Warning** > **Warning**
> >
> If you are building via the Nix daemon, it is the Nix daemon user > If you are building via the Nix daemon, it is the Nix daemon user account (that is, `root`) that should have SSH access to a user (not necessarily `root`) on the remote machine.
> account (that is, `root`) that should have SSH access to the remote >
> machine. If you cant or dont want to configure `root` to be able to > If you cant or dont want to configure `root` to be able to access the remote machine, you can use a private Nix store instead by passing e.g. `--store ~/my-nix` when running a Nix command from the local machine.
> access to remote machine, you can use a private Nix store instead by
> passing e.g. `--store ~/my-nix`.
The list of remote machines can be specified on the command line or in The list of remote machines can be specified on the command line or in
the Nix configuration file. The former is convenient for testing. For the Nix configuration file. The former is convenient for testing. For

View file

@ -17,3 +17,27 @@ These constants are built into the Nix language evaluator:
The built-in value `currentSystem` evaluates to the Nix platform The built-in value `currentSystem` evaluates to the Nix platform
identifier for the Nix installation on which the expression is being identifier for the Nix installation on which the expression is being
evaluated, such as `"i686-linux"` or `"x86_64-darwin"`. evaluated, such as `"i686-linux"` or `"x86_64-darwin"`.
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
- [`builtins.currentTime`]{#builtins-currentTime} (integer)
Return the [Unix time](https://en.wikipedia.org/wiki/Unix_time) at first evaluation.
Repeated references to that name will re-use the initially obtained value.
Example:
```console
$ nix repl
Welcome to Nix 2.15.1 Type :? for help.
nix-repl> builtins.currentTime
1683705525
nix-repl> builtins.currentTime
1683705525
```
The [store path](@docroot@/glossary.md#gloss-store-path) of a derivation depending on `currentTime` will differ for each evaluation.
Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).

View file

@ -2,8 +2,11 @@
## Recursive sets ## Recursive sets
Recursive sets are just normal sets, but the attributes can refer to Recursive sets are like normal [attribute sets](./values.md#attribute-set), but the attributes can refer to each other.
each other. For example,
> *rec-attrset* = `rec {` [ *name* `=` *expr* `;` `]`... `}`
Example:
```nix ```nix
rec { rec {
@ -12,7 +15,9 @@ rec {
}.x }.x
``` ```
evaluates to `123`. Note that without `rec` the binding `x = y;` would This evaluates to `123`.
Note that without `rec` the binding `x = y;` would
refer to the variable `y` in the surrounding scope, if one exists, and refer to the variable `y` in the surrounding scope, if one exists, and
would be invalid if no such variable exists. That is, in a normal would be invalid if no such variable exists. That is, in a normal
(non-recursive) set, attributes are not added to the lexical scope; in a (non-recursive) set, attributes are not added to the lexical scope; in a
@ -33,7 +38,10 @@ will crash with an `infinite recursion encountered` error message.
## Let-expressions ## Let-expressions
A let-expression allows you to define local variables for an expression. A let-expression allows you to define local variables for an expression.
For instance,
> *let-in* = `let` [ *identifier* = *expr* ]... `in` *expr*
Example:
```nix ```nix
let let
@ -42,18 +50,19 @@ let
in x + y in x + y
``` ```
evaluates to `"foobar"`. This evaluates to `"foobar"`.
## Inheriting attributes ## Inheriting attributes
When defining a set or in a let-expression it is often convenient to When defining an [attribute set](./values.md#attribute-set) or in a [let-expression](#let-expressions) it is often convenient to copy variables from the surrounding lexical scope (e.g., when you want to propagate attributes).
copy variables from the surrounding lexical scope (e.g., when you want This can be shortened using the `inherit` keyword.
to propagate attributes). This can be shortened using the `inherit`
keyword. For instance, Example:
```nix ```nix
let x = 123; in let x = 123; in
{ inherit x; {
inherit x;
y = 456; y = 456;
} }
``` ```
@ -62,15 +71,23 @@ is equivalent to
```nix ```nix
let x = 123; in let x = 123; in
{ x = x; {
x = x;
y = 456; y = 456;
} }
``` ```
and both evaluate to `{ x = 123; y = 456; }`. (Note that this works and both evaluate to `{ x = 123; y = 456; }`.
because `x` is added to the lexical scope by the `let` construct.) It is
also possible to inherit attributes from another set. For instance, in > **Note**
this fragment from `all-packages.nix`, >
> This works because `x` is added to the lexical scope by the `let` construct.
It is also possible to inherit attributes from another attribute set.
Example:
In this fragment from `all-packages.nix`,
```nix ```nix
graphviz = (import ../tools/graphics/graphviz) { graphviz = (import ../tools/graphics/graphviz) {

View file

@ -35,17 +35,14 @@
## Attribute selection ## Attribute selection
> *attrset* `.` *attrpath* \[ `or` *expr* \]
Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*. Select the attribute denoted by attribute path *attrpath* from [attribute set] *attrset*.
If the attribute doesnt exist, return the *expr* after `or` if provided, otherwise abort evaluation. If the attribute doesnt exist, return the *expr* after `or` if provided, otherwise abort evaluation.
<!-- FIXME: the following should to into its own language syntax section, but that needs more work to fit in well --> An attribute path is a dot-separated list of [attribute names](./values.md#attribute-set).
An attribute path is a dot-separated list of attribute names. > *attrpath* = *name* [ `.` *name* ]...
An attribute name can be an identifier or a string.
> *attrpath* = *name* [ `.` *name* ]... \
> *name* = *identifier* | *string* \
> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*`
[Attribute selection]: #attribute-selection [Attribute selection]: #attribute-selection

View file

@ -164,9 +164,17 @@ Note that lists are only lazy in values, and they are strict in length.
An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`). An attribute set is a collection of name-value-pairs (called *attributes*) enclosed in curly brackets (`{ }`).
An attribute name can be an identifier or a [string](#string).
An identifier must start with a letter (`a-z`, `A-Z`) or underscore (`_`), and can otherwise contain letters (`a-z`, `A-Z`), numbers (`0-9`), underscores (`_`), apostrophes (`'`), or dashes (`-`).
> *name* = *identifier* | *string* \
> *identifier* ~ `[a-zA-Z_][a-zA-Z0-9_'-]*`
Names and values are separated by an equal sign (`=`). Names and values are separated by an equal sign (`=`).
Each value is an arbitrary expression terminated by a semicolon (`;`). Each value is an arbitrary expression terminated by a semicolon (`;`).
> *attrset* = `{` [ *name* `=` *expr* `;` `]`... `}`
Attributes can appear in any order. Attributes can appear in any order.
An attribute name may only occur once. An attribute name may only occur once.
@ -182,15 +190,19 @@ Example:
This defines a set with attributes named `x`, `text`, `y`. This defines a set with attributes named `x`, `text`, `y`.
Attributes can be selected from a set using the `.` operator. For Attributes can be accessed with the [`.` operator](./operators.md#attribute-selection).
instance,
Example:
```nix ```nix
{ a = "Foo"; b = "Bar"; }.a { a = "Foo"; b = "Bar"; }.a
``` ```
evaluates to `"Foo"`. It is possible to provide a default value in an This evaluates to `"Foo"`.
attribute selection using the `or` keyword:
It is possible to provide a default value in an attribute selection using the `or` keyword.
Example:
```nix ```nix
{ a = "Foo"; b = "Bar"; }.c or "Xyzzy" { a = "Foo"; b = "Bar"; }.c or "Xyzzy"

View file

@ -0,0 +1,8 @@
# Release 2.16 (2023-05-31)
* Speed-up of downloads from binary caches.
The number of parallel downloads (also known as substitutions) has been separated from the [`--max-jobs` setting](../command-ref/conf-file.md#conf-max-jobs).
The new setting is called [`max-substitution-jobs`](../command-ref/conf-file.md#conf-max-substitution-jobs).
The number of parallel downloads is now set to 16 by default (previously, the default was 1 due to the coupling to build jobs).
* The function [`builtins.replaceStrings`](@docroot@/language/builtins.md#builtins-replaceStrings) is now lazy in the value of its second argument `to`. That is, `to` is only evaluated when its corresponding pattern in `from` is matched in the string `s`.

View file

@ -1,6 +1,2 @@
# Release X.Y (202?-??-??) # Release X.Y (202?-??-??)
- Speed-up of downloads from binary caches.
The number of parallel downloads (also known as substitutions) has been separated from the [`--max-jobs` setting](../command-ref/conf-file.md#conf-max-jobs).
The new setting is called [`max-substitution-jobs`](../command-ref/conf-file.md#conf-max-substitution-jobs).
The number of parallel downloads is now set to 16 by default (previously, the default was 1 due to the coupling to build jobs).

View file

@ -119,8 +119,7 @@ release:
TODO: This script requires the right AWS credentials. Document. TODO: This script requires the right AWS credentials. Document.
TODO: This script currently requires a TODO: This script currently requires a
`/home/eelco/Dev/nix-pristine` and `/home/eelco/Dev/nix-pristine`.
`/home/eelco/Dev/nixpkgs-pristine`.
TODO: trigger nixos.org netlify: https://docs.netlify.com/configure-builds/build-hooks/ TODO: trigger nixos.org netlify: https://docs.netlify.com/configure-builds/build-hooks/
@ -141,7 +140,7 @@ release:
$ git checkout master $ git checkout master
$ git pull $ git pull
$ NEW_VERSION=2.13.0 $ NEW_VERSION=2.13.0
$ echo -n $NEW_VERSION > .version $ echo $NEW_VERSION > .version
$ git checkout -b bump-$NEW_VERSION $ git checkout -b bump-$NEW_VERSION
$ git commit -a -m 'Bump version' $ git commit -a -m 'Bump version'
$ git push --set-upstream origin bump-$NEW_VERSION $ git push --set-upstream origin bump-$NEW_VERSION

View file

@ -15,7 +15,6 @@ my $evalId = $ARGV[0] or die "Usage: $0 EVAL-ID\n";
my $releasesBucketName = "nix-releases"; my $releasesBucketName = "nix-releases";
my $channelsBucketName = "nix-channels"; my $channelsBucketName = "nix-channels";
my $nixpkgsDir = "/home/eelco/Dev/nixpkgs-pristine";
my $TMPDIR = $ENV{'TMPDIR'} // "/tmp"; my $TMPDIR = $ENV{'TMPDIR'} // "/tmp";
@ -200,26 +199,22 @@ for my $fn (glob "$tmpDir/*") {
} }
} }
# Update nix-fallback-paths.nix. # Print new nix-fallback-paths.nix.
if ($isLatest) { if ($isLatest) {
system("cd $nixpkgsDir && git pull") == 0 or die;
sub getStorePath { sub getStorePath {
my ($jobName) = @_; my ($jobName) = @_;
my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json')); my $buildInfo = decode_json(fetch("$evalUrl/job/$jobName", 'application/json'));
return $buildInfo->{buildoutputs}->{out}->{path} or die "cannot get store path for '$jobName'"; return $buildInfo->{buildoutputs}->{out}->{path} or die "cannot get store path for '$jobName'";
} }
write_file("$nixpkgsDir/nixos/modules/installer/tools/nix-fallback-paths.nix", print STDERR "nixos/modules/installer/tools/nix-fallback-paths.nix:\n" .
"{\n" . "{\n" .
" x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" . " x86_64-linux = \"" . getStorePath("build.x86_64-linux") . "\";\n" .
" i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" . " i686-linux = \"" . getStorePath("build.i686-linux") . "\";\n" .
" aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" . " aarch64-linux = \"" . getStorePath("build.aarch64-linux") . "\";\n" .
" x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" . " x86_64-darwin = \"" . getStorePath("build.x86_64-darwin") . "\";\n" .
" aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" . " aarch64-darwin = \"" . getStorePath("build.aarch64-darwin") . "\";\n" .
"}\n"); "}\n";
system("cd $nixpkgsDir && git commit -a -m 'nix-fallback-paths.nix: Update to $version'") == 0 or die;
} }
# Update the "latest" symlink. # Update the "latest" symlink.

View file

@ -246,8 +246,15 @@ printf -v _OLD_LINE_FMT "%b" $'\033[1;7;31m-'"$ESC ${RED}%L${ESC}"
printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}" printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}"
_diff() { _diff() {
# macOS Ventura doesn't ship with GNU diff. Print similar output except
# without +/- markers or dimming
if diff --version | grep -q "Apple diff"; then
printf -v CHANGED_GROUP_FORMAT "%b" "${GREEN}%>${RED}%<${ESC}"
diff --changed-group-format="$CHANGED_GROUP_FORMAT" "$@"
else
# simple colorized diff comatible w/ pre `--color` versions # simple colorized diff comatible w/ pre `--color` versions
diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@" diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@"
fi
} }
confirm_rm() { confirm_rm() {

View file

@ -2620,7 +2620,7 @@ Strings EvalSettings::getDefaultNixPath()
{ {
Strings res; Strings res;
auto add = [&](const Path & p, const std::string & s = std::string()) { auto add = [&](const Path & p, const std::string & s = std::string()) {
if (pathExists(p)) { if (pathAccessible(p)) {
if (s.empty()) { if (s.empty()) {
res.push_back(p); res.push_back(p);
} else { } else {

View file

@ -745,7 +745,13 @@ struct EvalSettings : Config
)"}; )"};
Setting<bool> pureEval{this, false, "pure-eval", Setting<bool> pureEval{this, false, "pure-eval",
"Whether to restrict file system and network access to files specified by cryptographic hash."}; R"(
Pure evaluation mode ensures that the result of Nix expressions is fully determined by explicitly declared inputs, and not influenced by external state:
- Restrict file system and network access to files specified by cryptographic hash
- Disable [`bultins.currentSystem`](@docroot@/language/builtin-constants.md#builtins-currentSystem) and [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime)
)"
};
Setting<bool> enableImportFromDerivation{ Setting<bool> enableImportFromDerivation{
this, true, "allow-import-from-derivation", this, true, "allow-import-from-derivation",

View file

@ -1152,16 +1152,14 @@ drvName, Bindings * attrs, Value & v)
if (i->value->type() == nNull) continue; if (i->value->type() == nNull) continue;
} }
if (i->name == state.sContentAddressed) { if (i->name == state.sContentAddressed && state.forceBool(*i->value, noPos, context_below)) {
contentAddressed = state.forceBool(*i->value, noPos, context_below); contentAddressed = true;
if (contentAddressed) experimentalFeatureSettings.require(Xp::CaDerivations);
experimentalFeatureSettings.require(Xp::CaDerivations);
} }
else if (i->name == state.sImpure) { else if (i->name == state.sImpure && state.forceBool(*i->value, noPos, context_below)) {
isImpure = state.forceBool(*i->value, noPos, context_below); isImpure = true;
if (isImpure) experimentalFeatureSettings.require(Xp::ImpureDerivations);
experimentalFeatureSettings.require(Xp::ImpureDerivations);
} }
/* The `args' attribute is special: it supplies the /* The `args' attribute is special: it supplies the
@ -1503,7 +1501,7 @@ static RegisterPrimOp primop_storePath({
causes the path to be *copied* again to the Nix store, resulting causes the path to be *copied* again to the Nix store, resulting
in a new path (e.g. `/nix/store/ld01dnzc-source-source`). in a new path (e.g. `/nix/store/ld01dnzc-source-source`).
This function is not available in pure evaluation mode. Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
)", )",
.fun = prim_storePath, .fun = prim_storePath,
}); });
@ -3910,13 +3908,8 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
for (auto elem : args[0]->listItems()) for (auto elem : args[0]->listItems())
from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings")); from.emplace_back(state.forceString(*elem, pos, "while evaluating one of the strings to replace passed to builtins.replaceStrings"));
std::vector<std::pair<std::string, NixStringContext>> to; std::unordered_map<size_t, std::string> cache;
to.reserve(args[1]->listSize()); auto to = args[1]->listItems();
for (auto elem : args[1]->listItems()) {
NixStringContext ctx;
auto s = state.forceString(*elem, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings");
to.emplace_back(s, std::move(ctx));
}
NixStringContext context; NixStringContext context;
auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings"); auto s = state.forceString(*args[2], context, pos, "while evaluating the third argument passed to builtins.replaceStrings");
@ -3927,10 +3920,19 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
bool found = false; bool found = false;
auto i = from.begin(); auto i = from.begin();
auto j = to.begin(); auto j = to.begin();
for (; i != from.end(); ++i, ++j) size_t j_index = 0;
for (; i != from.end(); ++i, ++j, ++j_index)
if (s.compare(p, i->size(), *i) == 0) { if (s.compare(p, i->size(), *i) == 0) {
found = true; found = true;
res += j->first; auto v = cache.find(j_index);
if (v == cache.end()) {
NixStringContext ctx;
auto ts = state.forceString(**j, ctx, pos, "while evaluating one of the replacement strings passed to builtins.replaceStrings");
v = (cache.emplace(j_index, ts)).first;
for (auto& path : ctx)
context.insert(path);
}
res += v->second;
if (i->empty()) { if (i->empty()) {
if (p < s.size()) if (p < s.size())
res += s[p]; res += s[p];
@ -3938,9 +3940,6 @@ static void prim_replaceStrings(EvalState & state, const PosIdx pos, Value * * a
} else { } else {
p += i->size(); p += i->size();
} }
for (auto& path : j->second)
context.insert(path);
j->second.clear();
break; break;
} }
if (!found) { if (!found) {
@ -3958,7 +3957,11 @@ static RegisterPrimOp primop_replaceStrings({
.args = {"from", "to", "s"}, .args = {"from", "to", "s"},
.doc = R"( .doc = R"(
Given string *s*, replace every occurrence of the strings in *from* Given string *s*, replace every occurrence of the strings in *from*
with the corresponding string in *to*. For example, with the corresponding string in *to*.
The argument *to* is lazy, that is, it is only evaluated when its corresponding pattern in *from* is matched in the string *s*
Example:
```nix ```nix
builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar" builtins.replaceStrings ["oo" "a"] ["a" "i"] "foobar"

View file

@ -286,9 +286,9 @@ static RegisterPrimOp primop_fetchurl({
.name = "__fetchurl", .name = "__fetchurl",
.args = {"url"}, .args = {"url"},
.doc = R"( .doc = R"(
Download the specified URL and return the path of the downloaded Download the specified URL and return the path of the downloaded file.
file. This function is not available if [restricted evaluation
mode](../command-ref/conf-file.md) is enabled. Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
)", )",
.fun = prim_fetchurl, .fun = prim_fetchurl,
}); });
@ -338,8 +338,7 @@ static RegisterPrimOp primop_fetchTarball({
stdenv.mkDerivation { } stdenv.mkDerivation { }
``` ```
This function is not available if [restricted evaluation Not available in [restricted evaluation mode](@docroot@/command-ref/conf-file.md#conf-restrict-eval).
mode](../command-ref/conf-file.md) is enabled.
)", )",
.fun = prim_fetchTarball, .fun = prim_fetchTarball,
}); });
@ -470,14 +469,9 @@ static RegisterPrimOp primop_fetchGit({
} }
``` ```
> **Note** Nix will refetch the branch according to the [`tarball-ttl`](@docroot@/command-ref/conf-file.md#conf-tarball-ttl) setting.
>
> Nix will refetch the branch in accordance with
> the option `tarball-ttl`.
> **Note** This behavior is disabled in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
>
> This behavior is disabled in *Pure evaluation mode*.
- To fetch the content of a checked-out work directory: - To fetch the content of a checked-out work directory:

View file

@ -171,7 +171,7 @@ namespace nix {
hintfmt("value is %s while a string was expected", "an integer"), hintfmt("value is %s while a string was expected", "an integer"),
hintfmt("while evaluating one of the strings to replace passed to builtins.replaceStrings")); hintfmt("while evaluating one of the strings to replace passed to builtins.replaceStrings"));
ASSERT_TRACE2("replaceStrings [ \"old\" ] [ true ] {}", ASSERT_TRACE2("replaceStrings [ \"oo\" ] [ true ] \"foo\"",
TypeError, TypeError,
hintfmt("value is %s while a string was expected", "a Boolean"), hintfmt("value is %s while a string was expected", "a Boolean"),
hintfmt("while evaluating one of the replacement strings passed to builtins.replaceStrings")); hintfmt("while evaluating one of the replacement strings passed to builtins.replaceStrings"));

View file

@ -75,22 +75,28 @@ SourcePath SourcePath::resolveSymlinks() const
int linksAllowed = 1024; int linksAllowed = 1024;
for (auto & component : path) { std::list<std::string> todo;
res.path.push(component); for (auto & c : path)
while (true) { todo.push_back(std::string(c));
if (auto st = res.maybeLstat()) {
while (!todo.empty()) {
auto c = *todo.begin();
todo.pop_front();
if (c == "" || c == ".")
;
else if (c == "..")
res.path.pop();
else {
res.path.push(c);
if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) {
if (!linksAllowed--) if (!linksAllowed--)
throw Error("infinite symlink recursion in path '%s'", path); throw Error("infinite symlink recursion in path '%s'", path);
if (st->type != InputAccessor::tSymlink) break;
auto target = res.readLink(); auto target = res.readLink();
res.path.pop();
if (hasPrefix(target, "/")) if (hasPrefix(target, "/"))
res = CanonPath(target); res.path = CanonPath::root;
else { todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
res.path.pop(); }
res.path.extend(CanonPath(target));
}
} else
break;
} }
} }

View file

@ -357,7 +357,7 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull()
for (auto & [_, status] : initialOutputs) { for (auto & [_, status] : initialOutputs) {
if (!status.known) continue; if (!status.known) continue;
if (buildMode != bmCheck && status.known->isValid()) continue; if (buildMode != bmCheck && status.known->isValid()) continue;
auto p = worker.store.printStorePath(status.known->path); auto p = worker.store.toRealPath(status.known->path);
if (pathExists(chrootRootDir + p)) if (pathExists(chrootRootDir + p))
renameFile((chrootRootDir + p), p); renameFile((chrootRootDir + p), p);
} }
@ -1772,7 +1772,8 @@ void LocalDerivationGoal::runChild()
if (pathExists(path)) if (pathExists(path))
ss.push_back(path); ss.push_back(path);
dirsInChroot.emplace(settings.caFile, "/etc/ssl/certs/ca-certificates.crt"); if (settings.caFile != "")
dirsInChroot.try_emplace("/etc/ssl/certs/ca-certificates.crt", settings.caFile, true);
} }
for (auto & i : ss) dirsInChroot.emplace(i, i); for (auto & i : ss) dirsInChroot.emplace(i, i);

View file

@ -183,7 +183,7 @@ bool Settings::isWSL1()
Path Settings::getDefaultSSLCertFile() Path Settings::getDefaultSSLCertFile()
{ {
for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"}) for (auto & fn : {"/etc/ssl/certs/ca-certificates.crt", "/nix/var/nix/profiles/default/etc/ssl/certs/ca-bundle.crt"})
if (pathExists(fn)) return fn; if (pathAccessible(fn)) return fn;
return ""; return "";
} }

View file

@ -1014,6 +1014,18 @@ public:
| `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` | | `~/.nix-profile` | `$XDG_STATE_HOME/nix/profile` |
| `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` | | `~/.nix-defexpr` | `$XDG_STATE_HOME/nix/defexpr` |
| `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` | | `~/.nix-channels` | `$XDG_STATE_HOME/nix/channels` |
If you already have Nix installed and are using [profiles](@docroot@/package-management/profiles.md) or [channels](@docroot@/package-management/channels.md), you should migrate manually when you enable this option.
If `$XDG_STATE_HOME` is not set, use `$HOME/.local/state/nix` instead of `$XDG_STATE_HOME/nix`.
This can be achieved with the following shell commands:
```sh
nix_state_home=${XDG_STATE_HOME-$HOME/.local/state}/nix
mkdir -p $nix_state_home
mv $HOME/.nix-profile $nix_state_home/profile
mv $HOME/.nix-defexpr $nix_state_home/defexpr
mv $HOME/.nix-channels $nix_state_home/channels
```
)" )"
}; };
}; };

View file

@ -190,7 +190,11 @@ LocalStore::LocalStore(const Params & params)
/* Create missing state directories if they don't already exist. */ /* Create missing state directories if they don't already exist. */
createDirs(realStoreDir); createDirs(realStoreDir);
makeStoreWritable(); if (readOnly) {
experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore);
} else {
makeStoreWritable();
}
createDirs(linksDir); createDirs(linksDir);
Path profilesDir = stateDir + "/profiles"; Path profilesDir = stateDir + "/profiles";
createDirs(profilesDir); createDirs(profilesDir);
@ -202,10 +206,6 @@ LocalStore::LocalStore(const Params & params)
createSymlink(profilesDir, gcRootsDir + "/profiles"); createSymlink(profilesDir, gcRootsDir + "/profiles");
} }
if (readOnly) {
experimentalFeatureSettings.require(Xp::ReadOnlyLocalStore);
}
for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) { for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
createDirs(perUserDir); createDirs(perUserDir);
if (!readOnly) { if (!readOnly) {

View file

@ -9,8 +9,8 @@ static void checkName(std::string_view path, std::string_view name)
if (name.empty()) if (name.empty())
throw BadStorePath("store path '%s' has an empty name", path); throw BadStorePath("store path '%s' has an empty name", path);
if (name.size() > StorePath::MaxPathLen) if (name.size() > StorePath::MaxPathLen)
throw BadStorePath("store path '%s' has a name longer than '%d characters", throw BadStorePath("store path '%s' has a name longer than %d characters",
StorePath::MaxPathLen, path); path, StorePath::MaxPathLen);
// See nameRegexStr for the definition // See nameRegexStr for the definition
for (auto c : name) for (auto c : name)
if (!((c >= '0' && c <= '9') if (!((c >= '0' && c <= '9')

View file

@ -50,6 +50,8 @@ constexpr std::array<ExperimentalFeatureDetails, 14> xpFeatureDetails = {{
or other impure derivations can rely on impure derivations. Finally, or other impure derivations can rely on impure derivations. Finally,
an impure derivation cannot also be an impure derivation cannot also be
[content-addressed](#xp-feature-ca-derivations). [content-addressed](#xp-feature-ca-derivations).
This is a more explicit alternative to using [`builtins.currentTime`](@docroot@/language/builtin-constants.md#builtins-currentTime).
)", )",
}, },
{ {

View file

@ -202,7 +202,7 @@ namespace nix {
} }
TEST(pathExists, bogusPathDoesNotExist) { TEST(pathExists, bogusPathDoesNotExist) {
ASSERT_FALSE(pathExists("/home/schnitzel/darmstadt/pommes")); ASSERT_FALSE(pathExists("/schnitzel/darmstadt/pommes"));
} }
/* ---------------------------------------------------------------------------- /* ----------------------------------------------------------------------------

View file

@ -266,6 +266,17 @@ bool pathExists(const Path & path)
return false; return false;
} }
bool pathAccessible(const Path & path)
{
try {
return pathExists(path);
} catch (SysError & e) {
// swallow EPERM
if (e.errNo == EPERM) return false;
throw;
}
}
Path readLink(const Path & path) Path readLink(const Path & path)
{ {

View file

@ -120,6 +120,14 @@ struct stat lstat(const Path & path);
*/ */
bool pathExists(const Path & path); bool pathExists(const Path & path);
/**
* A version of pathExists that returns false on a permission error.
* Useful for inferring default paths across directories that might not
* be readable.
* @return true iff the given path can be accessed and exists
*/
bool pathAccessible(const Path & path);
/** /**
* Read the contents (target) of a symbolic link. The result is not * Read the contents (target) of a symbolic link. The result is not
* in any way canonicalised. * in any way canonicalised.

View file

@ -259,6 +259,7 @@ struct CmdFlakeInfo : CmdFlakeMetadata
struct CmdFlakeCheck : FlakeCommand struct CmdFlakeCheck : FlakeCommand
{ {
bool build = true; bool build = true;
bool checkAllSystems = false;
CmdFlakeCheck() CmdFlakeCheck()
{ {
@ -267,6 +268,11 @@ struct CmdFlakeCheck : FlakeCommand
.description = "Do not build checks.", .description = "Do not build checks.",
.handler = {&build, false} .handler = {&build, false}
}); });
addFlag({
.longName = "all-systems",
.description = "Check the outputs for all systems.",
.handler = {&checkAllSystems, true}
});
} }
std::string description() override std::string description() override
@ -292,6 +298,7 @@ struct CmdFlakeCheck : FlakeCommand
lockFlags.applyNixConfig = true; lockFlags.applyNixConfig = true;
auto flake = lockFlake(); auto flake = lockFlake();
auto localSystem = std::string(settings.thisSystem.get());
bool hasErrors = false; bool hasErrors = false;
auto reportError = [&](const Error & e) { auto reportError = [&](const Error & e) {
@ -307,6 +314,8 @@ struct CmdFlakeCheck : FlakeCommand
} }
}; };
std::set<std::string> omittedSystems;
// FIXME: rewrite to use EvalCache. // FIXME: rewrite to use EvalCache.
auto resolve = [&] (PosIdx p) { auto resolve = [&] (PosIdx p) {
@ -327,6 +336,15 @@ struct CmdFlakeCheck : FlakeCommand
reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos))); reportError(Error("'%s' is not a valid system type, at %s", system, resolve(pos)));
}; };
auto checkSystemType = [&](const std::string & system, const PosIdx pos) {
if (!checkAllSystems && system != localSystem) {
omittedSystems.insert(system);
return false;
} else {
return true;
}
};
auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional<StorePath> { auto checkDerivation = [&](const std::string & attrPath, Value & v, const PosIdx pos) -> std::optional<StorePath> {
try { try {
auto drvInfo = getDerivation(*state, v, false); auto drvInfo = getDerivation(*state, v, false);
@ -509,16 +527,18 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos, ""); if (checkSystemType(attr_name, attr.pos)) {
for (auto & attr2 : *attr.value->attrs) { state->forceAttrs(*attr.value, attr.pos, "");
auto drvPath = checkDerivation( for (auto & attr2 : *attr.value->attrs) {
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), auto drvPath = checkDerivation(
*attr2.value, attr2.pos); fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
if (drvPath && attr_name == settings.thisSystem.get()) { *attr2.value, attr2.pos);
drvPaths.push_back(DerivedPath::Built { if (drvPath && attr_name == settings.thisSystem.get()) {
.drvPath = *drvPath, drvPaths.push_back(DerivedPath::Built {
.outputs = OutputsSpec::All { }, .drvPath = *drvPath,
}); .outputs = OutputsSpec::All { },
});
}
} }
} }
} }
@ -529,9 +549,11 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
checkApp( if (checkSystemType(attr_name, attr.pos)) {
fmt("%s.%s", name, attr_name), checkApp(
*attr.value, attr.pos); fmt("%s.%s", name, attr_name),
*attr.value, attr.pos);
};
} }
} }
@ -540,11 +562,13 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos, ""); if (checkSystemType(attr_name, attr.pos)) {
for (auto & attr2 : *attr.value->attrs) state->forceAttrs(*attr.value, attr.pos, "");
checkDerivation( for (auto & attr2 : *attr.value->attrs)
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), checkDerivation(
*attr2.value, attr2.pos); fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos);
};
} }
} }
@ -553,11 +577,13 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos, ""); if (checkSystemType(attr_name, attr.pos)) {
for (auto & attr2 : *attr.value->attrs) state->forceAttrs(*attr.value, attr.pos, "");
checkApp( for (auto & attr2 : *attr.value->attrs)
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), checkApp(
*attr2.value, attr2.pos); fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
*attr2.value, attr2.pos);
};
} }
} }
@ -566,9 +592,11 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
checkDerivation( if (checkSystemType(attr_name, attr.pos)) {
fmt("%s.%s", name, attr_name), checkDerivation(
*attr.value, attr.pos); fmt("%s.%s", name, attr_name),
*attr.value, attr.pos);
};
} }
} }
@ -577,9 +605,11 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
checkApp( if (checkSystemType(attr_name, attr.pos) ) {
fmt("%s.%s", name, attr_name), checkApp(
*attr.value, attr.pos); fmt("%s.%s", name, attr_name),
*attr.value, attr.pos);
};
} }
} }
@ -587,6 +617,7 @@ struct CmdFlakeCheck : FlakeCommand
state->forceAttrs(vOutput, pos, ""); state->forceAttrs(vOutput, pos, "");
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
checkSystemName(state->symbols[attr.name], attr.pos); checkSystemName(state->symbols[attr.name], attr.pos);
checkSystemType(state->symbols[attr.name], attr.pos);
// FIXME: do getDerivations? // FIXME: do getDerivations?
} }
} }
@ -636,9 +667,11 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
checkBundler( if (checkSystemType(attr_name, attr.pos)) {
fmt("%s.%s", name, attr_name), checkBundler(
*attr.value, attr.pos); fmt("%s.%s", name, attr_name),
*attr.value, attr.pos);
};
} }
} }
@ -647,12 +680,14 @@ struct CmdFlakeCheck : FlakeCommand
for (auto & attr : *vOutput.attrs) { for (auto & attr : *vOutput.attrs) {
const auto & attr_name = state->symbols[attr.name]; const auto & attr_name = state->symbols[attr.name];
checkSystemName(attr_name, attr.pos); checkSystemName(attr_name, attr.pos);
state->forceAttrs(*attr.value, attr.pos, ""); if (checkSystemType(attr_name, attr.pos)) {
for (auto & attr2 : *attr.value->attrs) { state->forceAttrs(*attr.value, attr.pos, "");
checkBundler( for (auto & attr2 : *attr.value->attrs) {
fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]), checkBundler(
*attr2.value, attr2.pos); fmt("%s.%s.%s", name, attr_name, state->symbols[attr2.name]),
} *attr2.value, attr2.pos);
}
};
} }
} }
@ -685,7 +720,15 @@ struct CmdFlakeCheck : FlakeCommand
} }
if (hasErrors) if (hasErrors)
throw Error("some errors were encountered during the evaluation"); throw Error("some errors were encountered during the evaluation");
}
if (!omittedSystems.empty()) {
warn(
"The check omitted these incompatible systems: %s\n"
"Use '--all-systems' to check all.",
concatStringsSep(", ", omittedSystems)
);
};
};
}; };
static Strings defaultTemplateAttrPathsPrefixes{"templates."}; static Strings defaultTemplateAttrPathsPrefixes{"templates."};

View file

@ -102,6 +102,7 @@ way:
available in the flake. If this is undesirable, specify `path:<directory>` explicitly; available in the flake. If this is undesirable, specify `path:<directory>` explicitly;
For example, if `/foo/bar` is a git repository with the following structure: For example, if `/foo/bar` is a git repository with the following structure:
``` ```
. .
└── baz └── baz

View file

@ -35,3 +35,9 @@ nix-instantiate --eval -E 'assert 1 + 2 == 3; true'
# Check that symlink cycles don't cause a hang. # Check that symlink cycles don't cause a hang.
ln -sfn cycle.nix $TEST_ROOT/cycle.nix ln -sfn cycle.nix $TEST_ROOT/cycle.nix
(! nix eval --file $TEST_ROOT/cycle.nix) (! nix eval --file $TEST_ROOT/cycle.nix)
# Check that relative symlinks are resolved correctly.
mkdir -p $TEST_ROOT/xyzzy $TEST_ROOT/foo
ln -sfn ../xyzzy $TEST_ROOT/foo/bar
printf 123 > $TEST_ROOT/xyzzy/default.nix
[[ $(nix eval --impure --expr "import $TEST_ROOT/foo/bar") = 123 ]]

View file

@ -72,6 +72,8 @@ cat > $flakeDir/flake.nix <<EOF
} }
EOF EOF
checkRes=$(nix flake check --keep-going $flakeDir 2>&1 && fail "nix flake check should have failed" || true) nix flake check $flakeDir
checkRes=$(nix flake check --all-systems --keep-going $flakeDir 2>&1 && fail "nix flake check --all-systems should have failed" || true)
echo "$checkRes" | grepQuiet "packages.system-1.default" echo "$checkRes" | grepQuiet "packages.system-1.default"
echo "$checkRes" | grepQuiet "packages.system-2.default" echo "$checkRes" | grepQuiet "packages.system-2.default"

View file

@ -1 +1 @@
[ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" ] [ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" "fubar" ]

View file

@ -8,4 +8,5 @@ with builtins;
(replaceStrings [""] ["X"] "abc") (replaceStrings [""] ["X"] "abc")
(replaceStrings [""] ["X"] "") (replaceStrings [""] ["X"] "")
(replaceStrings ["-"] ["_"] "a-b") (replaceStrings ["-"] ["_"] "a-b")
(replaceStrings ["oo" "XX"] ["u" (throw "unreachable")] "foobar")
] ]

View file

@ -0,0 +1,29 @@
{ fixed-output }:
with import ./config.nix;
mkDerivation ({
name = "ssl-export";
buildCommand = ''
# Add some indirection, otherwise grepping into the debug output finds the string.
report () { echo CERT_$1_IN_SANDBOX; }
if [ -f /etc/ssl/certs/ca-certificates.crt ]; then
content=$(</etc/ssl/certs/ca-certificates.crt)
if [ "$content" == CERT_CONTENT ]; then
report present
fi
else
report missing
fi
# Always fail, because we do not want to bother with fixed-output
# derivations being cached, and do not want to compute the right hash.
false;
'';
} // (
if fixed-output == "fixed-output"
then { outputHash = "sha256:0000000000000000000000000000000000000000000000000000000000000000"; }
else { }
))

View file

@ -40,3 +40,27 @@ grepQuiet 'may not be deterministic' $TEST_ROOT/log
# Test that sandboxed builds cannot write to /etc easily # Test that sandboxed builds cannot write to /etc easily
(! nix-build -E 'with import ./config.nix; mkDerivation { name = "etc-write"; buildCommand = "echo > /etc/test"; }' --no-out-link --sandbox-paths /nix/store) (! nix-build -E 'with import ./config.nix; mkDerivation { name = "etc-write"; buildCommand = "echo > /etc/test"; }' --no-out-link --sandbox-paths /nix/store)
## Test mounting of SSL certificates into the sandbox
testCert () {
(! nix-build linux-sandbox-cert-test.nix --argstr fixed-output "$2" --no-out-link --sandbox-paths /nix/store --option ssl-cert-file "$3" 2> $TEST_ROOT/log)
cat $TEST_ROOT/log
grepQuiet "CERT_${1}_IN_SANDBOX" $TEST_ROOT/log
}
nocert=$TEST_ROOT/no-cert-file.pem
cert=$TEST_ROOT/some-cert-file.pem
echo -n "CERT_CONTENT" > $cert
# No cert in sandbox when not a fixed-output derivation
testCert missing normal "$cert"
# No cert in sandbox when ssl-cert-file is empty
testCert missing fixed-output ""
# No cert in sandbox when ssl-cert-file is a nonexistent file
testCert missing fixed-output "$nocert"
# Cert in sandbox when ssl-cert-file is set to an existing file
testCert present fixed-output "$cert"