summaryrefslogtreecommitdiffstats
path: root/pkgs/shell
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2024-05-08 17:03:57 +0200
committertv <tv@krebsco.de>2024-05-08 17:03:57 +0200
commitf5cf6c74f046647240a3a7070c4e6cf28d24e849 (patch)
tree724f8c61b7454ca0f73533ce5d3027212c84b7a0 /pkgs/shell
parent6d880ce26da5abc2982a610c41e27c559c0c5aac (diff)
pkgs: add overlay for shell scripts
Diffstat (limited to 'pkgs/shell')
-rw-r--r--pkgs/shell/default.nix76
1 files changed, 76 insertions, 0 deletions
diff --git a/pkgs/shell/default.nix b/pkgs/shell/default.nix
new file mode 100644
index 0000000..119b23e
--- /dev/null
+++ b/pkgs/shell/default.nix
@@ -0,0 +1,76 @@
+self: super:
+
+let
+ inherit (super) lib mylib;
+
+ # Takes a path to a shellscript an builds it into a package.
+ #
+ # The shellscript may contain instructions for modifying the PATH variable:
+ # #!buildShellBin path=PKGS resets the PATH variable to the specified packages
+ # #!buildShellBin append-path=PKGS append the packages to the PATH variable
+ # #!buildShellBin prepend-path=PKGS prepend the packages to the PATH variable
+ # where
+ # PKGS is a non-empty, colon-delimited list of package attribute names, e.g. coreutils:binutils:which
+ buildShellBin = name: path:
+ assert mylib.types.filename.check name;
+ assert isShellScript path;
+ self.callPackage
+ ({ pkgs }: pkgs.runCommand "${name}" {} /* sh */ ''
+ mkdir -p $out/bin
+ touch $out/bin/${name}
+ chmod +x $out/bin/${name}
+ exec > $out/bin/${name}
+ echo '#! ${pkgs.dash}/bin/dash'
+ echo ${mylib.shell.escape (lib.concatStringsSep "\n" (pathMods pkgs path))}
+ cat ${path}
+ '') {};
+
+ # Determine if a line is an instruction to modify the PATH variable.
+ readPathMod = pkgs: path: line: string: let
+ match = builtins.match "^#! *buildShellBin ((prepend-|append-)?path)=(.*)" string;
+ in
+ lib.optionalAttrs (match != null) {
+ inherit path line;
+ method = builtins.elemAt match 0;
+ packages = map (name: pkgs.${name}) (lib.splitString ":" (builtins.elemAt match 2));
+ };
+
+ # Generate shell assignment to modify the PATH variable.
+ applyPathMod = { method, packages, ... }: {
+ path = /* sh */ "PATH=${lib.makeBinPath packages}";
+ append-path = /* sh */ "PATH=\${PATH+$PATH:}${lib.makeBinPath packages}";
+ prepend-path = /* sh */ "PATH=${lib.makeBinPath packages}\${PATH+:$PATH}";
+ }.${method};
+
+ applyPathMods = mods:
+ assert checkPathMods mods;
+ map applyPathMod mods;
+
+ # Check if a list of list of PATH modifications for sanity.
+ checkPathMods = mods: let
+ index = lib.lists.findFirstIndex (mod: mod.method == "path") 0 mods;
+ mod = lib.elemAt mods index;
+ in
+ if index > 0 then
+ builtins.trace "warning: resetting PATH in ${toString mod.path}:${toString mod.line}" true
+ else
+ true;
+
+ pathMods = pkgs: path:
+ applyPathMods
+ (builtins.filter
+ (x: x != {})
+ (lib.imap1
+ (readPathMod pkgs path)
+ (lib.splitString "\n" (builtins.readFile path))));
+
+ isShellScript = path:
+ mylib.test "#! */bin/sh" (builtins.head (lib.splitString "\n" (builtins.readFile path)));
+
+in
+
+lib.mapAttrs
+ (name: _: buildShellBin name (./. + "/${name}"))
+ (lib.filterAttrs
+ (name: _: name != "default.nix")
+ (builtins.readDir ./.))