files: clean up

Specifically, replace the `link` and `clean` scripts by inline Bash
functions. This avoids a number of Bash invocations and simplifies the
code a little. This should also make it slightly easier to implement
support for arbitrary file locations.
This commit is contained in:
Robert Helgesson 2021-07-26 23:57:04 +02:00
parent f6f6990fc8
commit 8a1297444b
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89

View file

@ -106,7 +106,8 @@ in
$VERBOSE_ECHO "Skipping collision check for $targetPath" $VERBOSE_ECHO "Skipping collision check for $targetPath"
elif [[ -e "$targetPath" \ elif [[ -e "$targetPath" \
&& ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then && ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then
if [[ ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then if [[ ! -L "$targetPath" \
&& -v HOME_MANAGER_BACKUP_EXT && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then
backup="$targetPath.$HOME_MANAGER_BACKUP_EXT" backup="$targetPath.$HOME_MANAGER_BACKUP_EXT"
if [[ -e "$backup" ]]; then if [[ -e "$backup" ]]; then
errorEcho "Existing file '$backup' would be clobbered by backing up '$targetPath'" errorEcho "Existing file '$backup' would be clobbered by backing up '$targetPath'"
@ -160,101 +161,101 @@ in
# and a failure during the intermediate state FA ∩ FB will not # and a failure during the intermediate state FA ∩ FB will not
# result in lost links because this set of links are in both the # result in lost links because this set of links are in both the
# source and target generation. # source and target generation.
home.activation.linkGeneration = hm.dag.entryAfter ["writeBoundary"] ( home.activation.linkGeneration = hm.dag.entryAfter ["writeBoundary"] (''
let function clean() {
link = pkgs.writeShellScript "link" '' # A symbolic link whose target path matches this pattern will be
newGenFiles="$1" # considered part of a Home Manager generation.
shift local homeFilePattern
for sourcePath in "$@" ; do homeFilePattern="$(readlink -e ${escapeShellArg builtins.storeDir})/*-home-manager-files/*"
relativePath="''${sourcePath#$newGenFiles/}"
targetPath="$HOME/$relativePath"
if [[ -e "$targetPath" && ! -L "$targetPath" && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then
backup="$targetPath.$HOME_MANAGER_BACKUP_EXT"
$DRY_RUN_CMD mv $VERBOSE_ARG "$targetPath" "$backup" || errorEcho "Moving '$targetPath' failed!"
fi
$DRY_RUN_CMD mkdir -p $VERBOSE_ARG "$(dirname "$targetPath")"
$DRY_RUN_CMD ln -nsf $VERBOSE_ARG "$sourcePath" "$targetPath"
done
'';
cleanup = pkgs.writeShellScript "cleanup" '' local newGenFiles="$1"
. ${./lib-bash/color-echo.sh} local relativePath="$2"
local targetPath="$HOME/$relativePath"
if [[ -e "$newGenFiles/$relativePath" ]] ; then
$VERBOSE_ECHO "Checking $targetPath: exists"
elif [[ ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then
warnEcho "Path '$targetPath' does not link into a Home Manager generation. Skipping delete."
else
$VERBOSE_ECHO "Checking $targetPath: gone (deleting)"
$DRY_RUN_CMD rm $VERBOSE_ARG "$targetPath"
# A symbolic link whose target path matches this pattern will be # Recursively delete empty parent directories.
# considered part of a Home Manager generation. targetDir="$(dirname "$relativePath")"
homeFilePattern="$(readlink -e ${escapeShellArg builtins.storeDir})/*-home-manager-files/*" if [[ "$targetDir" != "." ]] ; then
pushd "$HOME" > /dev/null
newGenFiles="$1" # Call rmdir with a relative path excluding $HOME.
shift 1 # Otherwise, it might try to delete $HOME and exit
for relativePath in "$@" ; do # with a permission error.
targetPath="$HOME/$relativePath" $DRY_RUN_CMD rmdir $VERBOSE_ARG \
if [[ -e "$newGenFiles/$relativePath" ]] ; then -p --ignore-fail-on-non-empty \
$VERBOSE_ECHO "Checking $targetPath: exists" "$targetDir"
elif [[ ! "$(readlink "$targetPath")" == $homeFilePattern ]] ; then
warnEcho "Path '$targetPath' does not link into a Home Manager generation. Skipping delete."
else
$VERBOSE_ECHO "Checking $targetPath: gone (deleting)"
$DRY_RUN_CMD rm $VERBOSE_ARG "$targetPath"
# Recursively delete empty parent directories. popd > /dev/null
targetDir="$(dirname "$relativePath")"
if [[ "$targetDir" != "." ]] ; then
pushd "$HOME" > /dev/null
# Call rmdir with a relative path excluding $HOME.
# Otherwise, it might try to delete $HOME and exit
# with a permission error.
$DRY_RUN_CMD rmdir $VERBOSE_ARG \
-p --ignore-fail-on-non-empty \
"$targetDir"
popd > /dev/null
fi
fi
done
'';
in
''
function linkNewGen() {
echo "Creating home file links in $HOME"
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" \( -type f -or -type l \) \
-exec bash ${link} "$newGenFiles" {} +
}
function cleanOldGen() {
if [[ ! -v oldGenPath ]] ; then
return
fi
echo "Cleaning up orphan links from $HOME"
local newGenFiles oldGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
oldGenFiles="$(readlink -e "$oldGenPath/home-files")"
# Apply the cleanup script on each leaf in the old
# generation. The find command below will print the
# relative path of the entry.
find "$oldGenFiles" '(' -type f -or -type l ')' -printf '%P\0' \
| xargs -0 bash ${cleanup} "$newGenFiles"
}
cleanOldGen
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
echo "Creating profile generation $newGenNum"
$DRY_RUN_CMD nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
$DRY_RUN_CMD ln -Tsf $VERBOSE_ARG "$newGenPath" "$newGenGcPath"
else
echo "No change so reusing latest profile generation $oldGenNum"
fi fi
fi
}
linkNewGen function cleanOldGen() {
'' if [[ ! -v oldGenPath ]] ; then
); return
fi
echo "Cleaning up orphan links from $HOME"
local newGenFiles oldGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
oldGenFiles="$(readlink -e "$oldGenPath/home-files")"
# Apply the cleanup script on each leaf in the old
# generation. The find command below will print the
# relative path of the entry.
find "$oldGenFiles" '(' -type f -or -type l ')' -printf '%P\0' \
| while IFS= read -r -d "" relativePath ; do \
clean "$newGenFiles" "$relativePath"; \
done
}
function link() {
local newGenFiles="$1"
local sourcePath="$2"
local relativePath="''${sourcePath#$newGenFiles/}"
local targetPath="$HOME/$relativePath"
if [[ -e "$targetPath" && ! -L "$targetPath" \
&& -v HOME_MANAGER_BACKUP_EXT && -n "$HOME_MANAGER_BACKUP_EXT" ]] ; then
local backup="$targetPath.$HOME_MANAGER_BACKUP_EXT"
$DRY_RUN_CMD mv $VERBOSE_ARG "$targetPath" "$backup" \
|| errorEcho "Moving '$targetPath' failed!"
fi
$DRY_RUN_CMD mkdir -p $VERBOSE_ARG "$(dirname "$targetPath")"
$DRY_RUN_CMD ln -nsf $VERBOSE_ARG "$sourcePath" "$targetPath"
}
function linkNewGen() {
echo "Creating home file links in $HOME"
local newGenFiles
newGenFiles="$(readlink -e "$newGenPath/home-files")"
find "$newGenFiles" \( -type f -or -type l \) -print0 \
| while IFS= read -r -d "" sourcePath ; do \
link "$newGenFiles" "$sourcePath"; \
done
}
cleanOldGen
if [[ ! -v oldGenPath || "$oldGenPath" != "$newGenPath" ]] ; then
echo "Creating profile generation $newGenNum"
$DRY_RUN_CMD nix-env $VERBOSE_ARG --profile "$genProfilePath" --set "$newGenPath"
$DRY_RUN_CMD ln -Tsf $VERBOSE_ARG "$newGenPath" "$newGenGcPath"
else
echo "No change so reusing latest profile generation $oldGenNum"
fi
linkNewGen
unset -f clean cleanOldGen link linkNewGen
'');
home.activation.checkFilesChanged = hm.dag.entryBefore ["linkGeneration"] ( home.activation.checkFilesChanged = hm.dag.entryBefore ["linkGeneration"] (
let let