Skip to content

Commit

Permalink
feat: add flag add to MixEnvironment
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanhonof committed Sep 19, 2024
1 parent a45a7e8 commit 289f2ea
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 34 deletions.
79 changes: 54 additions & 25 deletions src/libcmd/command.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <algorithm>
#include <nlohmann/json.hpp>

#include "command.hh"
Expand All @@ -9,8 +10,7 @@
#include "profiles.hh"
#include "repl.hh"
#include "strings.hh"

extern char * * environ __attribute__((weak));
#include "environment-variables.hh"

namespace nix {

Expand Down Expand Up @@ -290,48 +290,77 @@ MixDefaultProfile::MixDefaultProfile()
MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
{
addFlag({
.longName = "ignore-environment",
.longName = "ignore-env",
.aliases = {"ignore-environment"},
.shortName = 'i',
.description = "Clear the entire environment (except those specified with `--keep`).",
.description = "Clear the entire environment, except for those specified with `--keep-env-var`. "
"Alias for `--ignore-environment`.",
.category = environmentVariablesCategory,
.handler = {&ignoreEnvironment, true},
});

addFlag({
.longName = "keep",
.longName = "keep-env-var",
.aliases = {"keep"},
.shortName = 'k',
.description = "Keep the environment variable *name*.",
.description = "Keep the environment variable *name*, when using `--ignore-env`. "
"Alias for `--keep`.",
.category = environmentVariablesCategory,
.labels = {"name"},
.handler = {[&](std::string s) { keep.insert(s); }},
.handler = {[&](std::string s) { keepVars.insert(s); }},
});

addFlag({
.longName = "unset",
.longName = "unset-env-var",
.aliases = {"unset"},
.shortName = 'u',
.description = "Unset the environment variable *name*.",
.description = "Unset the environment variable *name*. "
"Alias for `--unset`.",
.category = environmentVariablesCategory,
.labels = {"name"},
.handler = {[&](std::string s) { unset.insert(s); }},
.handler = {[&](std::string s) { unsetVars.insert(s); }},
});

addFlag({
.longName = "set-env-var",
.shortName = 's',
.description = "Add/override an environment variable *name* with *value*.\n\n"
"> **Notes**\n"
">\n"
"> Duplicate definitions will be overwritten, last one wins.\n\n"
"> Cancles out with `--unset-env-var`.\n\n",
.category = environmentVariablesCategory,
.labels = {"name", "value"},
.handler = {[&](std::string name, std::string value) { setVars.insert_or_assign(name, value); }},
});
}

void MixEnvironment::setEnviron() {
if (ignoreEnvironment) {
if (!unset.empty())
throw UsageError("--unset does not make sense with --ignore-environment");
if (ignoreEnvironment && !unsetVars.empty())
throw UsageError("--unset-env-var does not make sense with --ignore-env");

for (const auto & var : keep) {
auto val = getenv(var.c_str());
if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val));
}
if (!ignoreEnvironment && !keepVars.empty())
throw UsageError("--keep-env-var does not make sense without --ignore-env");

vectorEnv = stringsToCharPtrs(stringsEnv);
environ = vectorEnv.data();
} else {
if (!keep.empty())
throw UsageError("--keep does not make sense without --ignore-environment");
auto env = getEnv();

for (const auto & var : unset)
unsetenv(var.c_str());
}
if (ignoreEnvironment)
std::erase_if(env, [&](const auto & var) {
return !keepVars.contains(var.first);
});

if (!setVars.empty())
for (const auto & [name, value] : setVars)
env[name] = value;

if (!unsetVars.empty())
std::erase_if(env, [&](const auto & var) {
return unsetVars.contains(var.first);
});

replaceEnv(std::move(env));

return;
}

}
13 changes: 7 additions & 6 deletions src/libcmd/command.hh
Original file line number Diff line number Diff line change
Expand Up @@ -315,17 +315,18 @@ struct MixDefaultProfile : MixProfile

struct MixEnvironment : virtual Args {

StringSet keep, unset;
Strings stringsEnv;
std::vector<char*> vectorEnv;
StringSet keepVars;
StringSet unsetVars;
std::map<std::string, std::string> setVars;
bool ignoreEnvironment;

MixEnvironment();

/***
* Modify global environ based on `ignoreEnvironment`, `keep`, and
* `unset`. It's expected that exec will be called before this class
* goes out of scope, otherwise `environ` will become invalid.
* Modify global environ based on `ignoreEnvironment`, `keep`,
* `unset`, and `added`. It's expected that exec will be called
* before this class goes out of scope, otherwise `environ` will
* become invalid.
*/
void setEnviron();
};
Expand Down
2 changes: 2 additions & 0 deletions src/libutil/environment-variables.hh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

namespace nix {

static constexpr auto environmentVariablesCategory = "Options that change environment variables";

/**
* @return an environment variable.
*/
Expand Down
6 changes: 3 additions & 3 deletions tests/functional/flakes/develop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ echo "\$ENVVAR"
EOF
)" = "a" ]]

# Test whether `nix develop --ignore-environment` does _not_ pass through environment variables.
# Test whether `nix develop --ignore-env` does _not_ pass through environment variables.
[[ -z "$(
ENVVAR=a nix develop --ignore-environment --no-write-lock-file .#hello <<EOF
ENVVAR=a nix develop --ignore-env --no-write-lock-file .#hello <<EOF
echo "\$ENVVAR"
EOF
)" ]]
Expand All @@ -63,7 +63,7 @@ EOF

# Test whether `nix develop` with ignore environment sets `SHELL` to nixpkgs#bashInteractive shell.
[[ "$(
SHELL=custom nix develop --ignore-environment --no-write-lock-file .#hello <<EOF
SHELL=custom nix develop --ignore-env --no-write-lock-file .#hello <<EOF
echo "\$SHELL"
EOF
)" -ef "$BASH_INTERACTIVE_EXECUTABLE" ]]
Expand Down

0 comments on commit 289f2ea

Please sign in to comment.