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
1316 lines
40 KiB
Bash
1316 lines
40 KiB
Bash
#!@bash@/bin/bash
|
|
|
|
# Prepare to use tools from Nixpkgs.
|
|
PATH=@DEP_PATH@${PATH:+:}$PATH
|
|
|
|
set -euo pipefail
|
|
|
|
export TEXTDOMAIN=home-manager
|
|
export TEXTDOMAINDIR=@OUT@/share/locale
|
|
|
|
# shellcheck disable=1091
|
|
source @HOME_MANAGER_LIB@
|
|
|
|
function errMissingOptArg() {
|
|
# translators: For example: "home-manager: missing argument for --cores"
|
|
_iError "%s: missing argument for %s" "$0" "$1" >&2
|
|
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() {
|
|
if [[ -e $HOME/.nix-profile/manifest.json \
|
|
|| -e ${XDG_STATE_HOME:-$HOME/.local/state}/nix/profile/manifest.json ]] ; then
|
|
|
|
LIST_OUTPATH_CMD="nix profile list"
|
|
else
|
|
LIST_OUTPATH_CMD="nix-env -q --out-path"
|
|
fi
|
|
}
|
|
|
|
function setVerboseArg() {
|
|
if [[ -v VERBOSE ]]; then
|
|
export VERBOSE_ARG="--verbose"
|
|
else
|
|
export VERBOSE_ARG=""
|
|
fi
|
|
}
|
|
|
|
function setWorkDir() {
|
|
if [[ ! -v WORK_DIR ]]; then
|
|
WORK_DIR="$(mktemp --tmpdir -d home-manager-build.XXXXXXXXXX)"
|
|
# shellcheck disable=2064
|
|
trap "rm -r '$WORK_DIR'" EXIT
|
|
fi
|
|
}
|
|
|
|
# Check to see if flakes are functionally available.
|
|
function hasFlakeSupport() {
|
|
nix eval --expr 'builtins.getFlake' > /dev/null 2>&1
|
|
}
|
|
|
|
# Escape string for use in Nix files.
|
|
function escapeForNix() {
|
|
printf %s "$1" | sed 's/["$\\]/\\\0/g'
|
|
}
|
|
|
|
# Attempts to set the HOME_MANAGER_CONFIG global variable.
|
|
#
|
|
# If no configuration file can be found then this function will print
|
|
# an error message and exit with an error code.
|
|
function setConfigFile() {
|
|
if [[ -v HOME_MANAGER_CONFIG ]] ; then
|
|
if [[ -e "$HOME_MANAGER_CONFIG" ]] ; then
|
|
HOME_MANAGER_CONFIG="$(realpath "$HOME_MANAGER_CONFIG")"
|
|
else
|
|
_i 'No configuration file found at %s' \
|
|
"$HOME_MANAGER_CONFIG" >&2
|
|
exit 1
|
|
fi
|
|
elif [[ ! -v HOME_MANAGER_CONFIG ]]; then
|
|
local configHome="${XDG_CONFIG_HOME:-$HOME/.config}"
|
|
local hmConfigHome="$configHome/home-manager"
|
|
local nixpkgsConfigHome="$configHome/nixpkgs"
|
|
local defaultConfFile="$hmConfigHome/home.nix"
|
|
local configFile
|
|
|
|
if [[ -e "$defaultConfFile" ]]; then
|
|
configFile="$defaultConfFile"
|
|
elif [[ -e "$nixpkgsConfigHome/home.nix" ]]; then
|
|
configFile="$nixpkgsConfigHome/home.nix"
|
|
# translators: The first '%s' specifier will be replaced by either
|
|
# 'home.nix' or 'flake.nix'.
|
|
_iWarn $'Keeping your Home Manager %s in %s is deprecated,\nplease move it to %s' \
|
|
'home.nix' "$nixpkgsConfigHome" "$hmConfigHome" >&2
|
|
elif [[ -e "$HOME/.nixpkgs/home.nix" ]]; then
|
|
configFile="$HOME/.nixpkgs/home.nix"
|
|
_iWarn $'Keeping your Home Manager %s in %s is deprecated,\nplease move it to %s' \
|
|
'home.nix' "$HOME/.nixpkgs" "$hmConfigHome" >&2
|
|
fi
|
|
|
|
if [[ -v configFile ]]; then
|
|
HOME_MANAGER_CONFIG="$(realpath "$configFile")"
|
|
else
|
|
_i 'No configuration file found. Please create one at %s' \
|
|
"$defaultConfFile" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function setHomeManagerNixPath() {
|
|
local path="@HOME_MANAGER_PATH@"
|
|
|
|
if [[ -n "$path" ]] ; then
|
|
if [[ -e "$path" || "$path" =~ ^https?:// ]] ; then
|
|
EXTRA_NIX_PATH+=("home-manager=$path")
|
|
return
|
|
else
|
|
_iWarn 'Home Manager not found at %s.' "$path"
|
|
fi
|
|
fi
|
|
|
|
for p in "${XDG_CONFIG_HOME:-$HOME/.config}/nixpkgs/home-manager" \
|
|
"$HOME/.nixpkgs/home-manager" ; do
|
|
if [[ -e "$p" ]] ; then
|
|
# translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
|
_iWarn $'The fallback Home Manager path %s has been deprecated and a file/directory was found there.' \
|
|
"$p"
|
|
# translators: This message will be seen by very few users that likely are familiar with English. So feel free to leave this untranslated.
|
|
_i $'To remove this warning, do one of the following.
|
|
|
|
1. Explicitly tell Home Manager to use the path, for example by adding
|
|
|
|
{ programs.home-manager.path = "%s"; }
|
|
|
|
to your configuration.
|
|
|
|
If you import Home Manager directly, you can use the `path` parameter
|
|
|
|
pkgs.callPackage /path/to/home-manager-package { path = "%s"; }
|
|
|
|
when calling the Home Manager package.
|
|
|
|
2. Remove the deprecated path.
|
|
|
|
$ rm -r "%s"' "$p" "$p" "$p"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# Sets some useful Home Manager related paths as global read-only variables.
|
|
function setHomeManagerPathVariables() {
|
|
# If called twice then just exit early.
|
|
if [[ -v HM_DATA_HOME ]]; then
|
|
return
|
|
fi
|
|
|
|
_iVerbose "Sanity checking Nix"
|
|
nix-build --quiet --expr '{}' --no-out-link > /dev/null 2>&1 || true
|
|
nix-env -q > /dev/null 2>&1 || true
|
|
|
|
declare -r globalNixStateDir="${NIX_STATE_DIR:-/nix/var/nix}"
|
|
declare -r globalProfilesDir="$globalNixStateDir/profiles/per-user/$USER"
|
|
declare -r globalGcrootsDir="$globalNixStateDir/gcroots/per-user/$USER"
|
|
|
|
declare -r stateHome="${XDG_STATE_HOME:-$HOME/.local/state}"
|
|
declare -r userNixStateDir="$stateHome/nix"
|
|
|
|
declare -gr HM_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/home-manager"
|
|
declare -gr HM_STATE_DIR="$stateHome/home-manager"
|
|
declare -gr HM_GCROOT_LEGACY_PATH="$globalGcrootsDir/current-home"
|
|
|
|
if [[ -d $userNixStateDir/profiles ]]; then
|
|
declare -gr HM_PROFILE_DIR="$userNixStateDir/profiles"
|
|
elif [[ -d $globalProfilesDir ]]; then
|
|
declare -gr HM_PROFILE_DIR="$globalProfilesDir"
|
|
else
|
|
_iError 'Could not find suitable profile directory, tried %s and %s' \
|
|
"$userNixStateDir/profiles" "$globalProfilesDir" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
function setFlakeAttribute() {
|
|
if [[ -z $FLAKE_ARG && ! -v HOME_MANAGER_CONFIG ]]; then
|
|
local configHome="${XDG_CONFIG_HOME:-$HOME/.config}"
|
|
local hmConfigHome="$configHome/home-manager"
|
|
local nixpkgsConfigHome="$configHome/nixpkgs"
|
|
|
|
local configFlake
|
|
|
|
if [[ -e "$hmConfigHome/flake.nix" ]]; then
|
|
configFlake="$hmConfigHome/flake.nix"
|
|
elif [[ -e "$nixpkgsConfigHome/flake.nix" ]]; then
|
|
configFlake="$nixpkgsConfigHome/flake.nix"
|
|
_iWarn $'Keeping your Home Manager %s in %s is deprecated,\nplease move it to %s' \
|
|
'flake.nix' "$nixpkgsConfigHome" "$hmConfigHome" >&2
|
|
fi
|
|
|
|
if [[ -v configFlake ]]; then
|
|
FLAKE_ARG="$(dirname "$(readlink -f "$configFlake")")"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$FLAKE_ARG" ]]; then
|
|
local flake="${FLAKE_ARG%#*}"
|
|
case $FLAKE_ARG in
|
|
*#*)
|
|
local name="${FLAKE_ARG#*#}"
|
|
;;
|
|
*)
|
|
local name="$USER"
|
|
# Check FQDN, long, and short hostnames; long first to preserve
|
|
# pre-existing behaviour in case both happen to be defined.
|
|
for n in "$USER@$(hostname -f)" "$USER@$(hostname)" "$USER@$(hostname -s)"; do
|
|
if [[ "$(nix eval "$flake#homeConfigurations" --apply "x: x ? \"$(escapeForNix "$n")\"")" == "true" ]]; then
|
|
name="$n"
|
|
if [[ -v VERBOSE ]]; then
|
|
echo "Using flake homeConfiguration for $name"
|
|
fi
|
|
fi
|
|
done
|
|
;;
|
|
esac
|
|
export FLAKE_CONFIG_URI="$flake#homeConfigurations.\"$(printf %s "$name" | jq -sRr @uri)\""
|
|
export FLAKE_PATH="$flake"
|
|
export FLAKE_ATTR="homeConfigurations.\"$name\""
|
|
fi
|
|
}
|
|
|
|
function doInspectOption() {
|
|
setFlakeAttribute
|
|
if [[ -v FLAKE_CONFIG_URI ]]; then
|
|
# translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
|
_iError "Can't inspect options of a flake configuration"
|
|
exit 1
|
|
fi
|
|
setConfigFile
|
|
|
|
local extraArgs=("$@")
|
|
|
|
for p in "${EXTRA_NIX_PATH[@]}"; do
|
|
extraArgs=("${extraArgs[@]}" "-I" "$p")
|
|
done
|
|
|
|
if [[ -v VERBOSE ]]; then
|
|
extraArgs=("${extraArgs[@]}" "--show-trace")
|
|
fi
|
|
|
|
local HOME_MANAGER_CONFIG_NIX HOME_MANAGER_CONFIG_ATTRIBUTE_NIX
|
|
HOME_MANAGER_CONFIG_NIX=${HOME_MANAGER_CONFIG//'\'/'\\'}
|
|
HOME_MANAGER_CONFIG_NIX=${HOME_MANAGER_CONFIG_NIX//'"'/'\"'}
|
|
HOME_MANAGER_CONFIG_NIX=${HOME_MANAGER_CONFIG_NIX//$'\n'/$'\\n'}
|
|
HOME_MANAGER_CONFIG_ATTRIBUTE_NIX=${HOME_MANAGER_CONFIG_ATTRIBUTE//'\'/'\\'}
|
|
HOME_MANAGER_CONFIG_ATTRIBUTE_NIX=${HOME_MANAGER_CONFIG_ATTRIBUTE_NIX//'"'/'\"'}
|
|
HOME_MANAGER_CONFIG_ATTRIBUTE_NIX=${HOME_MANAGER_CONFIG_ATTRIBUTE_NIX//$'\n'/$'\\n'}
|
|
local modulesExpr
|
|
modulesExpr="let confPath = \"${HOME_MANAGER_CONFIG_NIX}\"; "
|
|
modulesExpr+="confAttr = \"${HOME_MANAGER_CONFIG_ATTRIBUTE_NIX}\"; in "
|
|
modulesExpr+="(import <home-manager/modules> {"
|
|
modulesExpr+=" configuration = if confAttr == \"\" then confPath else (import confPath).\${confAttr};"
|
|
modulesExpr+=" pkgs = import <nixpkgs> {}; check = true; })"
|
|
|
|
nixos-option \
|
|
--options_expr "$modulesExpr.options" \
|
|
--config_expr "$modulesExpr.config" \
|
|
"${extraArgs[@]}" \
|
|
"${PASSTHROUGH_OPTS[@]}"
|
|
}
|
|
|
|
function doInit() {
|
|
# The directory where we should place the initial configuration.
|
|
local confDir
|
|
|
|
# Whether we should immediate activate the configuration.
|
|
local switch
|
|
|
|
# Whether we should create a flake file.
|
|
local withFlake
|
|
|
|
if hasFlakeSupport; then
|
|
withFlake=1
|
|
fi
|
|
|
|
local homeManagerUrl="github:nix-community/home-manager"
|
|
local nixpkgsUrl="github:nixos/nixpkgs/nixos-unstable"
|
|
|
|
while (( $# > 0 )); do
|
|
local opt="$1"
|
|
shift
|
|
|
|
case $opt in
|
|
--no-flake)
|
|
unset withFlake
|
|
;;
|
|
--switch)
|
|
switch=1
|
|
;;
|
|
--home-manager-url)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
homeManagerUrl="$1"
|
|
shift
|
|
;;
|
|
--nixpkgs-url)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
nixpkgsUrl="$1"
|
|
shift
|
|
;;
|
|
-*)
|
|
_iError "%s: unknown option '%s'" "$0" "$opt" >&2
|
|
exit 1
|
|
;;
|
|
*)
|
|
if [[ -v confDir ]]; then
|
|
_i "Run '%s --help' for usage help" "$0" >&2
|
|
exit 1
|
|
else
|
|
confDir="$opt"
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ ! -v confDir ]]; then
|
|
confDir="${XDG_CONFIG_HOME:-$HOME/.config}/home-manager"
|
|
fi
|
|
|
|
if [[ ! -e $confDir ]]; then
|
|
mkdir -p "$confDir"
|
|
fi
|
|
|
|
if [[ ! -d $confDir ]]; then
|
|
_iError "%s: unknown option '%s'" "$0" "$opt" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local confFile="$confDir/home.nix"
|
|
local flakeFile="$confDir/flake.nix"
|
|
|
|
if [[ -e $confFile ]]; then
|
|
_i 'The file %s already exists, leaving it unchanged...' "$confFile"
|
|
else
|
|
_i 'Creating %s...' "$confFile"
|
|
local nl=$'\n'
|
|
local xdgVars=""
|
|
if [[ -v XDG_CACHE_HOME && $XDG_CACHE_HOME != "$HOME/.cache" ]]; then
|
|
xdgVars="$xdgVars xdg.cacheHome = \"$XDG_CACHE_HOME\";$nl"
|
|
fi
|
|
if [[ -v XDG_CONFIG_HOME && $XDG_CONFIG_HOME != "$HOME/.config" ]]; then
|
|
xdgVars="$xdgVars xdg.configHome = \"$XDG_CONFIG_HOME\";$nl"
|
|
fi
|
|
if [[ -v XDG_DATA_HOME && $XDG_DATA_HOME != "$HOME/.local/share" ]]; then
|
|
xdgVars="$xdgVars xdg.dataHome = \"$XDG_DATA_HOME\";$nl"
|
|
fi
|
|
if [[ -v XDG_STATE_HOME && $XDG_STATE_HOME != "$HOME/.local/state" ]]; then
|
|
xdgVars="$xdgVars xdg.stateHome = \"$XDG_STATE_HOME\";$nl"
|
|
fi
|
|
|
|
mkdir -p "$confDir"
|
|
cat > "$confFile" <<EOF
|
|
{ config, pkgs, ... }:
|
|
|
|
{
|
|
# Home Manager needs a bit of information about you and the paths it should
|
|
# manage.
|
|
home.username = "$(escapeForNix "$USER")";
|
|
home.homeDirectory = "$(escapeForNix "$HOME")";
|
|
$xdgVars
|
|
# This value determines the Home Manager release that your configuration is
|
|
# compatible with. This helps avoid breakage when a new Home Manager release
|
|
# introduces backwards incompatible changes.
|
|
#
|
|
# You should not change this value, even if you update Home Manager. If you do
|
|
# want to update the value, then make sure to first check the Home Manager
|
|
# release notes.
|
|
home.stateVersion = "25.05"; # Please read the comment before changing.
|
|
|
|
# The home.packages option allows you to install Nix packages into your
|
|
# environment.
|
|
home.packages = [
|
|
# # Adds the 'hello' command to your environment. It prints a friendly
|
|
# # "Hello, world!" when run.
|
|
# pkgs.hello
|
|
|
|
# # It is sometimes useful to fine-tune packages, for example, by applying
|
|
# # overrides. You can do that directly here, just don't forget the
|
|
# # parentheses. Maybe you want to install Nerd Fonts with a limited number of
|
|
# # fonts?
|
|
# (pkgs.nerdfonts.override { fonts = [ "FantasqueSansMono" ]; })
|
|
|
|
# # You can also create simple shell scripts directly inside your
|
|
# # configuration. For example, this adds a command 'my-hello' to your
|
|
# # environment:
|
|
# (pkgs.writeShellScriptBin "my-hello" ''
|
|
# echo "Hello, \${config.home.username}!"
|
|
# '')
|
|
];
|
|
|
|
# Home Manager is pretty good at managing dotfiles. The primary way to manage
|
|
# plain files is through 'home.file'.
|
|
home.file = {
|
|
# # Building this configuration will create a copy of 'dotfiles/screenrc' in
|
|
# # the Nix store. Activating the configuration will then make '~/.screenrc' a
|
|
# # symlink to the Nix store copy.
|
|
# ".screenrc".source = dotfiles/screenrc;
|
|
|
|
# # You can also set the file content immediately.
|
|
# ".gradle/gradle.properties".text = ''
|
|
# org.gradle.console=verbose
|
|
# org.gradle.daemon.idletimeout=3600000
|
|
# '';
|
|
};
|
|
|
|
# Home Manager can also manage your environment variables through
|
|
# 'home.sessionVariables'. These will be explicitly sourced when using a
|
|
# shell provided by Home Manager. If you don't want to manage your shell
|
|
# through Home Manager then you have to manually source 'hm-session-vars.sh'
|
|
# located at either
|
|
#
|
|
# ~/.nix-profile/etc/profile.d/hm-session-vars.sh
|
|
#
|
|
# or
|
|
#
|
|
# ~/.local/state/nix/profiles/profile/etc/profile.d/hm-session-vars.sh
|
|
#
|
|
# or
|
|
#
|
|
# /etc/profiles/per-user/$USER/etc/profile.d/hm-session-vars.sh
|
|
#
|
|
home.sessionVariables = {
|
|
# EDITOR = "emacs";
|
|
};
|
|
|
|
# Let Home Manager install and manage itself.
|
|
programs.home-manager.enable = true;
|
|
}
|
|
EOF
|
|
fi
|
|
|
|
if [[ ! -v withFlake ]]; then
|
|
HOME_MANAGER_CONFIG="$confFile"
|
|
else
|
|
FLAKE_ARG="$confDir"
|
|
|
|
if [[ -e $flakeFile ]]; then
|
|
_i 'The file %s already exists, leaving it unchanged...' "$flakeFile"
|
|
else
|
|
_i 'Creating %s...' "$flakeFile"
|
|
|
|
local nixSystem
|
|
nixSystem=$(nix eval --expr builtins.currentSystem --raw --impure)
|
|
|
|
mkdir -p "$confDir"
|
|
cat > "$flakeFile" <<EOF
|
|
{
|
|
description = "Home Manager configuration of $(escapeForNix "$USER")";
|
|
|
|
inputs = {
|
|
# Specify the source of Home Manager and Nixpkgs.
|
|
nixpkgs.url = "$nixpkgsUrl";
|
|
home-manager = {
|
|
url = "$homeManagerUrl";
|
|
inputs.nixpkgs.follows = "nixpkgs";
|
|
};
|
|
};
|
|
|
|
outputs =
|
|
{ nixpkgs, home-manager, ... }:
|
|
let
|
|
system = "$nixSystem";
|
|
pkgs = nixpkgs.legacyPackages.\${system};
|
|
in
|
|
{
|
|
homeConfigurations."$(escapeForNix "$USER")" = home-manager.lib.homeManagerConfiguration {
|
|
inherit pkgs;
|
|
|
|
# Specify your home configuration modules here, for example,
|
|
# the path to your home.nix.
|
|
modules = [ ./home.nix ];
|
|
|
|
# Optionally use extraSpecialArgs
|
|
# to pass through arguments to home.nix
|
|
};
|
|
};
|
|
}
|
|
EOF
|
|
fi
|
|
fi
|
|
|
|
if [[ -v switch ]]; then
|
|
echo
|
|
_i "Creating initial Home Manager generation..."
|
|
echo
|
|
|
|
if doSwitch --switch; then
|
|
# 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.' \
|
|
"$confFile"
|
|
exit 0
|
|
else
|
|
# translators: The "%s" specifier will be replaced by a URL.
|
|
_i $'Uh oh, the installation failed! Please create an issue at\n\n %s\n\nif the error seems to be the fault of Home Manager.' \
|
|
"https://github.com/nix-community/home-manager/issues"
|
|
exit 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
function doInstantiate() {
|
|
setFlakeAttribute
|
|
if [[ -v FLAKE_CONFIG_URI ]]; then
|
|
# translators: Here "flake" is a noun that refers to the Nix Flakes feature.
|
|
_i "Can't instantiate a flake configuration" >&2
|
|
exit 1
|
|
fi
|
|
setConfigFile
|
|
|
|
local extraArgs=()
|
|
|
|
for p in "${EXTRA_NIX_PATH[@]}"; do
|
|
extraArgs=("${extraArgs[@]}" "-I" "$p")
|
|
done
|
|
|
|
if [[ -v VERBOSE ]]; then
|
|
extraArgs=("${extraArgs[@]}" "--show-trace")
|
|
fi
|
|
|
|
nix-instantiate \
|
|
"<home-manager/home-manager/home-manager.nix>" \
|
|
"${extraArgs[@]}" \
|
|
"${PASSTHROUGH_OPTS[@]}" \
|
|
--argstr confPath "$HOME_MANAGER_CONFIG" \
|
|
--argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
|
|
}
|
|
|
|
function doBuildAttr() {
|
|
setConfigFile
|
|
|
|
local extraArgs=("$@")
|
|
|
|
for p in "${EXTRA_NIX_PATH[@]}"; do
|
|
extraArgs=("${extraArgs[@]}" "-I" "$p")
|
|
done
|
|
|
|
if [[ -v VERBOSE ]]; then
|
|
extraArgs=("${extraArgs[@]}" "--show-trace")
|
|
fi
|
|
|
|
nix-build \
|
|
"<home-manager/home-manager/home-manager.nix>" \
|
|
"${extraArgs[@]}" \
|
|
"${PASSTHROUGH_OPTS[@]}" \
|
|
--argstr confPath "$HOME_MANAGER_CONFIG" \
|
|
--argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
|
|
}
|
|
|
|
function doBuildFlake() {
|
|
local extraArgs=("$@")
|
|
|
|
if [[ -v VERBOSE ]]; then
|
|
extraArgs=("${extraArgs[@]}" "--verbose")
|
|
fi
|
|
|
|
nix build \
|
|
"${extraArgs[@]}" \
|
|
"${PASSTHROUGH_OPTS[@]}"
|
|
}
|
|
|
|
# Presents news to the user as specified by the `news.display` option.
|
|
function presentNews() {
|
|
local newsNixFile="$WORK_DIR/news.nix"
|
|
buildNews "$newsNixFile"
|
|
|
|
local newsDisplay
|
|
newsDisplay="$(nix-instantiate --eval --expr "(import ${newsNixFile}).meta.display" | xargs)"
|
|
|
|
local newsNumUnread
|
|
newsNumUnread="$(nix-instantiate --eval --expr "(import ${newsNixFile}).meta.numUnread" | xargs)"
|
|
|
|
# shellcheck disable=2154
|
|
if [[ $newsNumUnread -eq 0 ]]; then
|
|
return
|
|
elif [[ "$newsDisplay" == "silent" ]]; then
|
|
return
|
|
elif [[ "$newsDisplay" == "notify" ]]; then
|
|
local cmd msg
|
|
cmd="$(basename "$0")"
|
|
msg="$(_ip \
|
|
$'There is %d unread and relevant news item.\nRead it by running the command "%s news".' \
|
|
$'There are %d unread and relevant news items.\nRead them by running the command "%s news".' \
|
|
"$newsNumUnread" "$newsNumUnread" "$cmd")"
|
|
|
|
# Not actually an error but here stdout is reserved for
|
|
# nix-build output.
|
|
echo $'\n'"$msg"$'\n' >&2
|
|
|
|
if [[ -v DISPLAY ]] && type -P notify-send > /dev/null; then
|
|
notify-send "Home Manager" "$msg" > /dev/null 2>&1 || true
|
|
fi
|
|
elif [[ "$newsDisplay" == "show" ]]; then
|
|
doShowNews --unread
|
|
else
|
|
_i 'Unknown "news.display" setting "%s".' "$newsDisplay" >&2
|
|
fi
|
|
}
|
|
|
|
function doEdit() {
|
|
if [[ ! -v VISUAL || -z $VISUAL ]]; then
|
|
if [[ ! -v EDITOR || -z $EDITOR ]]; then
|
|
# shellcheck disable=2016
|
|
_i 'Please set the $EDITOR or $VISUAL environment variable' >&2
|
|
return 1
|
|
fi
|
|
else
|
|
EDITOR=$VISUAL
|
|
fi
|
|
|
|
setConfigFile
|
|
|
|
# Don't quote $EDITOR in order to support values including options, e.g.,
|
|
# "code --wait".
|
|
#
|
|
# shellcheck disable=2086
|
|
exec $EDITOR "$HOME_MANAGER_CONFIG"
|
|
}
|
|
|
|
function doBuild() {
|
|
if [[ ! -w . ]]; then
|
|
_i 'Cannot run build in read-only directory' >&2
|
|
return 1
|
|
fi
|
|
|
|
setWorkDir
|
|
|
|
setFlakeAttribute
|
|
if [[ -v FLAKE_CONFIG_URI ]]; then
|
|
doBuildFlake \
|
|
"$FLAKE_CONFIG_URI.activationPackage" \
|
|
${DRY_RUN+--dry-run} \
|
|
${NO_OUT_LINK+--no-link} \
|
|
${PRINT_BUILD_LOGS+--print-build-logs} \
|
|
|| return
|
|
else
|
|
doBuildAttr \
|
|
${NO_OUT_LINK+--no-out-link} \
|
|
--attr activationPackage \
|
|
|| return
|
|
fi
|
|
|
|
presentNews
|
|
}
|
|
|
|
function doRepl() {
|
|
setFlakeAttribute
|
|
|
|
if [[ -v FLAKE_CONFIG_URI ]]; then
|
|
printf -v bold '\033[1m'
|
|
printf -v blue '\033[34;1m'
|
|
printf -v reset '\033[0m'
|
|
exec nix repl --expr "
|
|
let
|
|
flake = builtins.getFlake ''$FLAKE_PATH'';
|
|
configuration = flake.$FLAKE_ATTR;
|
|
motd = ''
|
|
|
|
|
|
Hello and welcome to the Home Manager configuration
|
|
$FLAKE_ATTR
|
|
in $FLAKE_PATH
|
|
|
|
The following is loaded into nix repl's scope:
|
|
|
|
- ${blue}config${reset} All option values
|
|
- ${blue}options${reset} Option data and metadata
|
|
- ${blue}pkgs${reset} Nixpkgs package set
|
|
- ${blue}lib${reset} Nixpkgs library functions
|
|
|
|
- ${blue}flake${reset} Flake outputs, inputs and source info of $FLAKE_PATH
|
|
|
|
Use tab completion to browse around ${blue}config${reset}.
|
|
|
|
Use ${bold}:r${reset} to ${bold}reload${reset} everything after making a change in the flake.
|
|
|
|
See ${bold}:?${reset} for more repl commands.
|
|
'';
|
|
scope =
|
|
assert configuration.class or ''homeManager'' == ''homeManager'';
|
|
{
|
|
inherit (configuration) config options pkgs;
|
|
inherit (configuration.pkgs) lib;
|
|
inherit flake;
|
|
};
|
|
in builtins.seq scope builtins.trace motd scope
|
|
" "${PASSTHROUGH_OPTS[@]}"
|
|
fi
|
|
|
|
setConfigFile
|
|
|
|
extraArgs=()
|
|
for p in "${EXTRA_NIX_PATH[@]}"; do
|
|
extraArgs+=(-I "$p")
|
|
done
|
|
|
|
exec nix repl \
|
|
--file '<home-manager/home-manager/home-manager.nix>' \
|
|
"${extraArgs[@]}" \
|
|
"${PASSTHROUGH_OPTS[@]}" \
|
|
--argstr confPath "$HOME_MANAGER_CONFIG" \
|
|
--argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
|
|
}
|
|
|
|
function doSwitch() {
|
|
setHomeManagerPathVariables
|
|
setVerboseArg
|
|
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
|
|
|
|
case $action in
|
|
switch|test)
|
|
# Build the generation and run the activate script. Note, we
|
|
# specify an output link so that it is treated as a GC root. This
|
|
# prevents an unfortunately timed GC from removing the generation
|
|
# before activation completes.
|
|
generation="$WORK_DIR/generation"
|
|
|
|
setFlakeAttribute
|
|
if [[ -v FLAKE_CONFIG_URI ]]; then
|
|
doBuildFlake \
|
|
"$FLAKE_CONFIG_URI.activationPackage" \
|
|
--out-link "$generation" \
|
|
${PRINT_BUILD_LOGS+--print-build-logs}
|
|
else
|
|
doBuildAttr \
|
|
--out-link "$generation" \
|
|
--attr activationPackage
|
|
fi
|
|
;;
|
|
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
|
|
|
|
# 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() {
|
|
setHomeManagerPathVariables
|
|
|
|
# Whether to colorize the generations output.
|
|
local color="never"
|
|
if [[ ! -v NO_COLOR && -t 1 ]]; then
|
|
color="always"
|
|
fi
|
|
|
|
pushd "$HM_PROFILE_DIR" > /dev/null
|
|
local curProfile
|
|
curProfile=$(readlink home-manager)
|
|
|
|
# shellcheck disable=2012
|
|
ls --color=$color -gG --time-style=long-iso --sort time home-manager-*-link \
|
|
| cut -d' ' -f 4- \
|
|
| sed -E -e "/$curProfile/ { s/\$/ \(current\)/ }" \
|
|
-e 's/home-manager-([[:digit:]]*)-link/: id \1/'
|
|
popd > /dev/null
|
|
}
|
|
|
|
# Removes linked generations. Takes as arguments identifiers of
|
|
# generations to remove.
|
|
function doRmGenerations() {
|
|
setHomeManagerPathVariables
|
|
setVerboseArg
|
|
|
|
pushd "$HM_PROFILE_DIR" > /dev/null
|
|
|
|
for generationId in "$@"; do
|
|
local linkName="home-manager-$generationId-link"
|
|
|
|
if [[ ! -e $linkName ]]; then
|
|
_i 'No generation with ID %s' "$generationId" >&2
|
|
elif [[ $linkName == $(readlink home-manager) ]]; then
|
|
_i 'Cannot remove the current generation %s' "$generationId" >&2
|
|
else
|
|
_i 'Removing generation %s' "$generationId"
|
|
run rm $VERBOSE_ARG $linkName
|
|
fi
|
|
done
|
|
|
|
popd > /dev/null
|
|
}
|
|
|
|
function doExpireGenerations() {
|
|
setHomeManagerPathVariables
|
|
|
|
local generations
|
|
generations="$( \
|
|
find "$HM_PROFILE_DIR" -name 'home-manager-*-link' -not -newermt "$1" \
|
|
| sed 's/^.*-\([0-9]*\)-link$/\1/' \
|
|
)"
|
|
|
|
if [[ -n $generations ]]; then
|
|
# shellcheck disable=2086
|
|
doRmGenerations $generations
|
|
elif [[ -v VERBOSE ]]; then
|
|
_i "No generations to expire"
|
|
fi
|
|
}
|
|
|
|
function doListPackages() {
|
|
setNixProfileCommands
|
|
local outPath
|
|
outPath="$($LIST_OUTPATH_CMD | grep -o '/.*home-manager-path$')"
|
|
if [[ -n "$outPath" ]] ; then
|
|
nix-store -q --references "$outPath" | sed 's/[^-]*-//' | sort --ignore-case
|
|
else
|
|
_i 'No home-manager packages seem to be installed.' >&2
|
|
fi
|
|
}
|
|
|
|
function newsReadIdsFile() {
|
|
local dataDir="${XDG_DATA_HOME:-$HOME/.local/share}/home-manager"
|
|
local path="$dataDir/news-read-ids"
|
|
|
|
# If the path doesn't exist then we should create it, otherwise
|
|
# Nix will error out when we attempt to use builtins.readFile.
|
|
if [[ ! -f "$path" ]]; then
|
|
mkdir -p "$dataDir"
|
|
touch "$path"
|
|
fi
|
|
|
|
# Remove duplicate slashes in case $HOME or $XDG_DATA_HOME have a trailing
|
|
# slash. Double slashes causes Nix to error out with
|
|
#
|
|
# error: syntax error, unexpected PATH_END, expecting DOLLAR_CURLY".
|
|
echo "$path" | tr -s /
|
|
}
|
|
|
|
# Builds the Home Manager news data file.
|
|
#
|
|
# Note, we suppress build output to remove unnecessary verbosity. We
|
|
# put the output in the work directory to avoid the risk of an
|
|
# unfortunately timed GC removing it.
|
|
function buildNews() {
|
|
local newsNixFile="$1"
|
|
local newsJsonFile="$WORK_DIR/news.json"
|
|
|
|
if [[ -v FLAKE_CONFIG_URI ]]; then
|
|
# TODO: Use check=false to make it more likely that the build succeeds.
|
|
doBuildFlake \
|
|
"$FLAKE_CONFIG_URI.config.news.json.output" \
|
|
--quiet \
|
|
--out-link "$newsJsonFile" \
|
|
|| return
|
|
else
|
|
doBuildAttr \
|
|
--out-link "$newsJsonFile" \
|
|
--arg check false \
|
|
--attr config.news.json.output \
|
|
> /dev/null \
|
|
|| return
|
|
fi
|
|
|
|
local extraArgs=()
|
|
|
|
for p in "${EXTRA_NIX_PATH[@]}"; do
|
|
extraArgs=("${extraArgs[@]}" "-I" "$p")
|
|
done
|
|
|
|
local readIdsFile
|
|
readIdsFile="$(newsReadIdsFile)"
|
|
|
|
nix-instantiate \
|
|
--no-build-output --strict \
|
|
--eval '<home-manager/home-manager/build-news.nix>' \
|
|
--arg newsJsonFile "\"$(escapeForNix "$newsJsonFile")\"" \
|
|
--arg newsReadIdsFile "\"$(escapeForNix "$readIdsFile")\"" \
|
|
"${extraArgs[@]}" \
|
|
> "$newsNixFile"
|
|
}
|
|
|
|
function doShowNews() {
|
|
setWorkDir
|
|
setFlakeAttribute
|
|
|
|
local newsNixFile="$WORK_DIR/news.nix"
|
|
buildNews "$newsNixFile"
|
|
|
|
local readIdsFile
|
|
readIdsFile="$(newsReadIdsFile)"
|
|
|
|
local newsAttr
|
|
|
|
case $1 in
|
|
--all)
|
|
newsAttr="all"
|
|
;;
|
|
--unread)
|
|
newsAttr="unread"
|
|
;;
|
|
*)
|
|
_i 'Unknown argument %s' "$1"
|
|
return 1
|
|
esac
|
|
|
|
nix-instantiate --quiet --eval --json --expr "(import ${newsNixFile}).news.$newsAttr" \
|
|
| jq -r . \
|
|
| ${PAGER:-less}
|
|
|
|
local allIds
|
|
allIds="$(nix-instantiate --quiet --eval --expr "(import ${newsNixFile}).meta.ids")"
|
|
allIds="${allIds:1:-1}" # Trim surrounding quotes.
|
|
|
|
local readIdsFileNew="$WORK_DIR/news-read-ids.new"
|
|
{
|
|
cat "$readIdsFile"
|
|
echo -e "$allIds"
|
|
} | sort | uniq > "$readIdsFileNew"
|
|
|
|
mv -f "$readIdsFileNew" "$readIdsFile"
|
|
}
|
|
|
|
function doUninstall() {
|
|
setHomeManagerPathVariables
|
|
setNixProfileCommands
|
|
|
|
_i 'This will remove Home Manager from your system.'
|
|
|
|
if [[ -v DRY_RUN ]]; then
|
|
_i 'This is a dry run, nothing will actually be uninstalled.'
|
|
fi
|
|
|
|
local confirmation
|
|
read -r -n 1 -p "$(_i 'Really uninstall Home Manager?') [y/n] " confirmation
|
|
echo
|
|
|
|
# shellcheck disable=2086
|
|
case $confirmation in
|
|
y|Y)
|
|
_i "Switching to empty Home Manager configuration..."
|
|
HOME_MANAGER_CONFIG="$(mktemp --tmpdir home-manager.XXXXXXXXXX)"
|
|
cat > "$HOME_MANAGER_CONFIG" <<EOF
|
|
{
|
|
uninstall = true;
|
|
home.username = "$(escapeForNix "$USER")";
|
|
home.homeDirectory = "$(escapeForNix "$HOME")";
|
|
home.stateVersion = "25.05";
|
|
}
|
|
EOF
|
|
# shellcheck disable=2064
|
|
trap "rm '$HOME_MANAGER_CONFIG'" EXIT
|
|
doSwitch --switch
|
|
;;
|
|
*)
|
|
_i "Yay!"
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
_i "Home Manager is uninstalled but your home.nix is left untouched."
|
|
}
|
|
|
|
function doHelp() {
|
|
echo "Usage: $0 [OPTION] COMMAND"
|
|
echo
|
|
echo "Options"
|
|
echo
|
|
echo " -f FILE The home configuration file."
|
|
echo " Default is '~/.config/nixpkgs/home.nix'."
|
|
echo " -A ATTRIBUTE Optional attribute that selects a configuration"
|
|
echo " expression in the configuration file."
|
|
echo " -I PATH Add a path to the Nix expression search path."
|
|
echo " --flake flake-uri Use Home Manager configuration at flake-uri"
|
|
echo " Default is '~/.config/home-manager'."
|
|
echo " -b EXT Move existing files to new path rather than fail."
|
|
echo " -v Verbose output"
|
|
echo " -n Do a dry run, only prints what actions would be taken"
|
|
echo " -h Print this help"
|
|
echo " --version Print the Home Manager version"
|
|
echo
|
|
echo "Options passed on to nix-build(1)"
|
|
echo
|
|
echo " --arg(str) NAME VALUE Override inputs passed to home-manager.nix"
|
|
echo " --cores NUM"
|
|
echo " --debug"
|
|
echo " --impure"
|
|
echo " --keep-failed"
|
|
echo " --keep-going"
|
|
echo " -j, --max-jobs NUM"
|
|
echo " --option NAME VALUE"
|
|
echo " -L, --print-build-logs"
|
|
echo " --log-format FORMAT"
|
|
echo " --show-trace"
|
|
echo " --(no-)substitute"
|
|
echo " --no-out-link Do not create a symlink to the output path"
|
|
echo " --no-write-lock-file"
|
|
echo " --builders VALUE"
|
|
echo " --refresh Consider all previously downloaded files out-of-date"
|
|
echo
|
|
echo "Commands"
|
|
echo
|
|
echo " help Print this help"
|
|
echo
|
|
echo " edit Open the home configuration in \$VISUAL or \$EDITOR"
|
|
echo
|
|
echo " option OPTION.NAME"
|
|
echo " Inspect configuration option named OPTION.NAME."
|
|
echo
|
|
echo " build Build configuration into result directory"
|
|
echo
|
|
echo " init [--switch] [DIR]"
|
|
echo " Initializes a configuration in the given directory. If the directory"
|
|
echo " does not exist, then it will be created. The default directory is"
|
|
echo " '~/.config/home-manager'."
|
|
echo
|
|
echo " --switch Immediately activate the generated configuration."
|
|
echo
|
|
echo " instantiate Instantiate the configuration and print the resulting derivation"
|
|
echo
|
|
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 " generations List all home environment generations"
|
|
echo
|
|
echo " remove-generations ID..."
|
|
echo " Remove indicated generations. Use 'generations' command to"
|
|
echo " find suitable generation numbers."
|
|
echo
|
|
echo " repl"
|
|
echo " Opens the configuration in \`nix repl\`"
|
|
echo
|
|
echo " expire-generations TIMESTAMP"
|
|
echo " Remove generations older than TIMESTAMP where TIMESTAMP is"
|
|
echo " interpreted as in the -d argument of the date tool. For"
|
|
echo " example \"-30 days\" or \"2018-01-01\"."
|
|
echo
|
|
echo " packages List all packages installed in home-manager-path"
|
|
echo
|
|
echo " news Show news entries in a pager"
|
|
echo
|
|
echo " uninstall Remove Home Manager"
|
|
}
|
|
|
|
EXTRA_NIX_PATH=()
|
|
HOME_MANAGER_CONFIG_ATTRIBUTE=""
|
|
PASSTHROUGH_OPTS=()
|
|
COMMAND=""
|
|
COMMAND_ARGS=()
|
|
FLAKE_ARG=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
opt="$1"
|
|
shift
|
|
case $opt in
|
|
build|init|instantiate|option|edit|expire-generations|generations|help|news|packages|remove-generations|repl|rollback|switch|test|uninstall)
|
|
COMMAND="$opt"
|
|
;;
|
|
-A)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
HOME_MANAGER_CONFIG_ATTRIBUTE="$1"
|
|
shift
|
|
;;
|
|
-I)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
EXTRA_NIX_PATH+=("$1")
|
|
shift
|
|
;;
|
|
-b)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
export HOME_MANAGER_BACKUP_EXT="$1"
|
|
shift
|
|
;;
|
|
-f|--file)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
HOME_MANAGER_CONFIG="$1"
|
|
shift
|
|
;;
|
|
--flake)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
FLAKE_ARG="$1"
|
|
shift
|
|
;;
|
|
--recreate-lock-file|--no-update-lock-file|--no-write-lock-file|--no-registries|--commit-lock-file|--refresh)
|
|
PASSTHROUGH_OPTS+=("$opt")
|
|
;;
|
|
--update-input)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
PASSTHROUGH_OPTS+=("$opt" "$1")
|
|
shift
|
|
;;
|
|
--override-input)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
[[ -v 2 && $2 != -* ]] || errMissingOptArg "$opt $1"
|
|
PASSTHROUGH_OPTS+=("$opt" "$1" "$2")
|
|
shift 2
|
|
;;
|
|
--experimental-features)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
PASSTHROUGH_OPTS+=("$opt" "$1")
|
|
shift
|
|
;;
|
|
--extra-experimental-features)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
PASSTHROUGH_OPTS+=("$opt" "$1")
|
|
shift
|
|
;;
|
|
--no-out-link)
|
|
NO_OUT_LINK=1
|
|
;;
|
|
-L|--print-build-logs)
|
|
PRINT_BUILD_LOGS=1
|
|
;;
|
|
-h|--help)
|
|
doHelp
|
|
exit 0
|
|
;;
|
|
-n|--dry-run)
|
|
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)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
[[ -v 2 ]] || errMissingOptArg "$opt $1"
|
|
PASSTHROUGH_OPTS+=("$opt" "$1" "$2")
|
|
shift 2
|
|
;;
|
|
-j|--max-jobs|--cores|--builders|--log-format)
|
|
[[ -v 1 && $1 != -* ]] || errMissingOptArg "$opt"
|
|
PASSTHROUGH_OPTS+=("$opt" "$1")
|
|
shift
|
|
;;
|
|
--debug|--keep-failed|--keep-going|--show-trace\
|
|
|--substitute|--no-substitute|--impure)
|
|
PASSTHROUGH_OPTS+=("$opt")
|
|
;;
|
|
-v|--verbose)
|
|
export VERBOSE=1
|
|
;;
|
|
--version)
|
|
echo 25.11-pre
|
|
exit 0
|
|
;;
|
|
*)
|
|
case $COMMAND in
|
|
init|expire-generations|remove-generations|option)
|
|
COMMAND_ARGS+=("$opt")
|
|
;;
|
|
*)
|
|
_iError "%s: unknown option '%s'" "$0" "$opt" >&2
|
|
_i "Run '%s --help' for usage help" "$0" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
;;
|
|
esac
|
|
done
|
|
|
|
setHomeManagerNixPath
|
|
|
|
if [[ -z $COMMAND ]]; then
|
|
doHelp >&2
|
|
exit 1
|
|
fi
|
|
|
|
case $COMMAND in
|
|
edit)
|
|
doEdit
|
|
;;
|
|
build)
|
|
doBuild
|
|
;;
|
|
init)
|
|
doInit "${COMMAND_ARGS[@]}"
|
|
;;
|
|
instantiate)
|
|
doInstantiate
|
|
;;
|
|
switch)
|
|
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)
|
|
doListGens
|
|
;;
|
|
remove-generations)
|
|
doRmGenerations "${COMMAND_ARGS[@]}"
|
|
;;
|
|
rollback)
|
|
doRollback
|
|
;;
|
|
expire-generations)
|
|
if [[ ${#COMMAND_ARGS[@]} != 1 ]]; then
|
|
_i 'expire-generations expects one argument, got %d.' "${#COMMAND_ARGS[@]}" >&2
|
|
exit 1
|
|
else
|
|
doExpireGenerations "${COMMAND_ARGS[@]}"
|
|
fi
|
|
;;
|
|
option)
|
|
doInspectOption "${COMMAND_ARGS[@]}"
|
|
;;
|
|
packages)
|
|
doListPackages
|
|
;;
|
|
repl)
|
|
doRepl
|
|
;;
|
|
news)
|
|
doShowNews --all
|
|
;;
|
|
uninstall)
|
|
doUninstall
|
|
;;
|
|
help)
|
|
doHelp
|
|
;;
|
|
*)
|
|
_iError 'Unknown command: %s' "$COMMAND" >&2
|
|
doHelp >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# vim: ft=bash
|