1
0
mirror of https://github.com/nix-community/home-manager synced 2024-06-01 20:43:34 +02:00
home-manager/home-manager/home-manager

439 lines
11 KiB
Plaintext
Raw Normal View History

2017-01-07 19:16:26 +01:00
#!@bash@/bin/bash
# This code explicitly requires GNU Core Utilities and we therefore
# need to ensure they are prioritized over any other similarly named
# tools on the system.
PATH=@coreutils@/bin:@less@/bin${PATH:+:}$PATH
set -euo pipefail
2017-09-01 10:24:01 +02:00
function errorEcho() {
# shellcheck disable=2048,2086
echo $* >&2
2017-09-01 10:24:01 +02:00
}
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
}
# 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
2017-10-22 05:24:32 +02:00
errorEcho "No configuration file found at $HOME_MANAGER_CONFIG"
exit 1
fi
2017-01-07 19:16:26 +01:00
HOME_MANAGER_CONFIG="$(realpath "$HOME_MANAGER_CONFIG")"
return
fi
local defaultConfFile="${XDG_CONFIG_HOME:-$HOME/.config}/nixpkgs/home.nix"
local confFile
for confFile in "$defaultConfFile" \
"$HOME/.nixpkgs/home.nix" ; do
if [[ -e "$confFile" ]] ; then
HOME_MANAGER_CONFIG="$(realpath "$confFile")"
return
fi
done
2017-09-01 10:24:01 +02:00
errorEcho "No configuration file found." \
"Please create one at $defaultConfFile"
exit 1
}
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
export NIX_PATH="home-manager=$path${NIX_PATH:+:}$NIX_PATH"
return
fi
done
}
function doBuildAttr() {
setConfigFile
setHomeManagerNixPath
local extraArgs="$*"
for p in "${EXTRA_NIX_PATH[@]}"; do
extraArgs="$extraArgs -I $p"
done
if [[ -v VERBOSE ]]; then
extraArgs="$extraArgs --show-trace"
fi
# shellcheck disable=2086
if [[ -v USE_NIX2_COMMAND ]]; then
nix build \
-f "<home-manager/home-manager/home-manager.nix>" \
$extraArgs \
--argstr confPath "$HOME_MANAGER_CONFIG" \
--argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
else
nix-build \
"<home-manager/home-manager/home-manager.nix>" \
$extraArgs \
--argstr confPath "$HOME_MANAGER_CONFIG" \
--argstr confAttr "$HOME_MANAGER_CONFIG_ATTRIBUTE"
fi
}
# 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 msg
if [[ $newsNumUnread -eq 1 ]]; then
msg="There is an unread and relevant news item.\n"
msg+="Read it by running the command '$(basename "$0") news'."
else
msg="There are $newsNumUnread unread and relevant news items.\n"
msg+="Read them by running the command '$(basename "$0") news'."
fi
# Not actually an error but here stdout is reserved for
# nix-build output.
errorEcho
errorEcho -e "$msg"
errorEcho
if [[ -v DISPLAY ]] && type -P notify-send > /dev/null; then
notify-send "Home Manager" "$msg"
fi
elif [[ "$newsDisplay" == "show" ]]; then
doShowNews --unread
else
errorEcho "Unknown 'news.display' setting '$newsDisplay'."
fi
}
function doBuild() {
if [[ ! -w . ]]; then
errorEcho "Cannot run build in read-only directory";
return 1
fi
local newsInfo
newsInfo=$(buildNews)
local exitCode
if [[ -v USE_NIX2_COMMAND ]]; then
doBuildAttr activationPackage \
&& exitCode=0 || exitCode=1
else
doBuildAttr --attr activationPackage \
&& exitCode=0 || exitCode=1
fi
presentNews "$newsInfo"
return $exitCode
}
function doSwitch() {
setWorkDir
local newsInfo
newsInfo=$(buildNews)
local generation
local exitCode=0
# Build the generation and run the activate script. Note, we
2017-10-24 12:41:28 +02:00
# 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"
if [[ -v USE_NIX2_COMMAND ]]; then
doBuildAttr \
--out-link "$generation" \
activationPackage \
&& "$generation/activate" || exitCode=1
else
doBuildAttr \
--out-link "$generation" \
--no-build-output \
--attr activationPackage > /dev/null \
&& "$generation/activate" || exitCode=1
fi
presentNews "$newsInfo"
return $exitCode
2017-01-07 19:16:26 +01:00
}
function doListGens() {
# Whether to colorize the generations output.
local color="never"
if [[ -t 1 ]]; then
color="always"
fi
pushd "/nix/var/nix/profiles/per-user/$USER" > /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
2017-01-07 19:16:26 +01:00
}
# Removes linked generations. Takes as arguments identifiers of
# generations to remove.
function doRmGenerations() {
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
pushd "/nix/var/nix/profiles/per-user/$USER" > /dev/null
for generationId in "$@"; do
local linkName="home-manager-$generationId-link"
if [[ ! -e $linkName ]]; then
errorEcho "No generation with ID $generationId"
elif [[ $linkName == $(readlink home-manager) ]]; then
errorEcho "Cannot remove the current generation $generationId"
else
echo Removing generation $generationId
$DRY_RUN_CMD rm $VERBOSE_ARG $linkName
fi
done
popd > /dev/null
}
2017-01-07 19:16:26 +01:00
function doListPackages() {
local outPath
outPath="$(nix-env -q --out-path | grep -o '/.*home-manager-path$')"
if [[ -n "$outPath" ]] ; then
nix-store -q --references "$outPath" | sed 's/[^-]*-//'
else
2017-09-01 10:24:01 +02:00
errorEcho "No home-manager packages seem to be installed."
fi
2017-01-07 19:16:26 +01:00
}
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() {
local output
output="$WORK_DIR/news-info.sh"
if [[ -v USE_NIX2_COMMAND ]]; then
doBuildAttr \
--out-link "$output" \
--quiet \
--arg check false \
--argstr newsReadIdsFile "$(newsReadIdsFile)" \
newsInfo
else
doBuildAttr \
--out-link "$output" \
--no-build-output \
--quiet \
--arg check false \
--argstr newsReadIdsFile "$(newsReadIdsFile)" \
--attr newsInfo \
> /dev/null
fi
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"
;;
*)
errorEcho "Unknown argument $1"
return 1
esac
# shellcheck disable=2154
if [[ -s "$newsUnreadIdsFile" ]]; then
local newsReadIdsFile
newsReadIdsFile="$(newsReadIdsFile)"
cat "$newsUnreadIdsFile" >> "$newsReadIdsFile"
fi
}
2017-01-07 19:16:26 +01:00
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 " -v Verbose output"
echo " -n Do a dry run, only prints what actions would be taken"
echo " -h Print this help"
2017-01-07 19:16:26 +01:00
echo
echo "Commands"
echo
2017-01-12 22:26:24 +01:00
echo " help Print this help"
echo
echo " build Build configuration into result directory"
echo
echo " switch Build and activate configuration"
echo
2017-01-12 22:26:24 +01:00
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
2017-01-12 22:26:24 +01:00
echo " packages List all packages installed in home-manager-path"
echo
echo " news Show news entries in a pager"
2017-01-07 19:16:26 +01:00
}
EXTRA_NIX_PATH=()
HOME_MANAGER_CONFIG_ATTRIBUTE=""
# As a special case, if the user has given --help anywhere on the
# command line then print help and exit.
for arg in "$@"; do
if [[ $arg == "--help" ]]; then
doHelp
exit 0
fi
done
while getopts 2f:I:A:vnh opt; do
case $opt in
2)
USE_NIX2_COMMAND=1
;;
f)
HOME_MANAGER_CONFIG="$OPTARG"
;;
I)
EXTRA_NIX_PATH+=("$OPTARG")
;;
A)
HOME_MANAGER_CONFIG_ATTRIBUTE="$OPTARG"
;;
v)
export VERBOSE=1
;;
n)
export DRY_RUN=1
;;
h)
doHelp
exit 0
;;
*)
2017-09-01 10:24:01 +02:00
errorEcho "Unknown option -$OPTARG"
doHelp >&2
exit 1
;;
esac
done
# Get rid of the options.
shift "$((OPTIND-1))"
if [[ $# -eq 0 ]]; then
doHelp >&2
exit 1
fi
cmd="$1"
shift 1
case "$cmd" in
build)
doBuild
;;
switch)
doSwitch
2017-01-07 19:16:26 +01:00
;;
generations)
doListGens
;;
remove-generations)
doRmGenerations "$@"
;;
2017-01-07 19:16:26 +01:00
packages)
doListPackages
;;
news)
doShowNews --all
;;
2017-01-07 19:16:26 +01:00
help|--help)
doHelp
;;
*)
2017-09-01 10:24:01 +02:00
errorEcho "Unknown command: $cmd"
doHelp >&2
2017-01-07 19:16:26 +01:00
exit 1
2017-01-08 22:08:17 +01:00
;;
2017-01-07 19:16:26 +01:00
esac