forked from lix-project/lix
WIP: restore /nix/var/nix as source of truth for profiles
See-also: https://github.com/NixOS/nix/pull/5226
Change-Id: I35082c9a59148f7fb132232659c3a254d514d190
This commit is contained in:
parent
92e171a38a
commit
fdb2103358
|
@ -269,7 +269,13 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
|
||||||
|
|
||||||
MixDefaultProfile::MixDefaultProfile()
|
MixDefaultProfile::MixDefaultProfile()
|
||||||
{
|
{
|
||||||
profile = getDefaultProfile();
|
// `nix profile remove` at the least expects this. huh.
|
||||||
|
try {
|
||||||
|
this->profile = ensureDefaultProfile();
|
||||||
|
} catch (SysError const & e) {
|
||||||
|
// We get called in __dumpCli too, so we should not do fallable IO.
|
||||||
|
printTalkative("ignoring error initializing default profile %s", e.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
|
MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
|
||||||
|
|
|
@ -217,6 +217,8 @@ LocalStore::LocalStore(const Params & params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createUser(getUserName(), getuid());
|
||||||
|
|
||||||
/* Optionally, create directories and set permissions for a
|
/* Optionally, create directories and set permissions for a
|
||||||
multi-user install. */
|
multi-user install. */
|
||||||
if (getuid() == 0 && settings.buildUsersGroup != "") {
|
if (getuid() == 0 && settings.buildUsersGroup != "") {
|
||||||
|
@ -1793,6 +1795,21 @@ void LocalStore::signRealisation(Realisation & realisation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocalStore::createUser(std::string_view userName, uid_t userId)
|
||||||
|
{
|
||||||
|
// XXX: previously created gcroots/per-user; should this too?
|
||||||
|
auto const perUserProfile = fmt("%s/profiles/per-user/%s", stateDir, userName);
|
||||||
|
createDirs(perUserProfile);
|
||||||
|
|
||||||
|
if (chmod(perUserProfile.c_str(), 0755) == -1) {
|
||||||
|
throw SysError(errno, "changing permissions of directory '%s'", perUserProfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chown(perUserProfile.c_str(), userId, getgid()) == -1) {
|
||||||
|
throw SysError(errno, "changing owner of directory '%s'", perUserProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LocalStore::signPathInfo(ValidPathInfo & info)
|
void LocalStore::signPathInfo(ValidPathInfo & info)
|
||||||
{
|
{
|
||||||
// FIXME: keep secret keys in memory.
|
// FIXME: keep secret keys in memory.
|
||||||
|
|
|
@ -366,6 +366,8 @@ private:
|
||||||
void signPathInfo(ValidPathInfo & info);
|
void signPathInfo(ValidPathInfo & info);
|
||||||
void signRealisation(Realisation &);
|
void signRealisation(Realisation &);
|
||||||
|
|
||||||
|
void createUser(std::string_view userName, uid_t userId) override;
|
||||||
|
|
||||||
// XXX: Make a generic `Store` method
|
// XXX: Make a generic `Store` method
|
||||||
ContentAddress hashCAPath(
|
ContentAddress hashCAPath(
|
||||||
const ContentAddressMethod & method,
|
const ContentAddressMethod & method,
|
||||||
|
|
|
@ -307,12 +307,7 @@ std::string optimisticLockProfile(const Path & profile)
|
||||||
|
|
||||||
Path profilesDir()
|
Path profilesDir()
|
||||||
{
|
{
|
||||||
auto profileRoot =
|
return fmt("%s/profiles/per-user/%s", settings.nixStateDir, getUserName());
|
||||||
(getuid() == 0)
|
|
||||||
? rootProfilesDir()
|
|
||||||
: createNixStateDir() + "/profiles";
|
|
||||||
createDirs(profileRoot);
|
|
||||||
return profileRoot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Path rootProfilesDir()
|
Path rootProfilesDir()
|
||||||
|
@ -320,25 +315,52 @@ Path rootProfilesDir()
|
||||||
return settings.nixStateDir + "/profiles/per-user/root";
|
return settings.nixStateDir + "/profiles/per-user/root";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Path getDefaultProfileLink()
|
||||||
|
{
|
||||||
|
if (getuid() == 0) {
|
||||||
|
return settings.nixStateDir + "/profiles/default";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.useXDGBaseDirectories) {
|
||||||
|
return createNixStateDir() + "/profile";
|
||||||
|
}
|
||||||
|
return getHome() + "/.nix-profile";
|
||||||
|
}
|
||||||
|
|
||||||
|
Path ensureDefaultProfile()
|
||||||
|
{
|
||||||
|
Path const profileLink = getDefaultProfileLink();
|
||||||
|
Path const defaultProfile = profilesDir() + "/profile";
|
||||||
|
|
||||||
|
if (!pathExists(profileLink)) {
|
||||||
|
replaceSymlink(defaultProfile, profileLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backwards compatibiliy measure: Make root's profile available as
|
||||||
|
// `.../default` as it's what NixOS and most of the init scripts expect
|
||||||
|
Path const globalProfileLink = settings.nixStateDir + "/profiles/default";
|
||||||
|
if (getuid() == 0 && !pathExists(globalProfileLink)) {
|
||||||
|
replaceSymlink(defaultProfile, globalProfileLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
return absPath(readLink(profileLink), dirOf(profileLink));
|
||||||
|
}
|
||||||
|
|
||||||
Path getDefaultProfile()
|
Path getDefaultProfile()
|
||||||
{
|
{
|
||||||
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
Path const profileLink = getDefaultProfileLink();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto profile = profilesDir() + "/profile";
|
if (pathExists(profileLink)) {
|
||||||
if (!pathExists(profileLink)) {
|
|
||||||
replaceSymlink(profile, profileLink);
|
|
||||||
}
|
|
||||||
// Backwards compatibiliy measure: Make root's profile available as
|
|
||||||
// `.../default` as it's what NixOS and most of the init scripts expect
|
|
||||||
Path globalProfileLink = settings.nixStateDir + "/profiles/default";
|
|
||||||
if (getuid() == 0 && !pathExists(globalProfileLink)) {
|
|
||||||
replaceSymlink(profile, globalProfileLink);
|
|
||||||
}
|
|
||||||
return absPath(readLink(profileLink), dirOf(profileLink));
|
return absPath(readLink(profileLink), dirOf(profileLink));
|
||||||
} catch (Error &) {
|
|
||||||
return profileLink;
|
|
||||||
}
|
}
|
||||||
|
} catch (SysError const & e) {
|
||||||
|
printTalkative("ignoring error resolving default profile '%s': %s", profileLink, e.what());
|
||||||
|
} catch (Error const & e) {
|
||||||
|
printError("ignoring error resolving default profile '%s': %s", profileLink, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return profileLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
Path defaultChannelsDir()
|
Path defaultChannelsDir()
|
||||||
|
|
|
@ -234,4 +234,6 @@ Path rootChannelsDir();
|
||||||
*/
|
*/
|
||||||
Path getDefaultProfile();
|
Path getDefaultProfile();
|
||||||
|
|
||||||
|
Path ensureDefaultProfile();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -859,6 +859,11 @@ public:
|
||||||
return toRealPath(printStorePath(storePath));
|
return toRealPath(printStorePath(storePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void createUser(std::string_view userName, uid_t userId)
|
||||||
|
{
|
||||||
|
warn("base class Store called unimplemented createUser()");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronises the options of the client with those of the daemon
|
* Synchronises the options of the client with those of the daemon
|
||||||
* (a no-op when there’s no daemon)
|
* (a no-op when there’s no daemon)
|
||||||
|
|
|
@ -169,7 +169,7 @@ static int main_nix_channel(int argc, char ** argv)
|
||||||
nixDefExpr = getNixDefExpr();
|
nixDefExpr = getNixDefExpr();
|
||||||
|
|
||||||
// Figure out the name of the channels profile.
|
// Figure out the name of the channels profile.
|
||||||
profile = profilesDir() + "/channels";
|
profile = defaultChannelsDir();
|
||||||
createDirs(dirOf(profile));
|
createDirs(dirOf(profile));
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -81,7 +81,13 @@ static int main_nix_collect_garbage(int argc, char * * argv)
|
||||||
|
|
||||||
if (removeOld) {
|
if (removeOld) {
|
||||||
std::set<Path> dirsToClean = {
|
std::set<Path> dirsToClean = {
|
||||||
profilesDir(), settings.nixStateDir + "/profiles", dirOf(getDefaultProfile())};
|
// XXX: check
|
||||||
|
profilesDir(),
|
||||||
|
// XXX: refactor
|
||||||
|
settings.nixStateDir + "/profiles",
|
||||||
|
/// XXX: check
|
||||||
|
dirOf(getDefaultProfile()),
|
||||||
|
};
|
||||||
for (auto & dir : dirsToClean)
|
for (auto & dir : dirsToClean)
|
||||||
removeOldGenerations(dir);
|
removeOldGenerations(dir);
|
||||||
}
|
}
|
||||||
|
|
|
@ -503,7 +503,7 @@ static bool keep(DrvInfo & drv)
|
||||||
static void installDerivations(Globals & globals,
|
static void installDerivations(Globals & globals,
|
||||||
const Strings & args, const Path & profile)
|
const Strings & args, const Path & profile)
|
||||||
{
|
{
|
||||||
debug("installing derivations");
|
debug("installing derivations into profile %s", profile);
|
||||||
|
|
||||||
/* Get the set of user environment elements to be installed. */
|
/* Get the set of user environment elements to be installed. */
|
||||||
DrvInfos newElems, newElemsTmp;
|
DrvInfos newElems, newElemsTmp;
|
||||||
|
@ -554,8 +554,17 @@ static void installDerivations(Globals & globals,
|
||||||
|
|
||||||
if (globals.dryRun) return;
|
if (globals.dryRun) return;
|
||||||
|
|
||||||
if (createUserEnv(*globals.state, allElems,
|
bool success = createUserEnv(
|
||||||
profile, settings.envKeepDerivations, lockToken)) break;
|
*globals.state,
|
||||||
|
allElems,
|
||||||
|
profile,
|
||||||
|
settings.envKeepDerivations,
|
||||||
|
lockToken
|
||||||
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1296,6 +1305,7 @@ static void opSwitchProfile(Globals & globals, Strings opFlags, Strings opArgs)
|
||||||
if (opArgs.size() != 1)
|
if (opArgs.size() != 1)
|
||||||
throw UsageError("exactly one argument expected");
|
throw UsageError("exactly one argument expected");
|
||||||
|
|
||||||
|
// XXX: refactor
|
||||||
Path profile = absPath(opArgs.front());
|
Path profile = absPath(opArgs.front());
|
||||||
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
||||||
|
|
||||||
|
@ -1463,8 +1473,10 @@ static int main_nix_env(int argc, char * * argv)
|
||||||
op = opQuery;
|
op = opQuery;
|
||||||
opName = "-query";
|
opName = "-query";
|
||||||
}
|
}
|
||||||
else if (*arg == "--profile" || *arg == "-p")
|
else if (*arg == "--profile" || *arg == "-p") {
|
||||||
|
// XXX: check
|
||||||
globals.profile = absPath(getArg(*arg, arg, end));
|
globals.profile = absPath(getArg(*arg, arg, end));
|
||||||
|
}
|
||||||
else if (*arg == "--file" || *arg == "-f")
|
else if (*arg == "--file" || *arg == "-f")
|
||||||
file = getArg(*arg, arg, end);
|
file = getArg(*arg, arg, end);
|
||||||
else if (*arg == "--switch-profile" || *arg == "-S") {
|
else if (*arg == "--switch-profile" || *arg == "-S") {
|
||||||
|
@ -1528,11 +1540,15 @@ static int main_nix_env(int argc, char * * argv)
|
||||||
|
|
||||||
globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
|
globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
|
||||||
|
|
||||||
if (globals.profile == "")
|
if (globals.profile == "") {
|
||||||
|
// XXX: refactor?
|
||||||
globals.profile = getEnv("NIX_PROFILE").value_or("");
|
globals.profile = getEnv("NIX_PROFILE").value_or("");
|
||||||
|
}
|
||||||
|
|
||||||
if (globals.profile == "")
|
if (globals.profile == "") {
|
||||||
globals.profile = getDefaultProfile();
|
// XXX: check
|
||||||
|
globals.profile = ensureDefaultProfile();
|
||||||
|
}
|
||||||
|
|
||||||
op(globals, std::move(opFlags), std::move(opArgs));
|
op(globals, std::move(opFlags), std::move(opArgs));
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
const Path & profile, bool keepDerivations,
|
const Path & profile, bool keepDerivations,
|
||||||
const std::string & lockToken)
|
const std::string & lockToken)
|
||||||
{
|
{
|
||||||
|
debug("asked to create a user env %s for %u drvs", profile, elems.size());
|
||||||
/* Build the components in the user environment, if they don't
|
/* Build the components in the user environment, if they don't
|
||||||
exist already. */
|
exist already. */
|
||||||
std::vector<StorePathWithOutputs> drvsToBuild;
|
std::vector<StorePathWithOutputs> drvsToBuild;
|
||||||
|
@ -131,10 +132,11 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
state.repair ? bmRepair : bmNormal);
|
state.repair ? bmRepair : bmNormal);
|
||||||
|
|
||||||
/* Switch the current user environment to the output path. */
|
/* Switch the current user environment to the output path. */
|
||||||
auto store2 = state.store.dynamic_pointer_cast<LocalFSStore>();
|
auto localStore = state.store.dynamic_pointer_cast<LocalFSStore>();
|
||||||
|
|
||||||
if (store2) {
|
if (localStore) {
|
||||||
PathLocks lock;
|
PathLocks lock;
|
||||||
|
debug("locking profile %s", profile);
|
||||||
lockProfile(lock, profile);
|
lockProfile(lock, profile);
|
||||||
|
|
||||||
Path lockTokenCur = optimisticLockProfile(profile);
|
Path lockTokenCur = optimisticLockProfile(profile);
|
||||||
|
@ -144,7 +146,7 @@ bool createUserEnv(EvalState & state, DrvInfos & elems,
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("switching to new user environment");
|
debug("switching to new user environment");
|
||||||
Path generation = createGeneration(*store2, profile, topLevelOut);
|
Path generation = createGeneration(*localStore, profile, topLevelOut);
|
||||||
switchLink(profile, generation);
|
switchLink(profile, generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -357,6 +357,13 @@ static void daemonLoop(std::optional<TrustedFlag> forceTrustClientOpt)
|
||||||
// Restore normal handling of SIGCHLD.
|
// Restore normal handling of SIGCHLD.
|
||||||
setSigChldAction(false);
|
setSigChldAction(false);
|
||||||
|
|
||||||
|
auto store = openUncachedStore();
|
||||||
|
try {
|
||||||
|
store->createUser(user, peer.uid);
|
||||||
|
} catch (SysError const & e) {
|
||||||
|
printError("ignoring error while creating store per-user state: %s", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
// For debugging, stuff the pid into argv[1].
|
// For debugging, stuff the pid into argv[1].
|
||||||
if (peer.pidKnown && savedArgv[1]) {
|
if (peer.pidKnown && savedArgv[1]) {
|
||||||
auto processName = std::to_string(peer.pid);
|
auto processName = std::to_string(peer.pid);
|
||||||
|
@ -366,7 +373,7 @@ static void daemonLoop(std::optional<TrustedFlag> forceTrustClientOpt)
|
||||||
// Handle the connection.
|
// Handle the connection.
|
||||||
FdSource from(remote.get());
|
FdSource from(remote.get());
|
||||||
FdSink to(remote.get());
|
FdSink to(remote.get());
|
||||||
processConnection(openUncachedStore(), from, to, trusted, NotRecursive);
|
processConnection(store, from, to, trusted, NotRecursive);
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
}, options);
|
}, options);
|
||||||
|
|
|
@ -66,7 +66,7 @@ readLink() {
|
||||||
}
|
}
|
||||||
|
|
||||||
clearProfiles() {
|
clearProfiles() {
|
||||||
profiles="$HOME"/.local/state/nix/profiles
|
profiles="$NIX_STATE_DIR/profiles/per-user/$(whoami || echo -n nixbld)"
|
||||||
rm -rf "$profiles"
|
rm -rf "$profiles"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,8 +37,10 @@ nix-env -qa '*' --description | grepQuiet silly
|
||||||
# Query the system.
|
# Query the system.
|
||||||
nix-env -qa '*' --system | grepQuiet $system
|
nix-env -qa '*' --system | grepQuiet $system
|
||||||
|
|
||||||
|
readlink $HOME/.nix-profile || true
|
||||||
|
eza --tree -la $HOME/.local/state/nix || true
|
||||||
# Install "foo-1.0".
|
# Install "foo-1.0".
|
||||||
nix-env -i foo-1.0
|
nix-env --debug -i foo-1.0
|
||||||
|
|
||||||
# Query installed: should contain foo-1.0 now (which should be
|
# Query installed: should contain foo-1.0 now (which should be
|
||||||
# executable).
|
# executable).
|
||||||
|
|
|
@ -143,6 +143,8 @@ in
|
||||||
|
|
||||||
nix-upgrade-nix = runNixOSTestFor "x86_64-linux" ./nix-upgrade-nix.nix;
|
nix-upgrade-nix = runNixOSTestFor "x86_64-linux" ./nix-upgrade-nix.nix;
|
||||||
|
|
||||||
|
profiles = runNixOSTestFor "x86_64-linux" ./profiles.nix;
|
||||||
|
|
||||||
nssPreload = runNixOSTestFor "x86_64-linux" ./nss-preload.nix;
|
nssPreload = runNixOSTestFor "x86_64-linux" ./nss-preload.nix;
|
||||||
|
|
||||||
githubFlakes = runNixOSTestFor "x86_64-linux" ./github-flakes.nix;
|
githubFlakes = runNixOSTestFor "x86_64-linux" ./github-flakes.nix;
|
||||||
|
|
117
tests/nixos/profiles.nix
Normal file
117
tests/nixos/profiles.nix
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
{ lib, config, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
||||||
|
eza = "${lib.getExe pkgs.eza} -laa --group-directories-first --classify --color=always";
|
||||||
|
ezaTree = "${lib.getExe pkgs.eza} -la --tree --color=always";
|
||||||
|
|
||||||
|
in {
|
||||||
|
name = "nix-profiles";
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
machine = { config, lib, pkgs, ... }: {
|
||||||
|
virtualisation.writableStore = true;
|
||||||
|
virtualisation.additionalPaths = [
|
||||||
|
pkgs.hello
|
||||||
|
pkgs.cowsay.out
|
||||||
|
pkgs.cowsay.man
|
||||||
|
pkgs.cowsay.drvPath
|
||||||
|
pkgs.eza
|
||||||
|
];
|
||||||
|
users.users.alice.isNormalUser = true;
|
||||||
|
#virtualisation.additionalPaths = [ pkgs.hello.drvPath ];
|
||||||
|
#nix.settings.substituters = lib.mkForce [ ];
|
||||||
|
#nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
nix.settings = {
|
||||||
|
substituters = lib.mkForce [ ];
|
||||||
|
experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
allowed-users = [ "alice" ];
|
||||||
|
use-xdg-base-directories = true;
|
||||||
|
};
|
||||||
|
nix.nixPath = [ "nixpkgs=${pkgs.path}" ];
|
||||||
|
#nix.package = pkgs.nixVersions.nix_2_18;
|
||||||
|
#nix.package = pkgs.nixVersions.nix_2_3;
|
||||||
|
#services.getty.autologinUser = "alice";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = { nodes }: ''
|
||||||
|
# fmt: off
|
||||||
|
|
||||||
|
start_all()
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
machine.succeed("nix -vv --version >&2")
|
||||||
|
print(machine.succeed("${eza} ~ >&2"))
|
||||||
|
print(machine.succeed("realpath ~ >&2"))
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
set -x
|
||||||
|
#nix config show >&2
|
||||||
|
nix-env --version -vv >&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
print(machine.succeed("systemctl cat nix-daemon.service"))
|
||||||
|
|
||||||
|
# Initial state.
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c '
|
||||||
|
set -euxo pipefail
|
||||||
|
${ezaTree} ~
|
||||||
|
${ezaTree} /nix/var/nix
|
||||||
|
:' >&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
# This one is as root.
|
||||||
|
#machine.succeed("""
|
||||||
|
# nix-env --store local --file "<nixpkgs>" --install -A hello >&2
|
||||||
|
# ${ezaTree} ~ >&2
|
||||||
|
# ${ezaTree} /nix/var/nix/profiles >&2
|
||||||
|
#""")
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c '
|
||||||
|
set -euxo pipefail
|
||||||
|
nix-env --file "<nixpkgs>" --install -A hello
|
||||||
|
${ezaTree} ~
|
||||||
|
${ezaTree} /nix/var/nix/profiles
|
||||||
|
:' >&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c '
|
||||||
|
set -euxo pipefail
|
||||||
|
nix-env --file "<nixpkgs>" --install -A cowsay
|
||||||
|
${ezaTree} ~
|
||||||
|
${ezaTree} /nix/var/nix/profiles
|
||||||
|
:' >&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c '
|
||||||
|
set -euxo pipefail
|
||||||
|
PAGER=cat nix-env --option use-xdg-base-directories false --query '*'
|
||||||
|
${ezaTree} ~
|
||||||
|
${ezaTree} /nix/var/nix/profiles
|
||||||
|
:' >&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c '
|
||||||
|
set -euxo pipefail
|
||||||
|
nix-env --uninstall cowsay
|
||||||
|
${ezaTree} ~
|
||||||
|
${ezaTree} /nix/var/nix/profiles
|
||||||
|
:' >&2
|
||||||
|
""")
|
||||||
|
|
||||||
|
machine.succeed("""
|
||||||
|
su --login alice -c '
|
||||||
|
set -euxo pipefail
|
||||||
|
nix profile install -f "<nixpkgs>" cowsay
|
||||||
|
${ezaTree} ~
|
||||||
|
${ezaTree} /nix/var/nix/profiles
|
||||||
|
:' >&2
|
||||||
|
""")
|
||||||
|
'';
|
||||||
|
}
|
Loading…
Reference in a new issue