diff --git a/doc/hooks/index.xml b/doc/hooks/index.xml
index ed703c03d8b0..0917fac6c0ac 100644
--- a/doc/hooks/index.xml
+++ b/doc/hooks/index.xml
@@ -22,6 +22,7 @@
+
diff --git a/doc/hooks/patch-rc-path-hooks.section.md b/doc/hooks/patch-rc-path-hooks.section.md
new file mode 100644
index 000000000000..5c870dc782c2
--- /dev/null
+++ b/doc/hooks/patch-rc-path-hooks.section.md
@@ -0,0 +1,50 @@
+
+# `patchRcPath` hooks {#sec-patchRcPathHooks}
+
+These hooks provide shell-specific utilities (with the same name as the hook) to patch shell scripts meant to be sourced by software users.
+
+The typical usage is to patch initialisation or [rc](https://unix.stackexchange.com/questions/3467/what-does-rc-in-bashrc-stand-for) scripts inside `$out/bin` or `$out/etc`.
+Such scripts, when being sourced, would insert the binary locations of certain commands into `PATH`, modify other environment variables or run a series of start-up commands.
+When shipped from the upstream, they sometimes use commands that might not be available in the environment they are getting sourced in.
+
+The compatible shells for each hook are:
+
+ - `patchRcPathBash`: [Bash](https://www.gnu.org/software/bash/), [ksh](http://www.kornshell.org/), [zsh](https://www.zsh.org/) and other shells supporting the Bash-like parameter expansions.
+ - `patchRcPathCsh`: Csh scripts, such as those targeting [tcsh](https://www.tcsh.org/).
+ - `patchRcPathFish`: [Fish](https://fishshell.com/) scripts.
+ - `patchRcPathPosix`: POSIX-conformant shells supporting the limited parameter expansions specified by the POSIX standard. Current implementation uses the parameter expansion `${foo-}` only.
+
+For each supported shell, it modifies the script with a `PATH` prefix that is later removed when the script ends.
+It allows nested patching, which guarantees that a patched script may source another patched script.
+
+Syntax to apply the utility to a script:
+
+```sh
+patchRcPath
+```
+
+Example usage:
+
+Given a package `foo` containing an init script `this-foo.fish` that depends on `coreutils`, `man` and `which`,
+patch the init script for users to source without having the above dependencies in their `PATH`:
+
+```nix
+{ lib, stdenv, patchRcPathFish}:
+stdenv.mkDerivation {
+
+ # ...
+
+ nativeBuildInputs = [
+ patchRcPathFish
+ ];
+
+ postFixup = ''
+ patchRcPathFish $out/bin/this-foo.fish ${lib.makeBinPath [ coreutils man which ]}
+ '';
+}
+```
+
+::: {.note}
+`patchRcPathCsh` and `patchRcPathPosix` implementation depends on `sed` to do the string processing.
+The others are in vanilla shell and have no third-party dependencies.
+:::
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/default.nix b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/default.nix
new file mode 100644
index 000000000000..73fbfa6e9b72
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/default.nix
@@ -0,0 +1,64 @@
+{ lib
+, callPackage
+, makeSetupHook
+, gnused
+}:
+let
+ tests = import ./test { inherit callPackage; };
+in
+{
+ patchRcPathBash = (makeSetupHook
+ {
+ name = "patch-rc-path-bash";
+ meta = with lib; {
+ description = "Setup-hook to inject source-time PATH prefix to a Bash/Ksh/Zsh script";
+ maintainers = with maintainers; [ ShamrockLee ];
+ };
+ } ./patch-rc-path-bash.sh).overrideAttrs (oldAttrs: {
+ passthru.tests = {
+ inherit (tests) test-bash;
+ };
+ });
+ patchRcPathCsh = (makeSetupHook
+ {
+ name = "patch-rc-path-csh";
+ substitutions = {
+ sed = "${gnused}/bin/sed";
+ };
+ meta = with lib; {
+ description = "Setup-hook to inject source-time PATH prefix to a Csh script";
+ maintainers = with maintainers; [ ShamrockLee ];
+ };
+ } ./patch-rc-path-csh.sh).overrideAttrs (oldAttrs: {
+ passthru.tests = {
+ inherit (tests) test-csh;
+ };
+ });
+ patchRcPathFish = (makeSetupHook
+ {
+ name = "patch-rc-path-fish";
+ meta = with lib; {
+ description = "Setup-hook to inject source-time PATH prefix to a Fish script";
+ maintainers = with maintainers; [ ShamrockLee ];
+ };
+ } ./patch-rc-path-fish.sh).overrideAttrs (oldAttrs: {
+ passthru.tests = {
+ inherit (tests) test-fish;
+ };
+ });
+ patchRcPathPosix = (makeSetupHook
+ {
+ name = "patch-rc-path-posix";
+ substitutions = {
+ sed = "${gnused}/bin/sed";
+ };
+ meta = with lib; {
+ description = "Setup-hook to inject source-time PATH prefix to a POSIX shell script";
+ maintainers = with maintainers; [ ShamrockLee ];
+ };
+ } ./patch-rc-path-posix.sh).overrideAttrs (oldAttrs: {
+ passthru.tests = {
+ inherit (tests) test-posix;
+ };
+ });
+}
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-bash.sh b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-bash.sh
new file mode 100644
index 000000000000..b98b983861b0
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-bash.sh
@@ -0,0 +1,50 @@
+patchRcPathBash(){
+ local FILE_TO_PATCH="$1"
+ local SOURCETIME_PATH="$2"
+ local FILE_TO_WORK_ON="$(mktemp "$(basename "$FILE_TO_PATCH").XXXXXX.tmp")"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to add to PATH the source-time utilities for Nixpkgs packaging
+if [[ -n "\${NIXPKGS_SOURCETIME_PATH-}" ]]; then
+ NIXPKGS_SOURCETIME_PATH_OLD="\$NIXPKGS_SOURCETIME_PATH;\${NIXPKGS_SOURCETIME_PATH_OLD-}"
+fi
+NIXPKGS_SOURCETIME_PATH="$SOURCETIME_PATH"
+if [[ -n "\$PATH" ]]; then
+ PATH="\$NIXPKGS_SOURCETIME_PATH:\$PATH"
+else
+ PATH="\$NIXPKGS_SOURCETIME_PATH"
+fi
+export PATH
+# End of lines to add to PATH source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_PATCH" >> "$FILE_TO_WORK_ON"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+if [[ -n "\${PATH-}" ]]; then
+ # Remove the inserted section
+ PATH="\${PATH/\$NIXPKGS_SOURCETIME_PATH}"
+ # Remove the duplicated colons
+ PATH="\${PATH//::/:}"
+ # Remove the prefixing colon
+ if [[ -n "\$PATH" && "\${PATH:0:1}" == ":" ]]; then
+ PATH="\${PATH:1}"
+ fi
+ # Remove the trailing colon
+ if [[ -n "\$PATH" && "\${PATH:\${#PATH}-1}" == ":" ]]; then
+ PATH="\${PATH::}"
+ fi
+ export PATH
+fi
+if [[ -n "\${NIXPKGS_SOURCETIME_PATH_OLD-}" ]]; then
+ IFS="" read -r -d ";" NIXPKGS_SOURCETIME_PATH <<< "\$NIXPKGS_SOURCETIME_PATH_OLD"
+ NIXPKGS_SOURCETIME_PATH_OLD="\${NIXPKGS_SOURCETIME_PATH_OLD:\${#NIXPKGS_SOURCETIME_PATH}+1}"
+else
+ unset NIXPKGS_SOURCETIME_PATH
+fi
+if [[ -z "\${NIXPKGS_SOURCETIME_PATH_OLD-}" ]]; then
+ unset NIXPKGS_SOURCETIME_PATH_OLD
+fi
+# End of lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_WORK_ON" > "$FILE_TO_PATCH"
+ rm "$FILE_TO_WORK_ON"
+}
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-csh.sh b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-csh.sh
new file mode 100644
index 000000000000..5e2367003ade
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-csh.sh
@@ -0,0 +1,57 @@
+patchRcPathCsh(){
+ local FILE_TO_PATCH="$1"
+ local SOURCETIME_PATH="$2"
+ local FILE_TO_WORK_ON="$(mktemp "$(basename "$FILE_TO_PATCH").XXXXXX.tmp")"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to add to PATH the source-time utilities for Nixpkgs packaging
+if (\$?NIXPKGS_SOURCETIME_PATH) then
+ if ("\$NIXPKGS_SOURCETIME_PATH" != "") then
+ if (\$?NIXPKGS_SOURCETIME_PATH_OLD) then
+ if ("\$NIXPKGS_SOURCETIME_PATH_OLD" != "")
+ set NIXPKGS_SOURCETIME_PATH_OLD = (\$NIXPKGS_SOURCETIME_PATH \$NIXPKGS_SOURCETIME_PATH_OLD)
+ else
+ set NIXPKGS_SOURCETIME_PATH_OLD = \$NIXPKGS_SOURCETIME_PATH
+ endif
+ else
+ set NIXPKGS_SOURCETIME_PATH_OLD = \$NIXPKGS_SOURCETIME_PATH
+ endif
+ endif
+endif
+set NIXPKGS_SOURCETIME_PATH = "$SOURCETIME_PATH"
+if (! \$?PATH) then
+ setenv PATH ""
+endif
+if ("\$PATH" != "") then
+ setenv PATH "\${NIXPKGS_SOURCETIME_PATH}:\$PATH"
+else
+ setenv PATH "\$NIXPKGS_SOURCETIME_PATH"
+endif
+# End of lines to add to PATH source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_PATCH" >> "$FILE_TO_WORK_ON"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+if (\$?PATH) then
+ if ("\$PATH" != "") then
+ # Remove the inserted section, the duplicated colons, and the leading and trailing colon
+ setenv PATH \`echo "\$PATH" | @sed@ "s#\${NIXPKGS_SOURCETIME_PATH}##" | @sed@ "s#::#:#g" | @sed@ "s#^:##" | @sed@ 's#:\$##'\`
+ endif
+endif
+if (\$?NIXPKGS_SOURCETIME_PATH_OLD) then
+ if ("\$NIXPKGS_SOURCETIME_PATH_OLD" != "") then
+ set NIXPKGS_SOURCETIME_PATH = \$NIXPKGS_SOURCETIME_PATH_OLD[1]
+ set NIXPKGS_SOURCETIME_PATH_OLD = \$NIXPKGS_SOURCETIME_PATH_OLD[2-]
+ else
+ unset NIXPKGS_SOURCETIME_PATH
+ endif
+ if (NIXPKGS_SOURCETIME_PATH_OLD == "") then
+ unset NIXPKGS_SOURCETIME_PATH_OLD
+ endif
+else
+ unset NIXPKGS_SOURCETIME_PATH
+endif
+# End of lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_WORK_ON" > "$FILE_TO_PATCH"
+ rm "$FILE_TO_WORK_ON"
+}
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-fish.sh b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-fish.sh
new file mode 100644
index 000000000000..3d3e08c57a11
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-fish.sh
@@ -0,0 +1,50 @@
+patchRcPathFish(){
+ local FILE_TO_PATCH="$1"
+ local SOURCETIME_PATH="$2"
+ local FILE_TO_WORK_ON="$(mktemp "$(basename "$FILE_TO_PATCH").XXXXXX.tmp")"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to add to PATH the source-time utilities for Nixpkgs packaging
+if set -q NIXPKGS_SOURCETIME_PATH && test (count \$NIXPKGS_SOURCETIME_PATH) -gt 0
+ set --unpath NIXPKGS_SOURCETIME_PATH_OLD "\$NIXPKGS_SOURCETIME_PATH" \$NIXPKGS_SOURCETIME_PATH_OLD
+end
+set --path NIXPKGS_SOURCETIME_PATH $SOURCETIME_PATH
+set -g --path PATH \$NIXPKGS_SOURCETIME_PATH \$PATH
+# End of lines to add to PATH source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_PATCH" >> "$FILE_TO_WORK_ON"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+if set -q PATH && test "\$PATH" != "" && test (count \$PATH) -ge (count \$NIXPKGS_SOURCETIME_PATH)
+ # Remove the inserted section
+ for i in (seq 0 (math (count \$PATH) - (count \$NIXPKGS_SOURCETIME_PATH)))
+ for j in (seq 1 (count \$NIXPKGS_SOURCETIME_PATH))
+ if test \$PATH[(math \$i + \$j)] != \$NIXPKGS_SOURCETIME_PATH[\$j]
+ set i -1
+ break
+ end
+ end
+ if test \$i -eq -1
+ continue
+ end
+ if test \$i -eq 0
+ set -g --path PATH \$PATH[(math (count \$NIXPKGS_SOURCETIME_PATH) + 1)..]
+ else
+ set -g --path PATH \$PATH[..\$i] \$PATH[(math (count \$NIXPKGS_SOURCETIME_PATH) + 1 + \$i)..]
+ end
+ break
+ end
+end
+if set -q NIXPKGS_SOURCETIME_PATH_OLD && test (count \$NIXPKGS_SOURCETIME_PATH_OLD) -gt 0
+ set --path NIXPKGS_SOURCETIME_PATH \$NIXPKGS_SOURCETIME_PATH_OLD[1]
+ set --unpath NIXPKGS_SOURCETIME_PATH_OLD \$NIXPKGS_SOURCETIME_PATH_OLD[2..]
+else
+ set -e NIXPKGS_SOURCETIME_PATH
+end
+if set -q NIXPKGS_SOURCETIME_PATH_OLD && test (count \$NIXPKGS_SOURCETIME_PATH_OLD) -eq 0
+ set -e NIXPKGS_SOURCETIME_PATH_OLD
+end
+# End of lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_WORK_ON" > "$FILE_TO_PATCH"
+ rm "$FILE_TO_WORK_ON"
+}
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-posix.sh b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-posix.sh
new file mode 100644
index 000000000000..a3740d4436d9
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/patch-rc-path-posix.sh
@@ -0,0 +1,39 @@
+patchRcPathPosix(){
+ local FILE_TO_PATCH="$1"
+ local SOURCETIME_PATH="$2"
+ local FILE_TO_WORK_ON="$(mktemp "$(basename "$FILE_TO_PATCH").XXXXXX.tmp")"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to add to PATH the source-time utilities for Nixpkgs packaging
+if [ -n "\${NIXPKGS_SOURCETIME_PATH-}" ]; then
+ NIXPKGS_SOURCETIME_PATH_OLD="\$NIXPKGS_SOURCETIME_PATH;\${NIXPKGS_SOURCETIME_PATH_OLD-}"
+fi
+NIXPKGS_SOURCETIME_PATH="$SOURCETIME_PATH"
+if [ -n "\$PATH" ]; then
+ PATH="\$NIXPKGS_SOURCETIME_PATH:\$PATH";
+else
+ PATH="\$NIXPKGS_SOURCETIME_PATH"
+fi
+export PATH
+# End of lines to add to PATH source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_PATCH" >> "$FILE_TO_WORK_ON"
+ cat <> "$FILE_TO_WORK_ON"
+# Lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+if [ -n "\${PATH-}" ]; then
+ PATH="\$(echo "\$PATH" | @sed@ "s#\$NIXPKGS_SOURCETIME_PATH##" | @sed@ "s#::#:#g" | @sed@ "s#^:##" | @sed@ "s#:\\\$##")"
+ export PATH
+fi
+if [ -n "\${NIXPKGS_SOURCETIME_PATH_OLD-}" ]; then
+ NIXPKGS_SOURCETIME_PATH="\$(echo "\$NIXPKGS_SOURCETIME_PATH_OLD" | @sed@ "s#\\([^;]\\);.*#\\1#")"
+ NIXPKGS_SOURCETIME_PATH_OLD="\$(echo "\$NIXPKGS_SOURCETIME_PATH_OLD" | @sed@ "s#[^;];\\(.*\\)#\\1#")"
+else
+ unset NIXPKGS_SOURCETIME_PATH
+fi
+if [ -z "\${NIXPKGS_SOURCETIME_PATH_OLD-}" ]; then
+ unset NIXPKGS_SOURCETIME_PATH_OLD
+fi
+# End of lines to clean up inside PATH the source-time utilities for Nixpkgs packaging
+EOF
+ cat "$FILE_TO_WORK_ON" > "$FILE_TO_PATCH"
+ rm "$FILE_TO_WORK_ON"
+}
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/default.nix b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/default.nix
new file mode 100644
index 000000000000..82bc160387ee
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/default.nix
@@ -0,0 +1,442 @@
+{ callPackage }:
+
+{
+ test-bash = callPackage
+ (
+ { lib
+ , runCommandLocal
+ , bash
+ , hello
+ , ksh
+ , patchRcPathBash
+ , shellcheck
+ , zsh
+ }:
+ runCommandLocal "patch-rc-path-bash-test"
+ {
+ nativeBuildInputs = [
+ bash
+ ksh
+ patchRcPathBash
+ shellcheck
+ zsh
+ ];
+ meta = {
+ description = "Package test of patchActivateBash";
+ inherit (patchRcPathBash.meta) maintainers;
+ };
+ }
+ ''
+ set -eu -o pipefail
+
+
+ # Check the setup hook script
+
+ echo "Running shellcheck against ${./test-sourcing-bash}"
+ shellcheck -s bash --exclude SC1090 ${./test-sourcing-bash}
+ shellcheck -s ksh --exclude SC1090 ${./test-sourcing-bash}
+
+
+ # Test patching a blank file
+
+ echo > blank.bash
+
+ echo "Generating blank_patched.bash from blank.bash"
+ cp blank.bash blank_patched.bash
+ patchRcPathBash blank_patched.bash "$PWD/delta:$PWD/foxtrot"
+
+ echo "Running shellcheck against blank_patched.bash"
+ shellcheck -s bash blank_patched.bash
+ shellcheck -s ksh blank_patched.bash
+
+ echo "Testing in Bash if blank.bash and blank_patched.bash modifies PATH the same way"
+ bash ${./test-sourcing-bash} ./blank.bash ./blank_patched.bash
+
+ echo "Testing in Ksh if blank.bash and blank_patched.bash modifies PATH the same way"
+ ksh ${./test-sourcing-bash} "$PWD/blank.bash" "$PWD/blank_patched.bash"
+
+ echo "Testing in Zsh if blank.bash and blank_patched.bash modifies PATH the same way"
+ zsh ${./test-sourcing-bash} ./blank.bash ./blank_patched.bash
+
+
+ # Test patching silent_hello
+
+ echo "hello > /dev/null" > silent_hello.bash
+
+ echo "Generating silent_hello_patched.bash from silent_hello.bash"
+ cp silent_hello.bash silent_hello_patched.bash
+ patchRcPathBash silent_hello_patched.bash "${hello}/bin"
+
+ echo "Running shellcheck against silent_hello_patched.bash"
+ shellcheck -s bash silent_hello_patched.bash
+
+ echo "Testing in Bash if silent_hello_patched.bash get sourced without error"
+ bash -eu -o pipefail -c ". ./silent_hello_patched.bash"
+
+ echo "Testing in Ksh if silent_hello_patched.bash get sourced without error"
+ ksh -eu -o pipefail -c ". ./silent_hello_patched.bash"
+
+ echo "Testing in Zsh if silent_hello_patched.bash get sourced without error"
+ zsh -eu -o pipefail -c ". ./silent_hello_patched.bash"
+
+
+ # Check the sample source
+
+ echo "Running shellcheck against sample_source.bash"
+ shellcheck -s bash ${./sample_source.bash}
+ shellcheck -s ksh ${./sample_source.bash}
+
+
+ # Test patching the sample source
+
+ cp ${./sample_source.bash} sample_source_patched.bash
+ chmod u+w sample_source_patched.bash
+
+ echo "Generating sample_source_patched.bash from ./sample_source.bash"
+ patchRcPathBash sample_source_patched.bash "$PWD/delta:$PWD/foxtrot"
+
+ echo "Running shellcheck against sample_source_patched.bash"
+ shellcheck -s bash sample_source_patched.bash
+
+ echo "Testing in Bash if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ bash ${./test-sourcing-bash} ${./sample_source.bash} ./sample_source_patched.bash
+
+ echo "Testing in Ksh if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ ksh ${./test-sourcing-bash} ${./sample_source.bash} "$PWD/sample_source_patched.bash"
+
+ echo "Testing in Zsh if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ zsh ${./test-sourcing-bash} ${./sample_source.bash} ./sample_source_patched.bash
+
+
+ # Test double-patching the sample source
+
+ echo "Patching again sample_source_patched.bash"
+ patchRcPathBash sample_source_patched.bash "$PWD/foxtrot:$PWD/golf"
+
+ echo "Running shellcheck against sample_source_patched.bash"
+ shellcheck -s bash sample_source_patched.bash
+ shellcheck -s ksh sample_source_patched.bash
+
+ echo "Testing in Bash if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ bash ${./test-sourcing-bash} ${./sample_source.bash} ./sample_source_patched.bash
+
+ echo "Testing in Ksh if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ ksh ${./test-sourcing-bash} ${./sample_source.bash} "$PWD/sample_source_patched.bash"
+
+ echo "Testing in Zsh if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ zsh ${./test-sourcing-bash} ${./sample_source.bash} ./sample_source_patched.bash
+
+
+ # Create a dummy output
+ touch "$out"
+ ''
+ )
+ { };
+
+
+
+ test-csh = callPackage
+ (
+ { lib
+ , runCommandLocal
+ , gnused
+ , hello
+ , patchRcPathCsh
+ , tcsh
+ }:
+ runCommandLocal "patch-rc-path-csh-test"
+ {
+ nativeBuildInputs = [
+ patchRcPathCsh
+ tcsh
+ ];
+ meta = {
+ description = "Package test of patchActivateCsh";
+ inherit (patchRcPathCsh.meta) maintainers;
+ };
+ }
+ ''
+ set -eu -o pipefail
+
+
+ # Test patching a blank file
+
+ echo > blank.csh
+
+ echo "Generating blank_patched.csh from blank.csh"
+ cp blank.csh blank_patched.csh
+ patchRcPathCsh blank_patched.csh "$PWD/delta:$PWD/foxtrot"
+
+ echo "Testing in Csh if blank.csh and blank_patched.csh modifies PATH the same way"
+ tcsh -e ${./test-sourcing-csh} blank.csh blank_patched.csh
+
+
+ # Test patching silent_hello file
+
+ echo "hello > /dev/null" > silent_hello.csh
+
+ echo "Generating silent_hello_patched.csh from silent_hello.csh"
+ cp silent_hello.csh silent_hello_patched.csh
+ patchRcPathCsh silent_hello_patched.csh "${hello}/bin"
+
+ echo "Testing in Csh if silent_hello_patched.csh get sourced without errer"
+ tcsh -e -c "source silent_hello_patched.csh"
+
+
+ # Generate the sample source
+
+ substitute ${./sample_source.csh.in} sample_source.csh --replace @sed@ ${gnused}/bin/sed
+ chmod u+rw sample_source.csh
+
+
+ # Test patching the sample source
+
+ echo "Generating sample_source_patched.csh from sample_source.csh"
+ cp sample_source.csh sample_source_patched.csh
+ chmod u+w sample_source_patched.csh
+ patchRcPathCsh sample_source_patched.csh "$PWD/delta:$PWD/foxtrot"
+
+ echo "Testing in Csh if sample_source.csh and sample_source_patched.csh modifies PATH the same way"
+ tcsh -e ${./test-sourcing-csh} sample_source.csh sample_source_patched.csh
+
+
+ # Test double-patching the sample source
+
+ echo "Patching again sample_source_patched.csh from sample_source.csh"
+ patchRcPathCsh sample_source_patched.csh "$PWD/foxtrot:$PWD/golf"
+
+ echo "Testing in Csh if sample_source.csh and sample_source_patched.csh modifies PATH the same way"
+ tcsh -e ${./test-sourcing-csh} sample_source.csh sample_source_patched.csh
+
+
+ # Create a dummy output
+ touch "$out"
+ ''
+ )
+ { };
+
+
+
+ test-fish = callPackage
+ (
+ { lib
+ , runCommandLocal
+ , fish
+ , hello
+ , patchRcPathFish
+ }:
+ runCommandLocal "patch-rc-path-fish-test"
+ {
+ nativeBuildInputs = [
+ fish
+ patchRcPathFish
+ ];
+ meta = {
+ description = "Package test of patchActivateFish";
+ inherit (patchRcPathFish.meta) maintainers;
+ };
+ }
+ ''
+ set -eu -o pipefail
+
+
+ # Test patching a blank file
+
+ echo > blank.fish
+
+ echo "Generating blank_patched.fish from blank.fish"
+ cp blank.fish blank_patched.fish
+ patchRcPathFish blank_patched.fish "$PWD/delta:$PWD/foxtrot"
+
+ echo "Testing in Fish if blank.fish and blank_patched.fish modifies PATH the same way"
+ HOME_TEMP="$(mktemp -d temporary_home_XXXXXX)"
+ HOME="$HOME_TEMP" fish ${./test-sourcing-fish} blank.fish blank_patched.fish
+ rm -r "$HOME_TEMP"
+
+
+ # Test patching silent_hello file
+
+ echo "hello > /dev/null" > silent_hello.fish
+
+ echo "Generating silent_hello_patched.fish from silent_hello.fish"
+ cp silent_hello.fish silent_hello_patched.fish
+ patchRcPathFish silent_hello_patched.fish "${hello}/bin"
+
+ echo "Testing in Fish if silent_hello_patched.fish get sourced without error"
+ HOME_TEMP="$(mktemp -d temporary_home_XXXXXX)"
+ HOME="$HOME_TEMP" fish -c "source silent_hello_patched.fish"
+ rm -r "$HOME_TEMP"
+
+
+ # Test patching the sample source
+
+ cp ${./sample_source.fish} sample_source_patched.fish
+ chmod u+w sample_source_patched.fish
+
+ echo "Generating sample_source_patched.fish from ${./sample_source.fish}"
+ patchRcPathFish sample_source_patched.fish "$PWD/delta:$PWD/foxtrot"
+ echo "Testing in Fish if sample_source.fish and sample_source_patched.fish modifies PATH the same way"
+ HOME_TEMP="$(mktemp -d temporary_home_XXXXXX)"
+ HOME="$HOME_TEMP" fish ${./test-sourcing-fish} ${./sample_source.fish} sample_source_patched.fish
+ rm -r "$HOME_TEMP"
+
+
+ # Test double-patching the sample source
+
+ echo "Patching again sample_source_patched.fish from ${./sample_source.fish}"
+ patchRcPathFish sample_source_patched.fish "$PWD/foxtrot:$PWD/golf"
+
+ echo "Testing in Fish if sample_source.fish and sample_source_patched.fish modifies PATH the same way"
+ HOME_TEMP="$(mktemp -d temporary_home_XXXXXX)"
+ HOME="$HOME_TEMP" fish ${./test-sourcing-fish} ${./sample_source.fish} sample_source_patched.fish
+ rm -r "$HOME_TEMP"
+
+
+ # Create a dummy output
+ touch "$out"
+ ''
+ )
+ { };
+
+
+
+ test-posix = callPackage
+ (
+ { lib
+ , runCommandLocal
+ , bash
+ , dash
+ , gnused
+ , hello
+ , ksh
+ , patchRcPathPosix
+ , shellcheck
+ }:
+ runCommandLocal "patch-rc-path-posix-test"
+ {
+ nativeBuildInputs = [
+ bash
+ dash
+ ksh
+ patchRcPathPosix
+ shellcheck
+ ];
+ meta = {
+ description = "Package test of patchActivatePosix";
+ inherit (patchRcPathPosix.meta) maintainers;
+ };
+ }
+ ''
+ set -eu -o pipefail
+
+
+ # Check the setup hook script
+
+ echo "Running shellcheck against ${./test-sourcing-posix}"
+ shellcheck -s sh --exclude SC1090 ${./test-sourcing-posix}
+ shellcheck -s dash --exclude SC1090 ${./test-sourcing-posix}
+
+
+ # Test patching a blank file
+
+ echo > blank.sh
+
+ echo "Generating blank_patched.sh from blank.sh"
+ cp blank.sh blank_patched.sh
+ patchRcPathPosix blank_patched.sh "$PWD/delta:$PWD/foxtrot"
+
+ echo "Running shellcheck against blank_patched.sh"
+ shellcheck -s sh blank_patched.sh
+ shellcheck -s dash blank_patched.sh
+
+ echo "Testing in Bash if blank.sh and blank_patched.sh modifies PATH the same way"
+ bash --posix ${./test-sourcing-posix} ./blank.sh ./blank_patched.sh
+
+ echo "Testing in Dash if blank.sh and blank_patched.sh modifies PATH the same way"
+ dash ${./test-sourcing-posix} ./blank.sh ./blank_patched.sh
+
+ echo "Testing in Ksh if ./blank.sh and ./blank_patched.sh modifies PATH the same way"
+ ksh ${./test-sourcing-posix} "$PWD/blank.sh" "$PWD/blank_patched.sh"
+
+
+ # Test patching silent_hello file
+
+ echo "hello > /dev/null" > silent_hello.sh
+
+ echo "Generating silent_hello_patched.sh from silent_hello.sh"
+ cp silent_hello.sh silent_hello_patched.sh
+ patchRcPathPosix silent_hello_patched.sh "${hello}/bin"
+
+ echo "Running shellcheck against silent_hello_patched.sh"
+ shellcheck -s sh silent_hello_patched.sh
+ shellcheck -s dash silent_hello_patched.sh
+
+ echo "Testing in Bash if silent_hello_patched.sh get sourced without error"
+ bash --posix -eu -c ". ./silent_hello_patched.sh"
+
+ echo "Testing in Dash if silent_hello_patched.sh get sourced without error"
+ dash -eu -c ". ./silent_hello_patched.sh"
+
+ echo "Testing in Ksh if silent_hello_patched.sh get sourced without error"
+ ksh -eu -c ". $PWD/silent_hello_patched.sh"
+
+
+ # Generate the sample source "$PWD/delta:$PWD/foxtrot" "$PWD/delta:$PWD/foxtrot"
+
+ substitute ${./sample_source.sh.in} sample_source.sh --replace @sed@ ${gnused}/bin/sed
+ chmod u+rw sample_source.sh
+
+
+ # Check the sample source
+
+ echo "Running shellcheck against sample_source.sh"
+ shellcheck -s sh sample_source.sh
+ shellcheck -s dash sample_source.sh
+
+
+ # Test patching the sample source
+
+ echo "Generating sample_source_patched.sh from sample_source.sh"
+ cp sample_source.sh sample_source_patched.sh
+ chmod u+w sample_source_patched.sh
+ patchRcPathPosix sample_source_patched.sh "$PWD/delta:$PWD/foxtrot"
+
+ echo "Running shellcheck against sample_source_patched.sh"
+ shellcheck -s sh sample_source_patched.sh
+ shellcheck -s dash sample_source_patched.sh
+
+ echo "Testing in Bash if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ bash --posix ${./test-sourcing-posix} "./sample_source.sh" "./sample_source_patched.sh"
+
+ echo "Testing in Dash if sample_source.sh and sample_source_patched.sh modifies PATH the same way"
+ dash ${./test-sourcing-posix} "./sample_source.sh" "./sample_source_patched.sh"
+
+ echo "Testing in Ksh if sample_source.sh and sample_source_patched.sh modifies PATH the same way"
+ ksh ${./test-sourcing-posix} "$PWD/sample_source.sh" "$PWD/sample_source_patched.sh"
+
+
+ # Test double-patching the sample source
+
+ echo "Patching again sample_source_patched.sh"
+ patchRcPathPosix sample_source_patched.sh "$PWD/foxtrot:$PWD/golf"
+
+ echo "Running shellcheck against sample_source_patched.sh"
+ shellcheck -s sh sample_source_patched.sh
+ shellcheck -s dash sample_source_patched.sh
+
+ echo "Testing in Bash if sample_source.bash and sample_source_patched.bash modifies PATH the same way"
+ bash --posix ${./test-sourcing-posix} "./sample_source.sh" "./sample_source_patched.sh"
+
+ echo "Testing in Dash if sample_source.sh and sample_source_patched.sh modifies PATH the same way"
+ dash ${./test-sourcing-posix} "./sample_source.sh" "./sample_source_patched.sh"
+
+ echo "Testing in Ksh if sample_source.sh and sample_source_patched.sh modifies PATH the same way"
+ ksh ${./test-sourcing-posix} "$PWD/sample_source.sh" "$PWD/sample_source_patched.sh"
+
+
+ # Create a dummy output
+ touch "$out"
+ ''
+ )
+ { };
+}
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.bash b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.bash
new file mode 100644
index 000000000000..6cb043e4e70c
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.bash
@@ -0,0 +1,2 @@
+PATH="$PWD/charlie:${PATH/:$PWD\/bravo}"
+export PATH
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.csh.in b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.csh.in
new file mode 100644
index 000000000000..9606458c037e
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.csh.in
@@ -0,0 +1 @@
+setenv PATH $PWD/charlie:`echo "$PATH" | @sed@ "s#:$PWD/bravo##"`
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.fish b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.fish
new file mode 100644
index 000000000000..f638fe5e24d1
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.fish
@@ -0,0 +1,9 @@
+begin
+ for p in $PATH
+ if test $p != "$PWD/bravo"
+ set TEMPORARY_PATH $TEMPORARY_PATH $p
+ end
+ end
+ set -g PATH $TEMPORARY_PATH
+end
+set PATH "$PWD/charlie" $PATH
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.sh.in b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.sh.in
new file mode 100644
index 000000000000..42e64a1ffc08
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/sample_source.sh.in
@@ -0,0 +1,2 @@
+PATH="$PWD/charlie:$(echo "$PATH" | @sed@ "s#:$PWD/bravo##")"
+export PATH
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-bash b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-bash
new file mode 100644
index 000000000000..1b6cc54d8f93
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-bash
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+set -eu -o pipefail
+
+UNPATCHED_SOURCE_FILE="$1"
+PATCHED_SOURCE_FILE="$2"
+ORIG_PATH="$PWD/alfa:$PWD/bravo"
+RESULT_PATH_FROM_UNPATCHED="$(
+ PATH="$ORIG_PATH"; export PATH
+ . "$UNPATCHED_SOURCE_FILE"
+ echo "$PATH"
+)"
+RESULT_PATH_FROM_PATCHED="$(
+ PATH="$ORIG_PATH"; export PATH
+ . "$PATCHED_SOURCE_FILE"
+ echo "$PATH"
+)"
+if [[ "$RESULT_PATH_FROM_UNPATCHED" != "$RESULT_PATH_FROM_PATCHED" ]]; then
+ echo "Result path mismatched: $UNPATCHED_SOURCE_FILE ($RESULT_PATH_FROM_UNPATCHED) and $PATCHED_SOURCE_FILE ($RESULT_PATH_FROM_PATCHED)" >&2
+ exit 1
+fi
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-csh b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-csh
new file mode 100644
index 000000000000..7ddb2ddc1bdc
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-csh
@@ -0,0 +1,13 @@
+#/usr/bin/env tcsh
+
+set UNPATCHED_SOURCE_FILE = "$1"
+set PATCHED_SOURCE_FILE = "$2"
+set ORIG_PATH = "${PWD}/alfa:${PWD}/bravo"
+
+set RESULT_PATH_FROM_UNPATCHED = `setenv PATH "$ORIG_PATH"; source $UNPATCHED_SOURCE_FILE; echo $PATH`
+set RESULT_PATH_FROM_PATCHED = `setenv PATH "$ORIG_PATH"; source $PATCHED_SOURCE_FILE; echo $PATH`
+
+if ($RESULT_PATH_FROM_UNPATCHED != $RESULT_PATH_FROM_PATCHED) then
+ echo "Result path mismatched: $UNPATCHED_SOURCE_FILE ($RESULT_PATH_FROM_UNPATCHED) and $PATCHED_SOURCE_FILE ($RESULT_PATH_FROM_PATCHED)" > /dev/stderr
+ exit 1
+endif
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-fish b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-fish
new file mode 100644
index 000000000000..fcce014331e5
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-fish
@@ -0,0 +1,13 @@
+#/usr/bin/env fish
+
+set UNPATCHED_SOURCE_FILE $argv[1]
+set PATCHED_SOURCE_FILE $argv[2]
+set ORIG_PATH "$PWD/alfa:$PWD/bravo"
+
+set RESULT_PATH_FROM_UNPATCHED (fish -c "set -g PATH \"$ORIG_PATH\"; source $UNPATCHED_SOURCE_FILE; echo \"\$PATH\"")
+set RESULT_PATH_FROM_PATCHED (fish -c "set -g PATH \"$ORIG_PATH\"; source $PATCHED_SOURCE_FILE; echo \"\$PATH\"")
+
+if test "$RESULT_PATH_FROM_UNPATCHED" != "$RESULT_PATH_FROM_PATCHED"
+ echo "Result path mismatched: $UNPATCHED_SOURCE_FILE ($RESULT_PATH_FROM_UNPATCHED) and $PATCHED_SOURCE_FILE ($RESULT_PATH_FROM_PATCHED)" >&2
+ exit 1
+end
diff --git a/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-posix b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-posix
new file mode 100644
index 000000000000..6039b4dcf097
--- /dev/null
+++ b/pkgs/build-support/setup-hooks/patch-rc-path-hooks/test/test-sourcing-posix
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+set -eu
+
+UNPATCHED_SOURCE_FILE="$1"
+PATCHED_SOURCE_FILE="$2"
+ORIG_PATH="$PWD/alfa:$PWD/bravo"
+RESULT_PATH_FROM_UNPATCHED="$(
+ PATH="$ORIG_PATH"; export PATH
+ . "$UNPATCHED_SOURCE_FILE"
+ echo "$PATH"
+)"
+RESULT_PATH_FROM_PATCHED="$(
+ PATH="$ORIG_PATH"; export PATH
+ . "$PATCHED_SOURCE_FILE"
+ echo "$PATH"
+)"
+if [ "$RESULT_PATH_FROM_UNPATCHED" != "$RESULT_PATH_FROM_PATCHED" ]; then
+ echo "Result path mismatched: $UNPATCHED_SOURCE_FILE ($RESULT_PATH_FROM_UNPATCHED) and $PATCHED_SOURCE_FILE ($RESULT_PATH_FROM_PATCHED)" > /dev/stderr
+ exit 1
+fi
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index cdf564bd7644..22bace945227 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -983,6 +983,10 @@ with pkgs;
octant-desktop = callPackage ../applications/networking/cluster/octant/desktop.nix { };
starboard-octant-plugin = callPackage ../applications/networking/cluster/octant/plugins/starboard-octant-plugin.nix { };
+ inherit (
+ callPackages ../build-support/setup-hooks/patch-rc-path-hooks { }
+ ) patchRcPathBash patchRcPathCsh patchRcPathFish patchRcPathPosix;
+
pathsFromGraph = ../build-support/kernel/paths-from-graph.pl;
pruneLibtoolFiles = makeSetupHook { name = "prune-libtool-files"; }