mirror of
https://github.com/nix-community/home-manager.git
synced 2025-11-08 19:46:05 +01:00
home-manager: avoid profile management during activation
This commit deprecates profile management from the activation script. The profile management is instead the responsibility of the driving software, for example, the `home-manager` tool in the case of standalone installs. The legacy behavior is still available for backwards compatibility but may be removed in the future. The new behavior resolves (or moves us closer to resolving) a number of long standing open issues: - `home-manager switch --rollback`, which performs a rollback to the previous Home Manager generation before activating. While it was previously possible to accomplish this by activating an old generation, it did always create a new profile generation. This option has been implemented as part of this commit. - `home-manager switch --specialisation NAME`, which switches to the named specialisation. While it was previously possible to accomplish this by manually running the specialisation activate script, it did always create a new profile generation. This option has been implemented as part of this commit. - `home-manager switch --test`, which activates the configuration but does not create a new profile generation. This option has _not_ been implemented here since it relies on the current configuration being activated on login, which we do not currently do. - When using the "Home Manager as a NixOS module" installation method we previously created an odd `home-manager` per-user "shadow profile" for the user. This is no longer necessary. This has been implemented as part of this commit. Fixes #3450
This commit is contained in:
parent
e4bf85da68
commit
de448dcb57
21 changed files with 692 additions and 127 deletions
|
|
@ -53,11 +53,6 @@ Home Manager targets [NixOS][] unstable and NixOS version 25.05 (the current
|
||||||
stable version), it may or may not work on other Linux distributions and NixOS
|
stable version), it may or may not work on other Linux distributions and NixOS
|
||||||
versions.
|
versions.
|
||||||
|
|
||||||
Also, the `home-manager` tool does not explicitly support rollbacks at the
|
|
||||||
moment so if your home directory gets messed up you'll have to fix it yourself.
|
|
||||||
See the [rollbacks][] section for instructions on how to manually perform a
|
|
||||||
rollback.
|
|
||||||
|
|
||||||
Now when your expectations have been built up and you are eager to try all this
|
Now when your expectations have been built up and you are eager to try all this
|
||||||
out you can go ahead and read the rest of this text.
|
out you can go ahead and read the rest of this text.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@
|
||||||
.Cm | packages
|
.Cm | packages
|
||||||
.Cm | remove-generations Ar ID \&...
|
.Cm | remove-generations Ar ID \&...
|
||||||
.Cm | switch
|
.Cm | switch
|
||||||
|
.Op Fl -rollback
|
||||||
|
.Op Bro Fl c | Fl -specialisation Brc Ar NAME
|
||||||
.Cm | uninstall
|
.Cm | uninstall
|
||||||
.Brc
|
.Brc
|
||||||
.Op Fl A Ar attrPath
|
.Op Fl A Ar attrPath
|
||||||
|
|
@ -157,9 +159,18 @@ sub-command to find suitable generation numbers.
|
||||||
.RE
|
.RE
|
||||||
.Pp
|
.Pp
|
||||||
|
|
||||||
.It Cm switch
|
.It Cm switch Oo Fl -rollback Oc Oo Bro Fl c | Fl -specialisation Brc Ar NAME Oc
|
||||||
.RS 4
|
.RS 4
|
||||||
Build and activate the configuration\&.
|
Build and activate the configuration\&.
|
||||||
|
.sp
|
||||||
|
If the
|
||||||
|
.Fl -rollback
|
||||||
|
option is given, then the build is not done, instead roll back to and
|
||||||
|
activate the configuration prior to the current configuration\&.
|
||||||
|
.sp
|
||||||
|
If the
|
||||||
|
.Fl -specialisation
|
||||||
|
option is given, then the named specialisation is activated\&.
|
||||||
.RE
|
.RE
|
||||||
.Pp
|
.Pp
|
||||||
|
|
||||||
|
|
|
||||||
11
docs/manual/internals.md
Normal file
11
docs/manual/internals.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Home Manager Internals {#ch-internals}
|
||||||
|
|
||||||
|
This chapter collects some documentation about the internal workings
|
||||||
|
of Home Manager. The information here is mostly aimed to developers of
|
||||||
|
Home Manager and those who do non-trivial integration with Home
|
||||||
|
Manager.
|
||||||
|
|
||||||
|
|
||||||
|
```{=include=} sections
|
||||||
|
internals/activation.md
|
||||||
|
```
|
||||||
40
docs/manual/internals/activation.md
Normal file
40
docs/manual/internals/activation.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Activation {#sec-internals-activation}
|
||||||
|
|
||||||
|
Activating a Home Manager configuration ensures that the built
|
||||||
|
configuration is introduced into the user's environment. The
|
||||||
|
activation is performed by a suitably named script
|
||||||
|
{command}`activate`. This script is generated as part of the
|
||||||
|
configuration build and is placed in the root of the build output.
|
||||||
|
|
||||||
|
The activation script is implemented in the Bash language and consists
|
||||||
|
of initialization code followed by a number of _activation script
|
||||||
|
blocks_. These blocks are specified using the
|
||||||
|
[home.activation](#opt-home.activation) option. The blocks may have
|
||||||
|
dependencies among themselves and the generated activation script will
|
||||||
|
contain the blocks serialized such that the dependencies are
|
||||||
|
satisfied. A dependency cycle causes a failure when the configuration
|
||||||
|
is built.
|
||||||
|
|
||||||
|
Historically, the activation script has been responsible for creating
|
||||||
|
a new generation of the `home-manager` Nix profile. The more modern
|
||||||
|
way, however, is to let the _activation driver_ – that is, the
|
||||||
|
software calling the activation script – manage the profile. Indeed,
|
||||||
|
in some cases we may not have a `home-manager` profile at all! This is
|
||||||
|
the case when Home Manager is used as a NixOS or nix-darwin module, in
|
||||||
|
these cases the system profile will contain references to the
|
||||||
|
corresponding Home Manager configurations.
|
||||||
|
|
||||||
|
Note, to maintain backwards compatibility, the old activation script
|
||||||
|
behavior is still the default. To choose the new mode of operation you
|
||||||
|
have to call the activation script with the command line option
|
||||||
|
`--driver-version 1`. The old behavior is available using
|
||||||
|
`--driver-version 0`, or simply omit it entirely.
|
||||||
|
|
||||||
|
Unfortunately, driver software need to support both modes of operation
|
||||||
|
for the time being since a user may wish to activate an old generation
|
||||||
|
that contains an activation script that does not support
|
||||||
|
`--driver-version`. To determine whether support is available, check
|
||||||
|
the {file}`gen-version` file in the configuration build output root.
|
||||||
|
If the file is missing then the activation script does not support
|
||||||
|
`--driver-version`. If the file exists and contains the integer 1 or
|
||||||
|
higher, then `--driver-version 1` is supported.
|
||||||
|
|
@ -14,6 +14,7 @@ usage.md
|
||||||
nix-flakes.md
|
nix-flakes.md
|
||||||
writing-modules.md
|
writing-modules.md
|
||||||
contributing.md
|
contributing.md
|
||||||
|
internals.md
|
||||||
3rd-party.md
|
3rd-party.md
|
||||||
faq.md
|
faq.md
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,45 @@
|
||||||
# Rollbacks {#sec-usage-rollbacks}
|
# Rollbacks {#sec-usage-rollbacks}
|
||||||
|
|
||||||
While the `home-manager` tool does not explicitly support rollbacks at
|
When you perform a `home-manager switch` and discover a problem then
|
||||||
the moment it is relatively easy to perform one manually. The steps to
|
it is possible to _roll back_ to the previous version of your
|
||||||
do so are
|
configuration using `home-manager switch --rollback`. This will turn
|
||||||
|
the previous configuration into the current configuration.
|
||||||
|
|
||||||
1. Run `home-manager generations` to determine which generation you
|
::: {.example #ex-rollback-scenario}
|
||||||
wish to rollback to:
|
### Home Manager Rollback
|
||||||
|
|
||||||
``` shell
|
Imagine you have just updated Nixpkgs and switched to a new Home
|
||||||
$ home-manager generations
|
Manager configuration. You discover that a package update included in
|
||||||
2018-01-04 11:56 : id 765 -> /nix/store/kahm1rxk77mnvd2l8pfvd4jkkffk5ijk-home-manager-generation
|
your new configuration has a bug that was not present in the previous
|
||||||
2018-01-03 10:29 : id 764 -> /nix/store/2wsmsliqr5yynqkdyjzb1y57pr5q2lsj-home-manager-generation
|
configuration.
|
||||||
2018-01-01 12:21 : id 763 -> /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
|
|
||||||
2017-12-29 21:03 : id 762 -> /nix/store/6c0k1r03fxckql4vgqcn9ccb616ynb94-home-manager-generation
|
|
||||||
2017-12-25 18:51 : id 761 -> /nix/store/czc5y6vi1rvnkfv83cs3rn84jarcgsgh-home-manager-generation
|
|
||||||
…
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Copy the Nix store path of the generation you chose, e.g.,
|
You can then run `home-manager switch --rollback` to recover your
|
||||||
|
previous configuration, which includes the working version of the
|
||||||
|
package.
|
||||||
|
|
||||||
/nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
|
To see what happened above we can observe the list of Home Manager
|
||||||
|
generations before and after the rollback:
|
||||||
|
|
||||||
for generation 763.
|
``` shell
|
||||||
|
$ home-manager generations
|
||||||
|
2024-01-04 11:56 : id 765 -> /nix/store/kahm1rxk77mnvd2l8pfvd4jkkffk5ijk-home-manager-generation (current)
|
||||||
|
2024-01-03 10:29 : id 764 -> /nix/store/2wsmsliqr5yynqkdyjzb1y57pr5q2lsj-home-manager-generation
|
||||||
|
2024-01-01 12:21 : id 763 -> /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
|
||||||
|
2023-12-29 21:03 : id 762 -> /nix/store/6c0k1r03fxckql4vgqcn9ccb616ynb94-home-manager-generation
|
||||||
|
2023-12-25 18:51 : id 761 -> /nix/store/czc5y6vi1rvnkfv83cs3rn84jarcgsgh-home-manager-generation
|
||||||
|
…
|
||||||
|
|
||||||
3. Run the `activate` script inside the copied store path:
|
$ home-manager switch --rollback
|
||||||
|
Starting home manager activation
|
||||||
|
…
|
||||||
|
|
||||||
``` shell
|
$ home-manager generations
|
||||||
$ /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation/activate
|
2024-01-04 11:56 : id 765 -> /nix/store/kahm1rxk77mnvd2l8pfvd4jkkffk5ijk-home-manager-generation
|
||||||
Starting home manager activation
|
2024-01-03 10:29 : id 764 -> /nix/store/2wsmsliqr5yynqkdyjzb1y57pr5q2lsj-home-manager-generation (current)
|
||||||
…
|
2024-01-01 12:21 : id 763 -> /nix/store/mv960kl9chn2lal5q8lnqdp1ygxngcd1-home-manager-generation
|
||||||
```
|
2023-12-29 21:03 : id 762 -> /nix/store/6c0k1r03fxckql4vgqcn9ccb616ynb94-home-manager-generation
|
||||||
|
2023-12-25 18:51 : id 761 -> /nix/store/czc5y6vi1rvnkfv83cs3rn84jarcgsgh-home-manager-generation
|
||||||
|
…
|
||||||
|
```
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,46 @@ section is therefore not final.
|
||||||
|
|
||||||
This release has the following notable changes:
|
This release has the following notable changes:
|
||||||
|
|
||||||
- No changes.
|
- Updating the `home-manager` Nix profile inside the activation script
|
||||||
|
now deprecated. The profile update is instead the responsibility of
|
||||||
|
the software calling the activation script, such as the
|
||||||
|
`home-manager` tool.
|
||||||
|
|
||||||
|
The legacy behavior remains the default for backwards compatibility
|
||||||
|
but may emit a deprecation warning in the future and in the longer
|
||||||
|
term removed all together. If you have developed tooling that
|
||||||
|
directly call the generated activation script, then you are
|
||||||
|
encouraged to adapt to the new behavior. See the
|
||||||
|
[Activation](#sec-internals-activation) section in the manual for
|
||||||
|
details on how to call the activation script.
|
||||||
|
|
||||||
|
- The `home-manager switch` command now offers a `--rollback` option.
|
||||||
|
When given, the switch performs a rollback to the Home Manager
|
||||||
|
generation prior to the current before activating. While it was
|
||||||
|
previously possible to accomplish this by manually activating an old
|
||||||
|
generation, it always created a new profile generation. The new
|
||||||
|
behavior mirrors the behavior of `nixos-rebuild switch --rollback`.
|
||||||
|
See the [Rollbacks](#sec-usage-rollbacks) section for more.
|
||||||
|
|
||||||
|
- The `home-manager switch` command now offers a
|
||||||
|
`--specialisation NAME` option. When given, the switch activates the
|
||||||
|
named specialisation. While it was previously possible to accomplish
|
||||||
|
this by manually running the specialisation `activate` script it was
|
||||||
|
quite cumbersome and always created a new profile generation. The
|
||||||
|
new behavior mirrors the behavior of `nixos-rebuild switch
|
||||||
|
--specialisation`.
|
||||||
|
|
||||||
|
- When using Home Manager as a NixOS or nix-darwin module we
|
||||||
|
previously created an unnecessary `home-manager` per-user "shadow
|
||||||
|
profile" for the user. This no longer happens. You can restore the
|
||||||
|
old behavior by adding
|
||||||
|
|
||||||
|
``` nix
|
||||||
|
home-manager.enableLegacyProfileManagement = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
to your configuration. This option is likely to be deprecated in the
|
||||||
|
future.
|
||||||
|
|
||||||
## State Version Changes {#sec-release-25.11-state-version-changes}
|
## State Version Changes {#sec-release-25.11-state-version-changes}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ function errMissingOptArg() {
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function errTopLevelSubcommandOpt() {
|
||||||
|
# translators: For example: "home-manager: --rollback can only be used after switch"
|
||||||
|
_iError '%s: %s can only be used after %s' "$0" "$1" "$2" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
function setNixProfileCommands() {
|
function setNixProfileCommands() {
|
||||||
if [[ -e $HOME/.nix-profile/manifest.json \
|
if [[ -e $HOME/.nix-profile/manifest.json \
|
||||||
|| -e ${XDG_STATE_HOME:-$HOME/.local/state}/nix/profile/manifest.json ]] ; then
|
|| -e ${XDG_STATE_HOME:-$HOME/.local/state}/nix/profile/manifest.json ]] ; then
|
||||||
|
|
@ -481,7 +487,7 @@ EOF
|
||||||
_i "Creating initial Home Manager generation..."
|
_i "Creating initial Home Manager generation..."
|
||||||
echo
|
echo
|
||||||
|
|
||||||
if doSwitch; then
|
if doSwitch --switch; then
|
||||||
# translators: The "%s" specifier will be replaced by a file path.
|
# translators: The "%s" specifier will be replaced by a file path.
|
||||||
_i $'All done! The home-manager tool should now be installed and you can edit\n\n %s\n\nto configure Home Manager. Run \'man home-configuration.nix\' to\nsee all available options.' \
|
_i $'All done! The home-manager tool should now be installed and you can edit\n\n %s\n\nto configure Home Manager. Run \'man home-configuration.nix\' to\nsee all available options.' \
|
||||||
"$confFile"
|
"$confFile"
|
||||||
|
|
@ -699,31 +705,104 @@ function doRepl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function doSwitch() {
|
function doSwitch() {
|
||||||
|
setHomeManagerPathVariables
|
||||||
|
setVerboseArg
|
||||||
setWorkDir
|
setWorkDir
|
||||||
|
|
||||||
|
local action
|
||||||
|
local specialisation
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
local opt="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
case $opt in
|
||||||
|
--switch)
|
||||||
|
action='switch'
|
||||||
|
;;
|
||||||
|
--test)
|
||||||
|
action='test'
|
||||||
|
;;
|
||||||
|
--rollback)
|
||||||
|
action='rollback'
|
||||||
|
;;
|
||||||
|
--specialisation)
|
||||||
|
specialisation="$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_iError "%s: unknown option '%s'" "home-manager switch" "$opt" >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -v action ]]; then
|
||||||
|
errorEcho "home-manager switch: missing required option" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
local generation
|
local generation
|
||||||
|
|
||||||
# Build the generation and run the activate script. Note, we
|
case $action in
|
||||||
# specify an output link so that it is treated as a GC root. This
|
switch|test)
|
||||||
# prevents an unfortunately timed GC from removing the generation
|
# Build the generation and run the activate script. Note, we
|
||||||
# before activation completes.
|
# specify an output link so that it is treated as a GC root. This
|
||||||
generation="$WORK_DIR/generation"
|
# prevents an unfortunately timed GC from removing the generation
|
||||||
|
# before activation completes.
|
||||||
|
generation="$WORK_DIR/generation"
|
||||||
|
|
||||||
setFlakeAttribute
|
setFlakeAttribute
|
||||||
if [[ -v FLAKE_CONFIG_URI ]]; then
|
if [[ -v FLAKE_CONFIG_URI ]]; then
|
||||||
doBuildFlake \
|
doBuildFlake \
|
||||||
"$FLAKE_CONFIG_URI.activationPackage" \
|
"$FLAKE_CONFIG_URI.activationPackage" \
|
||||||
--out-link "$generation" \
|
--out-link "$generation" \
|
||||||
${PRINT_BUILD_LOGS+--print-build-logs} \
|
${PRINT_BUILD_LOGS+--print-build-logs}
|
||||||
&& "$generation/activate" || return
|
else
|
||||||
else
|
doBuildAttr \
|
||||||
doBuildAttr \
|
--out-link "$generation" \
|
||||||
--out-link "$generation" \
|
--attr activationPackage
|
||||||
--attr activationPackage \
|
fi
|
||||||
&& "$generation/activate" || return
|
;;
|
||||||
|
rollback)
|
||||||
|
generation="$HM_PROFILE_DIR/home-manager"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# If we are doing a switch but built a legacy configuration, where the
|
||||||
|
# activation script manages the profile, then we instead perform a test
|
||||||
|
# action.
|
||||||
|
#
|
||||||
|
# The migration away from legacy activation scripts happened when
|
||||||
|
# introducing the gen-version file, hence the existence check.
|
||||||
|
if [[ $action == 'switch' && ! -e "$generation/gen-version" ]]; then
|
||||||
|
action='test'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
presentNews
|
# Choose the activate script to run.
|
||||||
|
local activateScript="$generation/activate"
|
||||||
|
if [[ -v specialisation ]]; then
|
||||||
|
activateScript="$generation/specialisation/$specialisation/activate"
|
||||||
|
if [[ ! -x $activateScript ]]; then
|
||||||
|
_iError 'The configuration did not contain the specialisation "%s"' "$specialisation"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $action in
|
||||||
|
switch)
|
||||||
|
run nix-env $VERBOSE_ARG --profile "$HM_PROFILE_DIR/home-manager" --set "$generation"
|
||||||
|
;;
|
||||||
|
rollback)
|
||||||
|
run nix-env $VERBOSE_ARG --profile "$HM_PROFILE_DIR/home-manager" --rollback
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
"$activateScript" --driver-version 1 || return
|
||||||
|
|
||||||
|
if [[ $action == 'switch' || $action == 'test' ]]; then
|
||||||
|
presentNews
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
function doListGens() {
|
function doListGens() {
|
||||||
|
|
@ -736,10 +815,14 @@ function doListGens() {
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pushd "$HM_PROFILE_DIR" > /dev/null
|
pushd "$HM_PROFILE_DIR" > /dev/null
|
||||||
|
local curProfile
|
||||||
|
curProfile=$(readlink home-manager)
|
||||||
|
|
||||||
# shellcheck disable=2012
|
# shellcheck disable=2012
|
||||||
ls --color=$color -gG --time-style=long-iso --sort time home-manager-*-link \
|
ls --color=$color -gG --time-style=long-iso --sort time home-manager-*-link \
|
||||||
| cut -d' ' -f 4- \
|
| cut -d' ' -f 4- \
|
||||||
| sed -E 's/home-manager-([[:digit:]]*)-link/: id \1/'
|
| sed -E -e "/$curProfile/ { s/\$/ \(current\)/ }" \
|
||||||
|
-e 's/home-manager-([[:digit:]]*)-link/: id \1/'
|
||||||
popd > /dev/null
|
popd > /dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -994,7 +1077,15 @@ function doHelp() {
|
||||||
echo
|
echo
|
||||||
echo " instantiate Instantiate the configuration and print the resulting derivation"
|
echo " instantiate Instantiate the configuration and print the resulting derivation"
|
||||||
echo
|
echo
|
||||||
echo " switch Build and activate configuration"
|
echo " switch [OPTION]"
|
||||||
|
echo " Build and activate configuration"
|
||||||
|
echo
|
||||||
|
echo " --rollback Do not build a new configuration, instead roll back to"
|
||||||
|
echo " the configuration prior to the current configuration."
|
||||||
|
echo
|
||||||
|
echo " -c, --specialisation NAME"
|
||||||
|
echo " Activates the named specialisation; when not specified,"
|
||||||
|
echo " switching will activate the unspecialised configuration."
|
||||||
echo
|
echo
|
||||||
echo " generations List all home environment generations"
|
echo " generations List all home environment generations"
|
||||||
echo
|
echo
|
||||||
|
|
@ -1028,7 +1119,7 @@ while [[ $# -gt 0 ]]; do
|
||||||
opt="$1"
|
opt="$1"
|
||||||
shift
|
shift
|
||||||
case $opt in
|
case $opt in
|
||||||
build|init|instantiate|option|edit|expire-generations|generations|help|news|packages|remove-generations|repl|switch|uninstall)
|
build|init|instantiate|option|edit|expire-generations|generations|help|news|packages|remove-generations|repl|rollback|switch|test|uninstall)
|
||||||
COMMAND="$opt"
|
COMMAND="$opt"
|
||||||
;;
|
;;
|
||||||
-A)
|
-A)
|
||||||
|
|
@ -1093,6 +1184,28 @@ while [[ $# -gt 0 ]]; do
|
||||||
-n|--dry-run)
|
-n|--dry-run)
|
||||||
export DRY_RUN=1
|
export DRY_RUN=1
|
||||||
;;
|
;;
|
||||||
|
--rollback)
|
||||||
|
case $COMMAND in
|
||||||
|
switch)
|
||||||
|
COMMAND_ARGS+=("$opt")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
errTopLevelSubcommandOpt "--rollback" "switch"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
-c|--specialisation)
|
||||||
|
case $COMMAND in
|
||||||
|
switch)
|
||||||
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
||||||
|
COMMAND_ARGS+=("--specialisation" "$1")
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
errTopLevelSubcommandOpt "--specialisation" "switch"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
--option|--arg|--argstr)
|
--option|--arg|--argstr)
|
||||||
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
||||||
[[ -v 2 ]] || errMissingOptArg "$opt $1"
|
[[ -v 2 ]] || errMissingOptArg "$opt $1"
|
||||||
|
|
@ -1151,14 +1264,22 @@ case $COMMAND in
|
||||||
doInstantiate
|
doInstantiate
|
||||||
;;
|
;;
|
||||||
switch)
|
switch)
|
||||||
doSwitch
|
doSwitch --switch "${COMMAND_ARGS[@]}"
|
||||||
;;
|
;;
|
||||||
|
# TODO: The test functionality is not really sensible until we perform
|
||||||
|
# activation through some form of systemd unit.
|
||||||
|
# test)
|
||||||
|
# doSwitch --test
|
||||||
|
# ;;
|
||||||
generations)
|
generations)
|
||||||
doListGens
|
doListGens
|
||||||
;;
|
;;
|
||||||
remove-generations)
|
remove-generations)
|
||||||
doRmGenerations "${COMMAND_ARGS[@]}"
|
doRmGenerations "${COMMAND_ARGS[@]}"
|
||||||
;;
|
;;
|
||||||
|
rollback)
|
||||||
|
doRollback
|
||||||
|
;;
|
||||||
expire-generations)
|
expire-generations)
|
||||||
if [[ ${#COMMAND_ARGS[@]} != 1 ]]; then
|
if [[ ${#COMMAND_ARGS[@]} != 1 ]]; then
|
||||||
_i 'expire-generations expects one argument, got %d.' "${#COMMAND_ARGS[@]}" >&2
|
_i 'expire-generations expects one argument, got %d.' "${#COMMAND_ARGS[@]}" >&2
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Home Manager\n"
|
"Project-Id-Version: Home Manager\n"
|
||||||
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
||||||
"POT-Creation-Date: 2025-05-30 15:05+0200\n"
|
"POT-Creation-Date: 2025-07-22 10:59+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -23,36 +23,41 @@ msgstr ""
|
||||||
msgid "%s: missing argument for %s"
|
msgid "%s: missing argument for %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:65
|
#. translators: For example: "home-manager: --rollback can only be used after switch"
|
||||||
|
#: home-manager/home-manager:22
|
||||||
|
msgid "%s: %s can only be used after %s"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: home-manager/home-manager:71
|
||||||
msgid "No configuration file found at %s"
|
msgid "No configuration file found at %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. translators: The first '%s' specifier will be replaced by either
|
#. translators: The first '%s' specifier will be replaced by either
|
||||||
#. 'home.nix' or 'flake.nix'.
|
#. 'home.nix' or 'flake.nix'.
|
||||||
#: home-manager/home-manager:82 home-manager/home-manager:86
|
#: home-manager/home-manager:88 home-manager/home-manager:92
|
||||||
#: home-manager/home-manager:185
|
#: home-manager/home-manager:191
|
||||||
msgid ""
|
msgid ""
|
||||||
"Keeping your Home Manager %s in %s is deprecated,\n"
|
"Keeping your Home Manager %s in %s is deprecated,\n"
|
||||||
"please move it to %s"
|
"please move it to %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:93
|
#: home-manager/home-manager:99
|
||||||
msgid "No configuration file found. Please create one at %s"
|
msgid "No configuration file found. Please create one at %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:108
|
#: home-manager/home-manager:114
|
||||||
msgid "Home Manager not found at %s."
|
msgid "Home Manager not found at %s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
#. translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
||||||
#: home-manager/home-manager:116
|
#: home-manager/home-manager:122
|
||||||
msgid ""
|
msgid ""
|
||||||
"The fallback Home Manager path %s has been deprecated and a file/directory "
|
"The fallback Home Manager path %s has been deprecated and a file/directory "
|
||||||
"was found there."
|
"was found there."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
#. translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
||||||
#: home-manager/home-manager:119
|
#: home-manager/home-manager:125
|
||||||
msgid ""
|
msgid ""
|
||||||
"To remove this warning, do one of the following.\n"
|
"To remove this warning, do one of the following.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
@ -73,42 +78,42 @@ msgid ""
|
||||||
" $ rm -r \"%s\""
|
" $ rm -r \"%s\""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:147
|
#: home-manager/home-manager:153
|
||||||
msgid "Sanity checking Nix"
|
msgid "Sanity checking Nix"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:167
|
#: home-manager/home-manager:173
|
||||||
msgid "Could not find suitable profile directory, tried %s and %s"
|
msgid "Could not find suitable profile directory, tried %s and %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
#. translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
||||||
#: home-manager/home-manager:222
|
#: home-manager/home-manager:230
|
||||||
msgid "Can't inspect options of a flake configuration"
|
msgid "Can't inspect options of a flake configuration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:297 home-manager/home-manager:320
|
#: home-manager/home-manager:305 home-manager/home-manager:328
|
||||||
#: home-manager/home-manager:1060
|
#: home-manager/home-manager:734 home-manager/home-manager:1237
|
||||||
msgid "%s: unknown option '%s'"
|
msgid "%s: unknown option '%s'"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:302 home-manager/home-manager:1061
|
#: home-manager/home-manager:310 home-manager/home-manager:1238
|
||||||
msgid "Run '%s --help' for usage help"
|
msgid "Run '%s --help' for usage help"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:328 home-manager/home-manager:433
|
#: home-manager/home-manager:336 home-manager/home-manager:441
|
||||||
msgid "The file %s already exists, leaving it unchanged..."
|
msgid "The file %s already exists, leaving it unchanged..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:330 home-manager/home-manager:435
|
#: home-manager/home-manager:338 home-manager/home-manager:443
|
||||||
msgid "Creating %s..."
|
msgid "Creating %s..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:479
|
#: home-manager/home-manager:487
|
||||||
msgid "Creating initial Home Manager generation..."
|
msgid "Creating initial Home Manager generation..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. translators: The "%s" specifier will be replaced by a file path.
|
#. translators: The "%s" specifier will be replaced by a file path.
|
||||||
#: home-manager/home-manager:484
|
#: home-manager/home-manager:492
|
||||||
msgid ""
|
msgid ""
|
||||||
"All done! The home-manager tool should now be installed and you can edit\n"
|
"All done! The home-manager tool should now be installed and you can edit\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
@ -119,7 +124,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. translators: The "%s" specifier will be replaced by a URL.
|
#. translators: The "%s" specifier will be replaced by a URL.
|
||||||
#: home-manager/home-manager:489
|
#: home-manager/home-manager:497
|
||||||
msgid ""
|
msgid ""
|
||||||
"Uh oh, the installation failed! Please create an issue at\n"
|
"Uh oh, the installation failed! Please create an issue at\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
@ -129,11 +134,11 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#. translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
#. translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
||||||
#: home-manager/home-manager:500
|
#: home-manager/home-manager:508
|
||||||
msgid "Can't instantiate a flake configuration"
|
msgid "Can't instantiate a flake configuration"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:576
|
#: home-manager/home-manager:584
|
||||||
msgid ""
|
msgid ""
|
||||||
"There is %d unread and relevant news item.\n"
|
"There is %d unread and relevant news item.\n"
|
||||||
"Read it by running the command \"%s news\"."
|
"Read it by running the command \"%s news\"."
|
||||||
|
|
@ -143,72 +148,76 @@ msgid_plural ""
|
||||||
msgstr[0] ""
|
msgstr[0] ""
|
||||||
msgstr[1] ""
|
msgstr[1] ""
|
||||||
|
|
||||||
#: home-manager/home-manager:590
|
#: home-manager/home-manager:598
|
||||||
msgid "Unknown \"news.display\" setting \"%s\"."
|
msgid "Unknown \"news.display\" setting \"%s\"."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:598
|
#: home-manager/home-manager:606
|
||||||
#, sh-format
|
#, sh-format
|
||||||
msgid "Please set the $EDITOR or $VISUAL environment variable"
|
msgid "Please set the $EDITOR or $VISUAL environment variable"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:616
|
#: home-manager/home-manager:624
|
||||||
msgid "Cannot run build in read-only directory"
|
msgid "Cannot run build in read-only directory"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:697
|
#: home-manager/home-manager:787
|
||||||
|
msgid "The configuration did not contain the specialisation \"%s\""
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: home-manager/home-manager:841
|
||||||
msgid "No generation with ID %s"
|
msgid "No generation with ID %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:699
|
#: home-manager/home-manager:843
|
||||||
msgid "Cannot remove the current generation %s"
|
msgid "Cannot remove the current generation %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:701
|
#: home-manager/home-manager:845
|
||||||
msgid "Removing generation %s"
|
msgid "Removing generation %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:722
|
#: home-manager/home-manager:866
|
||||||
msgid "No generations to expire"
|
msgid "No generations to expire"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:733
|
#: home-manager/home-manager:877
|
||||||
msgid "No home-manager packages seem to be installed."
|
msgid "No home-manager packages seem to be installed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:818
|
#: home-manager/home-manager:962
|
||||||
msgid "Unknown argument %s"
|
msgid "Unknown argument %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:843
|
#: home-manager/home-manager:987
|
||||||
msgid "This will remove Home Manager from your system."
|
msgid "This will remove Home Manager from your system."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:846
|
#: home-manager/home-manager:990
|
||||||
msgid "This is a dry run, nothing will actually be uninstalled."
|
msgid "This is a dry run, nothing will actually be uninstalled."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:850
|
#: home-manager/home-manager:994
|
||||||
msgid "Really uninstall Home Manager?"
|
msgid "Really uninstall Home Manager?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:856
|
#: home-manager/home-manager:1000
|
||||||
msgid "Switching to empty Home Manager configuration..."
|
msgid "Switching to empty Home Manager configuration..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:871
|
#: home-manager/home-manager:1015
|
||||||
msgid "Yay!"
|
msgid "Yay!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:876
|
#: home-manager/home-manager:1020
|
||||||
msgid "Home Manager is uninstalled but your home.nix is left untouched."
|
msgid "Home Manager is uninstalled but your home.nix is left untouched."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:1100
|
#: home-manager/home-manager:1285
|
||||||
msgid "expire-generations expects one argument, got %d."
|
msgid "expire-generations expects one argument, got %d."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: home-manager/home-manager:1122
|
#: home-manager/home-manager:1310
|
||||||
msgid "Unknown command: %s"
|
msgid "Unknown command: %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -477,6 +477,18 @@ in
|
||||||
description = "The package containing the complete activation script.";
|
description = "The package containing the complete activation script.";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
home.activationGenerateGcRoot = mkOption {
|
||||||
|
internal = true;
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = ''
|
||||||
|
Whether the activation script should create a GC root to avoid being
|
||||||
|
garbage collected. Typically you want this but if you know for certain
|
||||||
|
that the Home Manager generation is referenced from some other GC root,
|
||||||
|
then it may be appropriate to not create our own root.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
home.extraActivationPath = mkOption {
|
home.extraActivationPath = mkOption {
|
||||||
internal = true;
|
internal = true;
|
||||||
type = types.listOf types.package;
|
type = types.listOf types.package;
|
||||||
|
|
@ -627,12 +639,16 @@ in
|
||||||
# The entry acting as a boundary between the activation script's "check" and
|
# The entry acting as a boundary between the activation script's "check" and
|
||||||
# the "write" phases. This is where we commit to attempting to actually
|
# the "write" phases. This is where we commit to attempting to actually
|
||||||
# activate the configuration.
|
# activate the configuration.
|
||||||
|
#
|
||||||
|
# Note, if we are run by a version 0 driver then we update the profile here.
|
||||||
home.activation.writeBoundary = lib.hm.dag.entryAnywhere ''
|
home.activation.writeBoundary = lib.hm.dag.entryAnywhere ''
|
||||||
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
|
if (( $hmDriverVersion < 1 )); then
|
||||||
_i "Creating new profile generation"
|
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
|
||||||
run nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
|
_i "Creating new profile generation"
|
||||||
else
|
run nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
|
||||||
_i "No change so reusing latest profile generation"
|
else
|
||||||
|
_i "No change so reusing latest profile generation"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
@ -763,6 +779,38 @@ in
|
||||||
export PATH="${activationBinPaths}"
|
export PATH="${activationBinPaths}"
|
||||||
${config.lib.bash.initHomeManagerLib}
|
${config.lib.bash.initHomeManagerLib}
|
||||||
|
|
||||||
|
# The driver version indicates the behavior expected by the caller of
|
||||||
|
# this script.
|
||||||
|
#
|
||||||
|
# - 0 : legacy behavior
|
||||||
|
# - 1 : the script will not attempt to update the Home Manager Nix profile.
|
||||||
|
hmDriverVersion=0
|
||||||
|
|
||||||
|
while (( $# > 0 )); do
|
||||||
|
opt="$1"
|
||||||
|
shift
|
||||||
|
|
||||||
|
case $opt in
|
||||||
|
--driver-version)
|
||||||
|
if (( $# == 0 )); then
|
||||||
|
errorEcho "$0: no driver version specified" >&2
|
||||||
|
exit 1
|
||||||
|
elif (( 0 <= $1 && $1 <= 1 )); then
|
||||||
|
hmDriverVersion=$1
|
||||||
|
else
|
||||||
|
errorEcho "$0: unexpected driver version $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_iError "%s: unknown option '%s'" "$0" "$opt" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
unset opt
|
||||||
|
|
||||||
${builtins.readFile ./lib-bash/activation-init.sh}
|
${builtins.readFile ./lib-bash/activation-init.sh}
|
||||||
|
|
||||||
if [[ ! -v SKIP_SANITY_CHECKS ]]; then
|
if [[ ! -v SKIP_SANITY_CHECKS ]]; then
|
||||||
|
|
@ -770,13 +818,15 @@ in
|
||||||
checkHomeDirectory ${lib.escapeShellArg config.home.homeDirectory}
|
checkHomeDirectory ${lib.escapeShellArg config.home.homeDirectory}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create a temporary GC root to prevent collection during activation.
|
${lib.optionalString config.home.activationGenerateGcRoot ''
|
||||||
trap 'run rm -f $VERBOSE_ARG "$newGenGcPath"' EXIT
|
# Create a temporary GC root to prevent collection during activation.
|
||||||
run --silence nix-store --realise "$newGenPath" --add-root "$newGenGcPath"
|
trap 'run rm -f $VERBOSE_ARG "$newGenGcPath"' EXIT
|
||||||
|
run --silence nix-store --realise "$newGenPath" --add-root "$newGenGcPath"
|
||||||
|
''}
|
||||||
|
|
||||||
${activationCmds}
|
${activationCmds}
|
||||||
|
|
||||||
${lib.optionalString (!config.uninstall) ''
|
${lib.optionalString (config.home.activationGenerateGcRoot && !config.uninstall) ''
|
||||||
# Create the "current generation" GC root.
|
# Create the "current generation" GC root.
|
||||||
run --silence nix-store --realise "$newGenPath" --add-root "$currentGenGcPath"
|
run --silence nix-store --realise "$newGenPath" --add-root "$currentGenGcPath"
|
||||||
|
|
||||||
|
|
@ -797,6 +847,11 @@ in
|
||||||
|
|
||||||
echo "${config.home.version.full}" > $out/hm-version
|
echo "${config.home.version.full}" > $out/hm-version
|
||||||
|
|
||||||
|
# The gen-version indicates the format of the generation package
|
||||||
|
# itself. It allows us to make backwards incompatible changes in the
|
||||||
|
# package output and have surrounding tooling adapt.
|
||||||
|
echo 1 > $out/gen-version
|
||||||
|
|
||||||
cp ${activationScript} $out/activate
|
cp ${activationScript} $out/activate
|
||||||
|
|
||||||
mkdir $out/bin
|
mkdir $out/bin
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: Home Manager Modules\n"
|
"Project-Id-Version: Home Manager Modules\n"
|
||||||
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
"Report-Msgid-Bugs-To: https://github.com/nix-community/home-manager/issues\n"
|
||||||
"POT-Creation-Date: 2025-05-30 15:05+0200\n"
|
"POT-Creation-Date: 2025-07-22 10:59+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -25,15 +25,15 @@ msgstr ""
|
||||||
msgid "Cleaning up orphan links from %s"
|
msgid "Cleaning up orphan links from %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: modules/home-environment.nix:632
|
#: modules/home-environment.nix:647
|
||||||
msgid "Creating new profile generation"
|
msgid "Creating new profile generation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: modules/home-environment.nix:635
|
#: modules/home-environment.nix:650
|
||||||
msgid "No change so reusing latest profile generation"
|
msgid "No change so reusing latest profile generation"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: modules/home-environment.nix:683
|
#: modules/home-environment.nix:699
|
||||||
msgid ""
|
msgid ""
|
||||||
"Oops, Nix failed to install your new Home Manager profile!\n"
|
"Oops, Nix failed to install your new Home Manager profile!\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
|
@ -49,10 +49,14 @@ msgid ""
|
||||||
"Then try activating your Home Manager configuration again."
|
"Then try activating your Home Manager configuration again."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: modules/home-environment.nix:719
|
#: modules/home-environment.nix:735
|
||||||
msgid "Activating %s"
|
msgid "Activating %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: modules/home-environment.nix:807
|
||||||
|
msgid "%s: unknown option '%s'"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: modules/lib-bash/activation-init.sh:22
|
#: modules/lib-bash/activation-init.sh:22
|
||||||
msgid "Migrating profile from %s to %s"
|
msgid "Migrating profile from %s to %s"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,22 @@ in
|
||||||
{ home-manager.extraSpecialArgs.darwinConfig = config; }
|
{ home-manager.extraSpecialArgs.darwinConfig = config; }
|
||||||
(lib.mkIf (cfg.users != { }) {
|
(lib.mkIf (cfg.users != { }) {
|
||||||
system.activationScripts.postActivation.text = lib.concatStringsSep "\n" (
|
system.activationScripts.postActivation.text = lib.concatStringsSep "\n" (
|
||||||
lib.mapAttrsToList (username: usercfg: ''
|
lib.mapAttrsToList (
|
||||||
echo Activating home-manager configuration for ${usercfg.home.username}
|
username: usercfg:
|
||||||
launchctl asuser "$(id -u ${usercfg.home.username})" sudo -u ${usercfg.home.username} --set-home ${pkgs.writeShellScript "activation-${usercfg.home.username}" ''
|
let
|
||||||
${lib.optionalString (
|
driverVersion = if cfg.enableLegacyProfileManagement then "0" else "1";
|
||||||
cfg.backupFileExtension != null
|
in
|
||||||
) "export HOME_MANAGER_BACKUP_EXT=${lib.escapeShellArg cfg.backupFileExtension}"}
|
''
|
||||||
${lib.optionalString cfg.verbose "export VERBOSE=1"}
|
echo Activating home-manager configuration for ${usercfg.home.username}
|
||||||
exec ${usercfg.home.activationPackage}/activate
|
launchctl asuser "$(id -u ${usercfg.home.username})" sudo -u ${usercfg.home.username} --set-home ${pkgs.writeShellScript "activation-${usercfg.home.username}" ''
|
||||||
''}
|
${lib.optionalString (
|
||||||
'') cfg.users
|
cfg.backupFileExtension != null
|
||||||
|
) "export HOME_MANAGER_BACKUP_EXT=${lib.escapeShellArg cfg.backupFileExtension}"}
|
||||||
|
${lib.optionalString cfg.verbose "export VERBOSE=1"}
|
||||||
|
exec ${usercfg.home.activationPackage}/activate --driver-version ${driverVersion}
|
||||||
|
''}
|
||||||
|
''
|
||||||
|
) cfg.users
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,19 @@ in
|
||||||
|
|
||||||
verbose = mkEnableOption "verbose output on activation";
|
verbose = mkEnableOption "verbose output on activation";
|
||||||
|
|
||||||
|
enableLegacyProfileManagement = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to enable legacy profile (and garbage collection root)
|
||||||
|
management during activation. When enabled, the Home Manager activation
|
||||||
|
will produce a per-user `home-manager` Nix profile as well as a garbage
|
||||||
|
collection root, just like in the standalone installation of Home
|
||||||
|
Manager. Typically, this is not desired when Home Manager is embedded in
|
||||||
|
the system configuration.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
users = mkOption {
|
users = mkOption {
|
||||||
type = types.attrsOf hmModule;
|
type = types.attrsOf hmModule;
|
||||||
default = { };
|
default = { };
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,12 @@ in
|
||||||
|
|
||||||
# Inherit glibcLocales setting from NixOS.
|
# Inherit glibcLocales setting from NixOS.
|
||||||
i18n.glibcLocales = lib.mkDefault config.i18n.glibcLocales;
|
i18n.glibcLocales = lib.mkDefault config.i18n.glibcLocales;
|
||||||
|
|
||||||
|
# Legacy profile management is when the activation script
|
||||||
|
# generates GC root and home-manager profile. The modern way
|
||||||
|
# simply relies on the GC root that the system maintains, which
|
||||||
|
# should also protect the Home Manager activation package outputs.
|
||||||
|
home.activationGenerateGcRoot = cfg.enableLegacyProfileManagement;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
@ -46,6 +52,7 @@ in
|
||||||
_: usercfg:
|
_: usercfg:
|
||||||
let
|
let
|
||||||
username = usercfg.home.username;
|
username = usercfg.home.username;
|
||||||
|
driverVersion = if cfg.enableLegacyProfileManagement then "0" else "1";
|
||||||
in
|
in
|
||||||
lib.nameValuePair "home-manager-${utils.escapeSystemdPath username}" {
|
lib.nameValuePair "home-manager-${utils.escapeSystemdPath username}" {
|
||||||
description = "Home Manager environment for ${username}";
|
description = "Home Manager environment for ${username}";
|
||||||
|
|
@ -94,7 +101,7 @@ in
|
||||||
| ${sed} -En '/^(${exportedSystemdVariables})=/s/^/export /p'
|
| ${sed} -En '/^(${exportedSystemdVariables})=/s/^/export /p'
|
||||||
)"
|
)"
|
||||||
|
|
||||||
exec "$1/activate"
|
exec "$1/activate" --driver-version ${driverVersion}
|
||||||
'';
|
'';
|
||||||
in
|
in
|
||||||
"${setupEnv} ${usercfg.home.activationPackage}";
|
"${setupEnv} ${usercfg.home.activationPackage}";
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,11 @@ let
|
||||||
mu = runTest ./standalone/mu;
|
mu = runTest ./standalone/mu;
|
||||||
nh = runTest ./standalone/nh.nix;
|
nh = runTest ./standalone/nh.nix;
|
||||||
nixos-basics = runTest ./nixos/basics.nix;
|
nixos-basics = runTest ./nixos/basics.nix;
|
||||||
|
nixos-legacy-profile-management = runTest ./nixos/legacy-profile-management.nix;
|
||||||
rclone = runTest ./standalone/rclone;
|
rclone = runTest ./standalone/rclone;
|
||||||
restic = runTest ./standalone/restic.nix;
|
restic = runTest ./standalone/restic.nix;
|
||||||
standalone-flake-basics = runTest ./standalone/flake-basics.nix;
|
standalone-flake-basics = runTest ./standalone/flake-basics.nix;
|
||||||
|
standalone-specialisation = runTest ./standalone/specialisation.nix;
|
||||||
standalone-standard-basics = runTest ./standalone/standard-basics.nix;
|
standalone-standard-basics = runTest ./standalone/standard-basics.nix;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
|
|
|
||||||
|
|
@ -82,17 +82,16 @@
|
||||||
|
|
||||||
logout_alice()
|
logout_alice()
|
||||||
|
|
||||||
with subtest("GC root and profile"):
|
with subtest("no GC root and profile"):
|
||||||
# There should be a GC root and Home Manager profile and they should point
|
# There should be no GC root and Home Manager profile since we are not
|
||||||
# to the same path in the Nix store.
|
# using legacy profile management.
|
||||||
gcroot = "/home/alice/.local/state/home-manager/gcroots/current-home"
|
hmState = "/home/alice/.local/state/home-manager"
|
||||||
gcrootTarget = machine.succeed(f"readlink {gcroot}")
|
machine.succeed(f"test ! -e {hmState}")
|
||||||
|
|
||||||
profile = "/home/alice/.local/state/nix/profiles"
|
hmProfile = "/home/alice/.local/state/nix/profiles/home-manager"
|
||||||
profileTarget = machine.succeed(f"readlink {profile}/home-manager")
|
machine.succeed(f"test ! -e {hmProfile}")
|
||||||
profile1Target = machine.succeed(f"readlink {profile}/{profileTarget}")
|
|
||||||
|
|
||||||
assert gcrootTarget == profile1Target, \
|
hmGcroot = "/home/alice/.local/state/home-manager/gcroots/current-home"
|
||||||
f"expected GC root and profile to point to same, but pointed to {gcrootTarget} and {profile1Target}"
|
machine.succeed(f"test ! -e {hmGcroot}")
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [ ../../../nixos ]; # Import the HM NixOS module.
|
||||||
|
|
||||||
|
system.stateVersion = "24.11";
|
||||||
|
|
||||||
|
users.users.alice = {
|
||||||
|
isNormalUser = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
home-manager = {
|
||||||
|
users.alice =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
home.stateVersion = "24.11";
|
||||||
|
home.file.test.text = "testfile new profile";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
111
tests/integration/nixos/legacy-profile-management.nix
Normal file
111
tests/integration/nixos/legacy-profile-management.nix
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "nixos-legacy-profile-management";
|
||||||
|
meta.maintainers = [ pkgs.lib.maintainers.rycee ];
|
||||||
|
|
||||||
|
nodes.machine =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
# Make the nixpkgs channel available.
|
||||||
|
"${pkgs.path}/nixos/modules/installer/cd-dvd/channel.nix"
|
||||||
|
# Import the HM NixOS module.
|
||||||
|
../../../nixos
|
||||||
|
];
|
||||||
|
|
||||||
|
system.stateVersion = "24.11";
|
||||||
|
|
||||||
|
users.users.alice = {
|
||||||
|
isNormalUser = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
specialisation = {
|
||||||
|
legacy.configuration = {
|
||||||
|
home-manager = {
|
||||||
|
# Force legacy profile management.
|
||||||
|
enableLegacyProfileManagement = true;
|
||||||
|
|
||||||
|
users.alice =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
home.stateVersion = "24.11";
|
||||||
|
home.file.test.text = "testfile legacy";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
modern.configuration = {
|
||||||
|
home-manager = {
|
||||||
|
# Assert that we expect the option to default to false.
|
||||||
|
enableLegacyProfileManagement = pkgs.lib.mkOptionDefault false;
|
||||||
|
|
||||||
|
users.alice =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
home.stateVersion = "24.11";
|
||||||
|
home.file.test.text = "testfile modern";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
{ nodes, ... }:
|
||||||
|
let
|
||||||
|
legacy = "${nodes.machine.system.build.toplevel}/specialisation/legacy";
|
||||||
|
modern = "${nodes.machine.system.build.toplevel}/specialisation/modern";
|
||||||
|
in
|
||||||
|
''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
machine.succeed("${legacy}/bin/switch-to-configuration test >&2")
|
||||||
|
machine.wait_for_console_text("Finished Home Manager environment for alice.")
|
||||||
|
|
||||||
|
with subtest("Home Manager file"):
|
||||||
|
# The file should be linked with the expected content.
|
||||||
|
path = "/home/alice/test"
|
||||||
|
machine.succeed(f"test -L {path}")
|
||||||
|
actual = machine.succeed(f"cat {path}")
|
||||||
|
expected = "testfile legacy"
|
||||||
|
assert actual == expected, f"expected {path} to contain {expected}, but got {actual}"
|
||||||
|
|
||||||
|
with subtest("GC root and profile"):
|
||||||
|
# There should be a GC root and Home Manager profile and they should point
|
||||||
|
# to the same path in the Nix store.
|
||||||
|
gcroot = "/home/alice/.local/state/home-manager/gcroots/current-home"
|
||||||
|
gcrootTarget = machine.succeed(f"readlink {gcroot}")
|
||||||
|
|
||||||
|
profile = "/home/alice/.local/state/nix/profiles"
|
||||||
|
profileTarget = machine.succeed(f"readlink {profile}/home-manager")
|
||||||
|
profile1Target = machine.succeed(f"readlink {profile}/{profileTarget}")
|
||||||
|
|
||||||
|
assert gcrootTarget == profile1Target, \
|
||||||
|
f"expected GC root and profile to point to same, but pointed to {gcrootTarget} and {profile1Target}"
|
||||||
|
|
||||||
|
with subtest("Switch to new profile management"):
|
||||||
|
machine.succeed("${modern}/bin/switch-to-configuration test >&2")
|
||||||
|
machine.wait_for_console_text("Finished Home Manager environment for alice.")
|
||||||
|
|
||||||
|
# The file should be linked with the expected content.
|
||||||
|
path = "/home/alice/test"
|
||||||
|
machine.succeed(f"test -L {path}")
|
||||||
|
actual = machine.succeed(f"cat {path}")
|
||||||
|
expected = "testfile modern"
|
||||||
|
assert actual == expected, f"expected {path} to contain {expected}, but got {actual}"
|
||||||
|
|
||||||
|
with subtest("Switch back to old profile management"):
|
||||||
|
machine.succeed("${legacy}/bin/switch-to-configuration test >&2")
|
||||||
|
machine.wait_for_console_text("Finished Home Manager environment for alice.")
|
||||||
|
|
||||||
|
# The file should be linked with the expected content.
|
||||||
|
path = "/home/alice/test"
|
||||||
|
machine.succeed(f"test -L {path}")
|
||||||
|
actual = machine.succeed(f"cat {path}")
|
||||||
|
expected = "testfile legacy"
|
||||||
|
assert actual == expected, f"expected {path} to contain {expected}, but got {actual}"
|
||||||
|
'';
|
||||||
|
}
|
||||||
16
tests/integration/standalone/alice-home-specialisation.nix
Normal file
16
tests/integration/standalone/alice-home-specialisation.nix
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{ ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
home.username = "alice";
|
||||||
|
home.homeDirectory = "/home/alice";
|
||||||
|
home.stateVersion = "25.05";
|
||||||
|
home.file.test.text = "test";
|
||||||
|
home.sessionVariables.EDITOR = "emacs";
|
||||||
|
programs.bash.enable = true;
|
||||||
|
programs.home-manager.enable = true;
|
||||||
|
|
||||||
|
specialisation.pueue.configuration = {
|
||||||
|
# Enable a light-weight systemd service.
|
||||||
|
services.pueue.enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
93
tests/integration/standalone/specialisation.nix
Normal file
93
tests/integration/standalone/specialisation.nix
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "standalone-specialisation";
|
||||||
|
meta.maintainers = [ pkgs.lib.maintainers.rycee ];
|
||||||
|
|
||||||
|
nodes.machine =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
imports = [ "${pkgs.path}/nixos/modules/installer/cd-dvd/channel.nix" ];
|
||||||
|
virtualisation.memorySize = 2048;
|
||||||
|
users.users.alice = {
|
||||||
|
isNormalUser = true;
|
||||||
|
description = "Alice Foobar";
|
||||||
|
password = "foobar";
|
||||||
|
uid = 1000;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
start_all()
|
||||||
|
machine.wait_for_unit("network.target")
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
home_manager = "${../../..}"
|
||||||
|
|
||||||
|
def login_as_alice():
|
||||||
|
machine.wait_until_tty_matches("1", "login: ")
|
||||||
|
machine.send_chars("alice\n")
|
||||||
|
machine.wait_until_tty_matches("1", "Password: ")
|
||||||
|
machine.send_chars("foobar\n")
|
||||||
|
machine.wait_until_tty_matches("1", "alice\\@machine")
|
||||||
|
|
||||||
|
def logout_alice():
|
||||||
|
machine.send_chars("exit\n")
|
||||||
|
|
||||||
|
def alice_cmd(cmd):
|
||||||
|
return f"su -l alice --shell /bin/sh -c $'export XDG_RUNTIME_DIR=/run/user/$UID ; {cmd}'"
|
||||||
|
|
||||||
|
def succeed_as_alice(cmd):
|
||||||
|
return machine.succeed(alice_cmd(cmd))
|
||||||
|
|
||||||
|
def fail_as_alice(cmd):
|
||||||
|
return machine.fail(alice_cmd(cmd))
|
||||||
|
|
||||||
|
# Create a persistent login so that Alice has a systemd session.
|
||||||
|
login_as_alice()
|
||||||
|
|
||||||
|
# Set up a home-manager channel.
|
||||||
|
succeed_as_alice(" ; ".join([
|
||||||
|
"mkdir -p /home/alice/.nix-defexpr/channels",
|
||||||
|
f"ln -s {home_manager} /home/alice/.nix-defexpr/channels/home-manager"
|
||||||
|
]))
|
||||||
|
|
||||||
|
with subtest("Home Manager installation"):
|
||||||
|
# Install the configuration with included specialisation.
|
||||||
|
succeed_as_alice("mkdir -p /home/alice/.config/home-manager")
|
||||||
|
succeed_as_alice("cp ${./alice-home-specialisation.nix} /home/alice/.config/home-manager/home.nix")
|
||||||
|
|
||||||
|
# Install Home Manager with the unspecialised configuration.
|
||||||
|
succeed_as_alice("nix-shell \"<home-manager>\" -A install")
|
||||||
|
|
||||||
|
# Ensure we are activated.
|
||||||
|
machine.succeed("test -L /home/alice/.cache/.keep")
|
||||||
|
|
||||||
|
with subtest("Home Manager switch to missing specialisation"):
|
||||||
|
actual = fail_as_alice("home-manager switch --specialisation no-such-specialisation")
|
||||||
|
expected = "The configuration did not contain the specialisation \"no-such-specialisation\""
|
||||||
|
assert expected in actual, \
|
||||||
|
f"expected home-manager switch to contain {expected}, but got {actual}"
|
||||||
|
|
||||||
|
with subtest("Home Manager switch to specialisation"):
|
||||||
|
actual = succeed_as_alice("home-manager switch --specialisation pueue")
|
||||||
|
expected = "Starting units: pueued.service"
|
||||||
|
assert expected in actual, \
|
||||||
|
f"expected home-manager switch to contain {expected}, but got {actual}"
|
||||||
|
|
||||||
|
actual = succeed_as_alice("pueue status")
|
||||||
|
expected = "running"
|
||||||
|
assert expected in actual, \
|
||||||
|
f"expected pueue status to contain {expected}, but got {actual}"
|
||||||
|
|
||||||
|
with subtest("Home Manager switch back to base configuration"):
|
||||||
|
actual = succeed_as_alice("home-manager switch")
|
||||||
|
expected = "Stopping units: pueued.service"
|
||||||
|
assert expected in actual, \
|
||||||
|
f"expected home-manager switch to contain {expected}, but got {actual}"
|
||||||
|
|
||||||
|
fail_as_alice("pueue status")
|
||||||
|
|
||||||
|
logout_alice()
|
||||||
|
'';
|
||||||
|
}
|
||||||
2
xgettext
2
xgettext
|
|
@ -1,5 +1,5 @@
|
||||||
#! /usr/bin/env nix-shell
|
#! /usr/bin/env nix-shell
|
||||||
#! nix-shell -I https://github.com/NixOS/nixpkgs/archive/62b852f6c6742134ade1abdd2a21685fd617a291.tar.gz -i bash -p gettext
|
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/62b852f6c6742134ade1abdd2a21685fd617a291.tar.gz -i bash -p gettext
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
shopt -s globstar
|
shopt -s globstar
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue