Files
nixpkgs/pkgs/build-support/setup-hooks/no-broken-symlinks.sh
Dark Steveneq 646b892680
Some checks failed
Periodic Merges (6h) / master → staging-nixos (push) Failing after 12m50s
Periodic Merges (6h) / master → staging-next (push) Failing after 12m54s
Periodic Merges (24h) / merge-base(master,staging) → haskell-updates (push) Failing after 11m54s
Periodic Merges (6h) / staging-next → staging (push) Failing after 12m13s
Periodic Merges (24h) / staging-next-25.05 → staging-25.05 (push) Failing after 13m24s
Periodic Merges (24h) / release-25.05 → staging-next-25.05 (push) Failing after 14m28s
push sheeet
2025-10-09 14:15:47 +02:00

90 lines
3.1 KiB
Bash

# shellcheck shell=bash
# Guard against double inclusion.
if (("${noBrokenSymlinksHookInstalled:-0}" > 0)); then
nixInfoLog "skipping because the hook has been propagated more than once"
return 0
fi
declare -ig noBrokenSymlinksHookInstalled=1
# symlinks are often created in postFixup
# don't use fixupOutputHooks, it is before postFixup
postFixupHooks+=(noBrokenSymlinksInAllOutputs)
# A symlink is "dangling" if it points to a non-existent target.
# A symlink is "reflexive" if it points to itself.
# A symlink is "unreadable" if the readlink command fails, e.g. because of permission errors.
# A symlink is considered "broken" if it is either dangling, reflexive or unreadable.
noBrokenSymlinks() {
local -r output="${1:?}"
local path
local pathParent
local symlinkTarget
local -i numDanglingSymlinks=0
local -i numReflexiveSymlinks=0
local -i numUnreadableSymlinks=0
# NOTE(@connorbaker): This hook doesn't check for cycles in symlinks.
if [[ ! -e $output ]]; then
nixWarnLog "skipping non-existent output $output"
return 0
fi
nixInfoLog "running on $output"
# NOTE: path is absolute because we're running `find` against an absolute path (`output`).
while IFS= read -r -d $'\0' path; do
pathParent="$(dirname "$path")"
if ! symlinkTarget="$(readlink "$path")"; then
nixErrorLog "the symlink $path is unreadable"
numUnreadableSymlinks+=1
continue
fi
# Canonicalize symlinkTarget to an absolute path.
if [[ $symlinkTarget == /* ]]; then
nixInfoLog "symlink $path points to absolute target $symlinkTarget"
else
nixInfoLog "symlink $path points to relative target $symlinkTarget"
# Use --no-symlinks to avoid dereferencing again and --canonicalize-missing to avoid existence
# checks at this step (which can lead to infinite recursion).
symlinkTarget="$(realpath --no-symlinks --canonicalize-missing "$pathParent/$symlinkTarget")"
fi
# use $TMPDIR like audit-tmpdir.sh
if [[ $symlinkTarget = "$TMPDIR"/* ]]; then
nixErrorLog "the symlink $path points to $TMPDIR directory: $symlinkTarget"
numDanglingSymlinks+=1
continue
fi
if [[ $symlinkTarget != "$NIX_STORE"/* ]]; then
nixInfoLog "symlink $path points outside the Nix store; ignoring"
continue
fi
if [[ $path == "$symlinkTarget" ]]; then
nixErrorLog "the symlink $path is reflexive"
numReflexiveSymlinks+=1
elif [[ ! -e $symlinkTarget ]]; then
nixErrorLog "the symlink $path points to a missing target: $symlinkTarget"
numDanglingSymlinks+=1
else
nixDebugLog "the symlink $path is irreflexive and points to a target which exists"
fi
done < <(find "$output" -type l -print0)
if ((numDanglingSymlinks > 0 || numReflexiveSymlinks > 0 || numUnreadableSymlinks > 0)); then
nixErrorLog "found $numDanglingSymlinks dangling symlinks, $numReflexiveSymlinks reflexive symlinks and $numUnreadableSymlinks unreadable symlinks"
exit 1
fi
return 0
}
noBrokenSymlinksInAllOutputs() {
if [[ -z ${dontCheckForBrokenSymlinks-} ]]; then
for output in $(getAllOutputNames); do
noBrokenSymlinks "${!output}"
done
fi
}