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
86 lines
2.6 KiB
Bash
Executable File
86 lines
2.6 KiB
Bash
Executable File
#! /usr/bin/env bash
|
|
|
|
set -eu -o pipefail
|
|
|
|
# A shell implementation for pgrep as we don't want to depend on procps
|
|
pgrep(){
|
|
PATTERN="$1"
|
|
for pid_dir in /proc/[0-9]*; do
|
|
pid="${pid_dir##*/}"
|
|
|
|
# Attempt to open /proc/<PID>/cmdline for reading on a new file descriptor
|
|
# If we can't read it (no permission or doesn't exist), skip
|
|
exec {fd}< "$pid_dir/cmdline" 2>/dev/null || continue
|
|
|
|
cmdline=""
|
|
# Read each null-delimited token from /proc/<PID>/cmdline
|
|
# and join them with a space for easier pattern matching
|
|
while IFS= read -r -d $'\0' arg <&$fd; do
|
|
if [[ -z "$cmdline" ]]; then
|
|
cmdline="$arg"
|
|
else
|
|
cmdline="$cmdline $arg"
|
|
fi
|
|
done
|
|
|
|
# Close the file descriptor
|
|
exec {fd}>&-
|
|
|
|
# If cmdline is non-empty and matches the pattern, print the PID
|
|
if [[ -n "$cmdline" && "$cmdline" =~ $PATTERN ]]; then
|
|
echo "$pid"
|
|
fi
|
|
done
|
|
}
|
|
|
|
# helper to extract variables from the build env
|
|
getVar(){
|
|
while IFS= read -r -d $'\0' line; do
|
|
case "$line" in
|
|
*"$1="* )
|
|
echo "$line"
|
|
;;
|
|
esac
|
|
done < /proc/$pid/environ \
|
|
| cut -d "=" -f 2
|
|
}
|
|
|
|
id="$1"
|
|
pids="$(pgrep "sleep $id" || :)"
|
|
if [ -z "$pids" ]; then
|
|
echo "Error: No process found for 'sleep $id'. The build must still be running in order to attach. Also make sure it's not on a remote builder." >&2
|
|
exit 1s
|
|
elif [ "$(echo "$pids" | wc -l)" -ne 1 ]; then
|
|
echo "Error: Multiple processes found matching 'sleep $id'" >&2
|
|
exit 1
|
|
fi
|
|
pid="$(echo "$pids" | head -n1)"
|
|
|
|
|
|
# get the build top level directory inside the sandbox (eg. /build)
|
|
buildDir=$(getVar NIX_BUILD_TOP)
|
|
|
|
# bash is needed to load the env vars, as we do not know the syntax of the debug shell.
|
|
# bashInteractive is used instead of bash, as we depend on it anyways, due to it being
|
|
# the default debug shell
|
|
bashInteractive="$(getVar bashInteractive)"
|
|
# the debug shell will be started as interactive shell after loading the env vars
|
|
debugShell="$(getVar debugShell)"
|
|
# to drop the user into the working directory at the point of failure
|
|
pwd="$(readlink /proc/$pid/cwd)"
|
|
|
|
# enter the namespace of the failed build
|
|
# bash needs to be executed with --init-file /build/env-vars to include the bash native
|
|
# variables like ones declared via `declare -a`.
|
|
# If another shell is chosen via `debugShell`, it will only have simple env vars avaialable.
|
|
exec nsenter --mount --ipc --uts --pid --net --target "$pid" "$bashInteractive" -c "
|
|
set -eu -o pipefail
|
|
source \"$buildDir/env-vars\"
|
|
cd \"$pwd\"
|
|
if [ -n \"$debugShell\" ]; then
|
|
exec \"$debugShell\"
|
|
else
|
|
exec \"$bashInteractive\" --init-file \"$buildDir/env-vars\"
|
|
fi
|
|
"
|