{ pkgs, lib, config, ... }: let inherit (lib) mkOption mkPackageOption types; strNotEmpty = s: lib.stringLength s != 0; wrapperType = lib.types.submodule { options = { package = mkPackageOption "wrapped" { } { nullable = false; }; executable = mkOption { type = types.str; default = ""; }; args = mkOption { type = types.listOf types.str; default = [ ]; }; env = mkOption { type = types.attrsOf (types.listOf types.str); default = { }; }; retainEnv = mkOption { type = types.bool; default = false; }; addPwd = mkOption { type = types.bool; default = false; }; }; }; in { options = { wrappers = mkOption { type = types.attrsOf wrapperType; }; wrapperPkg = mkPackageOption "wrapper" { } { nullable = false; }; }; config = let wrap = name: opts: let envs = lib.concatStringsSep " " ( lib.mapAttrsToList (n: v: "${n}=${lib.concatStringsSep ":" v}") opts.env ); sandboxArgs = pkgs.stdenvNoCC.mkDerivation { name = "${name}-opts"; __structuredAttrs = true; exportReferencesGraph.closure = [ opts.package ]; preferLocalBuild = true; nativeBuildInputs = [ pkgs.coreutils pkgs.jq ]; buildCommand = '' echo -n "--fs rx=" > $out jq -r '.closure[].path' < "$NIX_ATTRS_JSON_FILE" \ | tr '\n' ':' | sed 's/:$//' >> $out ${if (strNotEmpty envs) then "echo -n ' --env ${envs}' >> $out" else ""} ''; }; command = lib.getExe' opts.package ( if (strNotEmpty opts.executable) then opts.executable else opts.package.pname ); wrappedArgs = lib.concatStringsSep " " opts.args; script = '' #! /usr/bin/env bash ${lib.getExe' config.wrapperPkg "yoke"} \ ${if opts.addPwd then "--fs rwx=$PWD" else ""} \ ${if opts.retainEnv then "--retain-env" else ""} \ --fd-args -- \ ${command} \ ${wrappedArgs} $@ \ 3< ${sandboxArgs} ''; in pkgs.writeScriptBin "${name}" script; in { environment.systemPackages = lib.mapAttrsToList wrap config.wrappers; }; }