home-manager: internalize uninstall
This adds a Boolean option `uninstall`. When enabled this option will reset side-effecting configurations to their "empty" state. The intent is that this will cause the activation script to remove all managed files and packages. Doing it this way should hopefully be more robust than the previous solution. It also allows a somewhat more convenient uninstall process when using Flakes; put `uninstall = true` in your existing configuration and then do a switch. Also add simple uninstall test in CI test job.
This commit is contained in:
parent
93e804e7f8
commit
7403ed4980
1
.github/workflows/test.yml
vendored
1
.github/workflows/test.yml
vendored
|
@ -27,4 +27,5 @@ jobs:
|
||||||
- run: nix-build --show-trace -A docs.jsonModuleMaintainers
|
- run: nix-build --show-trace -A docs.jsonModuleMaintainers
|
||||||
- run: ./format -c
|
- run: ./format -c
|
||||||
- run: nix-shell --show-trace . -A install
|
- run: nix-shell --show-trace . -A install
|
||||||
|
- run: yes | home-manager -I home-manager=. uninstall
|
||||||
- run: nix-shell --show-trace --arg enableBig false --pure tests -A run.all
|
- run: nix-shell --show-trace --arg enableBig false --pure tests -A run.all
|
||||||
|
|
|
@ -10,6 +10,27 @@ This release has the following notable changes:
|
||||||
- The `.release` file in the Home Manager project root has been
|
- The `.release` file in the Home Manager project root has been
|
||||||
removed. Please use the `release.json` file instead.
|
removed. Please use the `release.json` file instead.
|
||||||
|
|
||||||
|
- The {command}`home-manager uninstall` command has been reworked to,
|
||||||
|
hopefully, be more robust. The new implementation makes use of a new
|
||||||
|
Boolean configuration option [uninstall](#opt-uninstall) that can
|
||||||
|
also be used in a pure Nix Flake setup.
|
||||||
|
|
||||||
|
Specifically, if you are using a Flake only installation, then you
|
||||||
|
can clean up a Home Manager installation by adding
|
||||||
|
|
||||||
|
``` nix
|
||||||
|
uninstall = true;
|
||||||
|
```
|
||||||
|
|
||||||
|
to your existing configuration and then build and activate. This
|
||||||
|
will override any other configuration and cause, for example, the
|
||||||
|
removal of all managed files.
|
||||||
|
|
||||||
|
Please be very careful when enabling this option since activating
|
||||||
|
the built configuration will not only remove the managed files but
|
||||||
|
_all_ Home Manager state from your user environment. This includes
|
||||||
|
removing all your historic Home Manager generations!
|
||||||
|
|
||||||
## State Version Changes {#sec-release-24.05-state-version-changes}
|
## State Version Changes {#sec-release-24.05-state-version-changes}
|
||||||
|
|
||||||
The state version in this release includes the changes below. These
|
The state version in this release includes the changes below. These
|
||||||
|
|
|
@ -11,32 +11,13 @@ export TEXTDOMAINDIR=@OUT@/share/locale
|
||||||
# shellcheck disable=1091
|
# shellcheck disable=1091
|
||||||
source @HOME_MANAGER_LIB@
|
source @HOME_MANAGER_LIB@
|
||||||
|
|
||||||
function nixProfileList() {
|
|
||||||
# We attempt to use `--json` first (added in Nix 2.17). Otherwise attempt to
|
|
||||||
# parse the legacy output format.
|
|
||||||
{
|
|
||||||
nix profile list --json 2>/dev/null \
|
|
||||||
| jq -r --arg name "$1" '.elements[].storePaths[] | select(endswith($name))'
|
|
||||||
} || {
|
|
||||||
nix profile list \
|
|
||||||
| { grep "$1\$" || test $? = 1; } \
|
|
||||||
| cut -d ' ' -f 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeByName() {
|
|
||||||
nixProfileList "$1" | xargs -t $DRY_RUN_CMD nix profile remove $VERBOSE_ARG
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
LIST_OUTPATH_CMD="nix profile list"
|
LIST_OUTPATH_CMD="nix profile list"
|
||||||
REMOVE_CMD="removeByName"
|
|
||||||
else
|
else
|
||||||
LIST_OUTPATH_CMD="nix-env -q --out-path"
|
LIST_OUTPATH_CMD="nix-env -q --out-path"
|
||||||
REMOVE_CMD="nix-env --uninstall"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -846,30 +827,17 @@ function doUninstall() {
|
||||||
y|Y)
|
y|Y)
|
||||||
_i "Switching to empty Home Manager configuration..."
|
_i "Switching to empty Home Manager configuration..."
|
||||||
HOME_MANAGER_CONFIG="$(mktemp --tmpdir home-manager.XXXXXXXXXX)"
|
HOME_MANAGER_CONFIG="$(mktemp --tmpdir home-manager.XXXXXXXXXX)"
|
||||||
echo "{ lib, ... }: {" > "$HOME_MANAGER_CONFIG"
|
cat > "$HOME_MANAGER_CONFIG" <<EOF
|
||||||
echo " home.file = lib.mkForce {};" >> "$HOME_MANAGER_CONFIG"
|
{
|
||||||
echo " home.stateVersion = \"18.09\";" >> "$HOME_MANAGER_CONFIG"
|
uninstall = true;
|
||||||
echo " manual.manpages.enable = false;" >> "$HOME_MANAGER_CONFIG"
|
home.username = "$USER";
|
||||||
echo "}" >> "$HOME_MANAGER_CONFIG"
|
home.homeDirectory = "$HOME";
|
||||||
doSwitch
|
home.stateVersion = "23.11";
|
||||||
$DRY_RUN_CMD $REMOVE_CMD home-manager-path || true
|
}
|
||||||
rm "$HOME_MANAGER_CONFIG"
|
EOF
|
||||||
|
# shellcheck disable=2064
|
||||||
if [[ -e $HM_DATA_HOME ]]; then
|
trap "rm '$HOME_MANAGER_CONFIG'" EXIT
|
||||||
$DRY_RUN_CMD rm $VERBOSE_ARG -r "$HM_DATA_HOME"
|
doSwitch --switch
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -e $HM_STATE_DIR ]]; then
|
|
||||||
$DRY_RUN_CMD rm $VERBOSE_ARG -r "$HM_STATE_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -e $HM_PROFILE_DIR ]]; then
|
|
||||||
$DRY_RUN_CMD rm $VERBOSE_ARG "$HM_PROFILE_DIR/home-manager"*
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -e $HM_GCROOT_LEGACY_PATH ]]; then
|
|
||||||
$DRY_RUN_CMD rm $VERBOSE_ARG "$HM_GCROOT_LEGACY_PATH"
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
_i "Yay!"
|
_i "Yay!"
|
||||||
|
|
|
@ -585,44 +585,14 @@ in
|
||||||
if config.submoduleSupport.externalPackageInstall
|
if config.submoduleSupport.externalPackageInstall
|
||||||
then
|
then
|
||||||
''
|
''
|
||||||
# We don't use `cfg.profileDirectory` here because it defaults to
|
nixProfileRemove home-manager-path
|
||||||
# `/etc/profiles/per-user/<user>` which is constructed by NixOS or
|
|
||||||
# nix-darwin and won't require uninstalling `home-manager-path`.
|
|
||||||
if [[ -e $HOME/.nix-profile/manifest.json \
|
|
||||||
|| -e "''${XDG_STATE_HOME:-$HOME/.local/state}/nix/profile/manifest.json" ]] ; then
|
|
||||||
nix profile list \
|
|
||||||
| { grep 'home-manager-path$' || test $? = 1; } \
|
|
||||||
| cut -d ' ' -f 4 \
|
|
||||||
| xargs -rt $DRY_RUN_CMD nix profile remove $VERBOSE_ARG
|
|
||||||
else
|
|
||||||
if nix-env -q | grep '^home-manager-path$'; then
|
|
||||||
$DRY_RUN_CMD nix-env -e home-manager-path
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
''
|
''
|
||||||
else
|
else
|
||||||
''
|
''
|
||||||
function nixProfileList() {
|
|
||||||
# We attempt to use `--json` first (added in Nix 2.17). Otherwise attempt to
|
|
||||||
# parse the legacy output format.
|
|
||||||
{
|
|
||||||
nix profile list --json 2>/dev/null \
|
|
||||||
| jq -r --arg name "$1" '.elements[].storePaths[] | select(endswith($name))'
|
|
||||||
} || {
|
|
||||||
nix profile list \
|
|
||||||
| { grep "$1\$" || test $? = 1; } \
|
|
||||||
| cut -d ' ' -f 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function nixRemoveProfileByName() {
|
|
||||||
nixProfileList "$1" | xargs -t $DRY_RUN_CMD nix profile remove $VERBOSE_ARG
|
|
||||||
}
|
|
||||||
|
|
||||||
function nixReplaceProfile() {
|
function nixReplaceProfile() {
|
||||||
local oldNix="$(command -v nix)"
|
local oldNix="$(command -v nix)"
|
||||||
|
|
||||||
nixRemoveProfileByName 'home-manager-path'
|
nixProfileRemove 'home-manager-path'
|
||||||
|
|
||||||
$DRY_RUN_CMD $oldNix profile install $1
|
$DRY_RUN_CMD $oldNix profile install $1
|
||||||
}
|
}
|
||||||
|
@ -644,7 +614,7 @@ in
|
||||||
_iError $'Oops, Nix failed to install your new Home Manager profile!\n\nPerhaps there is a conflict with a package that was installed using\n"%s"? Try running\n\n %s\n\nand if there is a conflicting package you can remove it with\n\n %s\n\nThen try activating your Home Manager configuration again.' "$INSTALL_CMD" "$LIST_CMD" "$REMOVE_CMD_SYNTAX"
|
_iError $'Oops, Nix failed to install your new Home Manager profile!\n\nPerhaps there is a conflict with a package that was installed using\n"%s"? Try running\n\n %s\n\nand if there is a conflicting package you can remove it with\n\n %s\n\nThen try activating your Home Manager configuration again.' "$INSTALL_CMD" "$LIST_CMD" "$REMOVE_CMD_SYNTAX"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
unset -f nixProfileList nixRemoveProfileByName nixReplaceProfile
|
unset -f nixReplaceProfile
|
||||||
unset INSTALL_CMD INSTALL_CMD_ACTUAL LIST_CMD REMOVE_CMD_SYNTAX
|
unset INSTALL_CMD INSTALL_CMD_ACTUAL LIST_CMD REMOVE_CMD_SYNTAX
|
||||||
''
|
''
|
||||||
);
|
);
|
||||||
|
|
|
@ -34,7 +34,8 @@ function migrateProfile() {
|
||||||
function setupVars() {
|
function setupVars() {
|
||||||
declare -r stateHome="${XDG_STATE_HOME:-$HOME/.local/state}"
|
declare -r stateHome="${XDG_STATE_HOME:-$HOME/.local/state}"
|
||||||
declare -r userNixStateDir="$stateHome/nix"
|
declare -r userNixStateDir="$stateHome/nix"
|
||||||
declare -r hmGcrootsDir="$stateHome/home-manager/gcroots"
|
declare -gr hmStatePath="$stateHome/home-manager"
|
||||||
|
declare -r hmGcrootsDir="$hmStatePath/gcroots"
|
||||||
|
|
||||||
declare -r globalNixStateDir="${NIX_STATE_DIR:-/nix/var/nix}"
|
declare -r globalNixStateDir="${NIX_STATE_DIR:-/nix/var/nix}"
|
||||||
declare -r globalProfilesDir="$globalNixStateDir/profiles/per-user/$USER"
|
declare -r globalProfilesDir="$globalNixStateDir/profiles/per-user/$USER"
|
||||||
|
@ -55,6 +56,7 @@ function setupVars() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
declare -gr hmDataPath="${XDG_DATA_HOME:-$HOME/.local/share}/home-manager"
|
||||||
declare -gr genProfilePath="$profilesDir/home-manager"
|
declare -gr genProfilePath="$profilesDir/home-manager"
|
||||||
declare -gr newGenPath="@GENERATION_DIR@";
|
declare -gr newGenPath="@GENERATION_DIR@";
|
||||||
declare -gr newGenGcPath="$hmGcrootsDir/current-home"
|
declare -gr newGenGcPath="$hmGcrootsDir/current-home"
|
||||||
|
@ -88,6 +90,34 @@ function setupVars() {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Helper used to list content of a `nix profile` profile.
|
||||||
|
function nixProfileList() {
|
||||||
|
# We attempt to use `--json` first (added in Nix 2.17). Otherwise attempt to
|
||||||
|
# parse the legacy output format.
|
||||||
|
{
|
||||||
|
nix profile list --json 2>/dev/null \
|
||||||
|
| jq -r --arg name "$1" '.elements[].storePaths[] | select(endswith($name))'
|
||||||
|
} || {
|
||||||
|
nix profile list \
|
||||||
|
| { grep "$1\$" || test $? = 1; } \
|
||||||
|
| cut -d ' ' -f 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Helper used to remove a package from a Nix profile. Supports both `nix-env`
|
||||||
|
# and `nix profile`.
|
||||||
|
function nixProfileRemove() {
|
||||||
|
# We don't use `cfg.profileDirectory` here because it defaults to
|
||||||
|
# `/etc/profiles/per-user/<user>` which is constructed by NixOS or
|
||||||
|
# nix-darwin and won't require uninstalling `home-manager-path`.
|
||||||
|
if [[ -e $HOME/.nix-profile/manifest.json \
|
||||||
|
|| -e ${XDG_STATE_HOME:-$HOME/.local/state}/nix/profile/manifest.json ]] ; then
|
||||||
|
nixProfileList "$1" | xargs -t $DRY_RUN_CMD nix profile remove $VERBOSE_ARG
|
||||||
|
else
|
||||||
|
$DRY_RUN_CMD nix-env -e "$1" > $DRY_RUN_NULL 2>&1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
function checkUsername() {
|
function checkUsername() {
|
||||||
local expectedUser="$1"
|
local expectedUser="$1"
|
||||||
|
|
||||||
|
|
50
modules/misc/uninstall.nix
Normal file
50
modules/misc/uninstall.nix
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
inherit (lib) mkIf mkOption types;
|
||||||
|
|
||||||
|
in {
|
||||||
|
options.uninstall = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to set up a minimal configuration that will remove all managed
|
||||||
|
files and packages.
|
||||||
|
|
||||||
|
Use this with extreme care since running the generated activation script
|
||||||
|
will remove all Home Manager state from your user environment. This
|
||||||
|
includes removing all your historic Home Manager generations.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf config.uninstall {
|
||||||
|
home.packages = lib.mkForce [ ];
|
||||||
|
home.file = lib.mkForce { };
|
||||||
|
home.stateVersion = lib.mkForce "23.11";
|
||||||
|
home.enableNixpkgsReleaseCheck = lib.mkForce false;
|
||||||
|
manual.manpages.enable = lib.mkForce false;
|
||||||
|
news.display = lib.mkForce "silent";
|
||||||
|
|
||||||
|
home.activation.uninstall =
|
||||||
|
lib.hm.dag.entryAfter [ "installPackages" "linkGeneration" ] ''
|
||||||
|
nixProfileRemove home-manager-path
|
||||||
|
|
||||||
|
if [[ -e $hmDataPath ]]; then
|
||||||
|
$DRY_RUN_CMD rm $VERBOSE_ARG -r "$hmDataPath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e $hmStatePath ]]; then
|
||||||
|
$DRY_RUN_CMD rm $VERBOSE_ARG -r "$hmStatePath"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e $genProfilePath ]]; then
|
||||||
|
$DRY_RUN_CMD rm $VERBOSE_ARG "$genProfilePath"*
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -e $legacyGenGcPath ]]; then
|
||||||
|
$DRY_RUN_CMD rm $VERBOSE_ARG "$legacyGenGcPath"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ let
|
||||||
./misc/specialisation.nix
|
./misc/specialisation.nix
|
||||||
./misc/submodule-support.nix
|
./misc/submodule-support.nix
|
||||||
./misc/tmpfiles.nix
|
./misc/tmpfiles.nix
|
||||||
|
./misc/uninstall.nix
|
||||||
./misc/version.nix
|
./misc/version.nix
|
||||||
./misc/vte.nix
|
./misc/vte.nix
|
||||||
./misc/xdg-desktop-entries.nix
|
./misc/xdg-desktop-entries.nix
|
||||||
|
|
Loading…
Reference in a new issue