nixos/activation: run user nixos-activation.service exactly once per switch
stc-ng starts every active target (including default.target) and then explicitly restarts nixos-activation.service. As a Type=oneshot without RemainAfterExit the unit is inactive after login, so the default.target start job re-runs it via Wants=, and the explicit restart runs it again (or, depending on ordering, SIGTERMs the currently running script and re-runs it). Set RemainAfterExit=yes so target starts are a no-op for an already-run activation, and restartIfChanged=false so the unit-diff pass leaves it alone when the script changes. The explicit restart in stc-ng remains the single trigger per switch. Print that restart so it is visible in the switch output, and drop it from the "NOT restarting" list. Extend the user-activation-scripts test to assert the activation is only run once, and never killed.
This commit is contained in:
@@ -322,6 +322,9 @@ in
|
||||
description = "Run user-specific NixOS activation";
|
||||
script = config.system.userActivationScripts.script;
|
||||
unitConfig.ConditionUser = "!@system";
|
||||
# switch-to-configuration restarts this explicitly on every switch.
|
||||
restartIfChanged = false;
|
||||
serviceConfig.RemainAfterExit = true;
|
||||
serviceConfig.Type = "oneshot";
|
||||
wantedBy = [ "default.target" ];
|
||||
};
|
||||
|
||||
@@ -1754,9 +1754,10 @@ in
|
||||
out = switch_to_specialisation("${machine}", "simpleUserService")
|
||||
user_systemctl("is-active usertest.service")
|
||||
|
||||
# No-op switch does nothing
|
||||
# No-op switch leaves the test unit alone.
|
||||
out = switch_to_specialisation("${machine}", "simpleUserService")
|
||||
assert_lacks(out, "user units:")
|
||||
assert_lacks(out, "usertest.service")
|
||||
assert_contains(out, "restarting the following user units: nixos-activation.service")
|
||||
|
||||
# Modifying the unit stop-starts it (default stopIfChanged=true)
|
||||
out = switch_to_specialisation("${machine}", "simpleUserServiceModified")
|
||||
@@ -1773,7 +1774,7 @@ in
|
||||
# reloadIfChanged=true reloads instead
|
||||
out = switch_to_specialisation("${machine}", "simpleUserServiceReload")
|
||||
assert_lacks(out, "stopping the following user units:")
|
||||
assert_lacks(out, "restarting the following user units:")
|
||||
assert_lacks(out, "restarting the following user units: usertest.service")
|
||||
assert_contains(out, "reloading the following user units: usertest.service")
|
||||
user_systemctl("is-active usertest.service")
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
isNormalUser = true;
|
||||
};
|
||||
systemd.user.tmpfiles.users.alice.rules = [ "r %h/file-to-remove" ];
|
||||
specialisation.changed.configuration.system.userActivationScripts.bar = "true";
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
@@ -36,5 +37,12 @@
|
||||
machine.succeed("/run/current-system/bin/switch-to-configuration test")
|
||||
verify_user_activation_run_count(2)
|
||||
machine.succeed("[[ ! -f /home/alice/file-to-remove ]] || false")
|
||||
# Activation must not be killed while running.
|
||||
machine.fail("journalctl -b _SYSTEMD_USER_UNIT=nixos-activation.service | grep -q 'code=killed'")
|
||||
|
||||
# Changed activation script: still exactly one run.
|
||||
machine.succeed("/run/current-system/specialisation/changed/bin/switch-to-configuration test")
|
||||
verify_user_activation_run_count(3)
|
||||
machine.fail("journalctl -b _SYSTEMD_USER_UNIT=nixos-activation.service | grep -q 'code=killed'")
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1486,6 +1486,9 @@ fn do_user_switch(parent_exe: String) -> anyhow::Result<()> {
|
||||
&mut units_to_filter,
|
||||
)?;
|
||||
|
||||
// Restarted unconditionally below; don't list it as skipped.
|
||||
units_to_skip.remove("nixos-activation.service");
|
||||
|
||||
let print_units = |verb: &str, units: &HashMap<String, ()>| {
|
||||
if units.is_empty() {
|
||||
return;
|
||||
@@ -1586,6 +1589,7 @@ fn do_user_switch(parent_exe: String) -> anyhow::Result<()> {
|
||||
// Toplevels with system.activatable = false do not ship this unit; mirror
|
||||
// the system scope's tolerance for a missing activate script.
|
||||
if new_unit_dir.join("nixos-activation.service").exists() {
|
||||
eprintln!("restarting the following user units: nixos-activation.service");
|
||||
match systemd.restart_unit("nixos-activation.service", "replace") {
|
||||
Ok(_) => {
|
||||
log::debug!("waiting for nixos activation to finish");
|
||||
|
||||
Reference in New Issue
Block a user