#!@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 removeByName() { nix profile list \ | { grep "$1" || test $? = 1; } \ | cut -d ' ' -f 4 \ | xargs -t $DRY_RUN_CMD nix profile remove $VERBOSE_ARG } function setNixProfileCommands() { if [[ -e $HOME/.nix-profile/manifest.json ]] ; then LIST_OUTPATH_CMD="nix profile list" REMOVE_CMD="removeByName" else LIST_OUTPATH_CMD="nix-env -q --out-path" REMOVE_CMD="nix-env --uninstall" fi } function setVerboseAndDryRun() { if [[ -v VERBOSE ]]; then export VERBOSE_ARG="--verbose" else export VERBOSE_ARG="" fi if [[ -v DRY_RUN ]] ; then export DRY_RUN_CMD=echo else export DRY_RUN_CMD="" 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 } # Checks whether the 'flakes' and 'nix-command' Nix options are enabled. function hasFlakeSupport() { type -p nix > /dev/null \ && nix show-config \ | grep experimental-features \ | grep flakes \ | grep -q nix-command } # 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 configFlake 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 [[ $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 for path in "@HOME_MANAGER_PATH@" \ "${XDG_CONFIG_HOME:-$HOME/.config}/nixpkgs/home-manager" \ "$HOME/.nixpkgs/home-manager" ; do if [[ -e "$path" || "$path" =~ ^https?:// ]] ; then EXTRA_NIX_PATH+=("home-manager=$path") return fi done } # Sets some useful Home Manager related paths as global read-only variables. function setHomeManagerPathVariables() { declare -r nixStateDir="${NIX_STATE_DIR:-/nix/var/nix}" declare -r globalProfilesDir="$nixStateDir/profiles/per-user/$USER" declare -r globalGcrootsDir="$nixStateDir/gcroots/per-user/$USER" declare -gr HM_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}/home-manager" declare -gr HM_STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/home-manager" declare -gr HM_GCROOT_LEGACY_PATH="$globalGcrootsDir/current-home" if [[ -d "$globalProfilesDir" ]]; then declare -gr HM_PROFILE_DIR="$globalProfilesDir" else declare -gr HM_PROFILE_DIR="$HM_STATE_DIR/profiles" 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 both long and short hostnames; long first to preserve # pre-existing behaviour in case both happen to be defined. for n in "$USER@$(hostname)" "$USER@$(hostname -s)"; do if [[ "$(nix eval "$flake#homeConfigurations" --apply "x: x ? \"$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.\"$name\"" fi } function doInspectOption() { setFlakeAttribute if [[ -v FLAKE_CONFIG_URI ]]; then _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 {" modulesExpr+=" configuration = if confAttr == \"\" then confPath else (import confPath).\${confAttr};" modulesExpr+=" pkgs = import {}; 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 while (( $# > 0 )); do local opt="$1" shift case $opt in --no-flake) unset withFlake ;; --switch) switch=1 ;; -*) _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" < "$flakeFile" <&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 \ "" \ "${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 \ "" \ "${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. Takes as argument the path to a "news # info" file as generated by `buildNews`. function presentNews() { local infoFile="$1" # shellcheck source=/dev/null . "$infoFile" # 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" fi elif [[ "$newsDisplay" == "show" ]]; then doShowNews --unread else _i 'Unknown "news.display" setting "%s".' "$newsDisplay" >&2 fi } function doEdit() { if [[ ! -v EDITOR || -z $EDITOR ]]; then # shellcheck disable=2016 _i 'Please set the $EDITOR environment variable' >&2 return 1 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 local newsInfo newsInfo=$(buildNews) presentNews "$newsInfo" fi } function doSwitch() { setWorkDir local generation # 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} \ && "$generation/activate" || return else doBuildAttr \ --out-link "$generation" \ --attr activationPackage \ && "$generation/activate" || return local newsInfo newsInfo=$(buildNews) presentNews "$newsInfo" fi } function doListGens() { # 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 # shellcheck disable=2012 ls --color=$color -gG --time-style=long-iso --sort time home-manager-*-link \ | cut -d' ' -f 4- \ | sed -E 's/home-manager-([[:digit:]]*)-link/: id \1/' popd > /dev/null } # Removes linked generations. Takes as arguments identifiers of # generations to remove. function doRmGenerations() { setVerboseAndDryRun 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" $DRY_RUN_CMD rm $VERBOSE_ARG $linkName fi done popd > /dev/null } function doExpireGenerations() { 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/[^-]*-//' 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 echo "$path" } # Builds news meta information to be sourced into this script. # # 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() { setFlakeAttribute if [[ -v FLAKE_CONFIG_URI ]]; then _iError "Sorry, this command is not yet supported in flake setup" >&2 exit 1 fi local output output="$WORK_DIR/news-info.sh" doBuildAttr \ --out-link "$output" \ --no-build-output \ --quiet \ --arg check false \ --argstr newsReadIdsFile "$(newsReadIdsFile)" \ --attr newsInfo \ > /dev/null echo "$output" } function doShowNews() { setWorkDir local infoFile infoFile=$(buildNews) || return 1 # shellcheck source=/dev/null . "$infoFile" # shellcheck disable=2154 case $1 in --all) ${PAGER:-less} "$newsFileAll" ;; --unread) ${PAGER:-less} "$newsFileUnread" ;; *) _i 'Unknown argument %s' "$1" return 1 esac # shellcheck disable=2154 if [[ -s "$newsUnreadIdsFile" ]]; then local newsReadIdsFile newsReadIdsFile="$(newsReadIdsFile)" cat "$newsUnreadIdsFile" >> "$newsReadIdsFile" fi } function doUninstall() { setVerboseAndDryRun 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)" echo "{ lib, ... }: {" > "$HOME_MANAGER_CONFIG" echo " home.file = lib.mkForce {};" >> "$HOME_MANAGER_CONFIG" echo " home.stateVersion = \"18.09\";" >> "$HOME_MANAGER_CONFIG" echo "}" >> "$HOME_MANAGER_CONFIG" doSwitch $DRY_RUN_CMD $REMOVE_CMD home-manager-path || true rm "$HOME_MANAGER_CONFIG" if [[ -e $HM_DATA_HOME ]]; then $DRY_RUN_CMD rm $VERBOSE_ARG -r "$HM_DATA_HOME" 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 if [[ -e $HM_STATE_DIR ]]; then $DRY_RUN_CMD rm $VERBOSE_ARG -r "$HM_STATE_DIR" fi ;; *) _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 " --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 \$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 Build and activate 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 " 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="" setHomeManagerNixPath setHomeManagerPathVariables while [[ $# -gt 0 ]]; do opt="$1" shift case $opt in build|init|instantiate|option|edit|expire-generations|generations|help|news|packages|remove-generations|switch|uninstall) COMMAND="$opt" ;; -A) HOME_MANAGER_CONFIG_ATTRIBUTE="$1" shift ;; -I) EXTRA_NIX_PATH+=("$1") shift ;; -b) export HOME_MANAGER_BACKUP_EXT="$1" shift ;; -f|--file) HOME_MANAGER_CONFIG="$1" shift ;; --flake) 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) PASSTHROUGH_OPTS+=("$opt" "$1") shift ;; --override-input) PASSTHROUGH_OPTS+=("$opt" "$1" "$2") shift 2 ;; --experimental-features) PASSTHROUGH_OPTS+=("$opt" "$1") shift ;; --extra-experimental-features) 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 ;; --option|--arg|--argstr) PASSTHROUGH_OPTS+=("$opt" "$1" "$2") shift 2 ;; -j|--max-jobs|--cores|--builders) 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 23.05-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 if [[ -z $COMMAND ]]; then doHelp >&2 exit 1 fi case $COMMAND in edit) doEdit ;; build) doBuild ;; init) doInit "${COMMAND_ARGS[@]}" ;; instantiate) doInstantiate ;; switch) doSwitch ;; generations) doListGens ;; remove-generations) doRmGenerations "${COMMAND_ARGS[@]}" ;; 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 ;; news) doShowNews --all ;; uninstall) doUninstall ;; help) doHelp ;; *) _iError 'Unknown command: %s' "$COMMAND" >&2 doHelp >&2 exit 1 ;; esac # vim: ft=bash