mirror of
https://github.com/nix-community/home-manager
synced 2024-11-10 07:04:17 +00:00
c82fc8cf3f
This partially reverts commit 89670e27e1
.
Co-authored-by: Johannes Maier <johannes.maier@mailbox.org>
1124 lines
34 KiB
Bash
1124 lines
34 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 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
|
|
}
|
|
|
|
# Checks whether the 'flakes' and 'nix-command' Nix options are enabled.
|
|
function hasFlakeSupport() {
|
|
type -p nix > /dev/null \
|
|
&& nix show-config 2> /dev/null \
|
|
| 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 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="path:$(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 ? \"$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
|
|
# 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 = "$USER";
|
|
home.homeDirectory = "$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 = "24.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 $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."$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; 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 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
|
|
fi
|
|
|
|
presentNews
|
|
}
|
|
|
|
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
|
|
# 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() {
|
|
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 "$newsJsonFile" \
|
|
--arg newsReadIdsFile "$readIdsFile" \
|
|
"${extraArgs[@]}" \
|
|
> "$newsNixFile"
|
|
}
|
|
|
|
function doShowNews() {
|
|
setWorkDir
|
|
setFlakeAttribute
|
|
|
|
local newsNixFile="$WORK_DIR/news.nix"
|
|
buildNews "$newsNixFile"
|
|
|
|
local readIdsFile
|
|
readIdsFile="$(newsReadIdsFile)"
|
|
|
|
local news
|
|
|
|
# shellcheck disable=2154,2046
|
|
case $1 in
|
|
--all)
|
|
news="$(nix-instantiate --quiet --eval --expr "(import ${newsNixFile}).news.all")"
|
|
;;
|
|
--unread)
|
|
news="$(nix-instantiate --quiet --eval --expr "(import ${newsNixFile}).news.unread")"
|
|
;;
|
|
*)
|
|
_i 'Unknown argument %s' "$1"
|
|
return 1
|
|
esac
|
|
|
|
# Prints the news without surrounding quotes.
|
|
echo -e "${news:1:-1}" | ${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 = "$USER";
|
|
home.homeDirectory = "$HOME";
|
|
home.stateVersion = "24.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 " --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 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=""
|
|
|
|
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)
|
|
[[ -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
|
|
;;
|
|
--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)
|
|
[[ -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 24.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
|
|
;;
|
|
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
|