From 0363cb2986f02a579935f47ca2fcaf6ccbe9e90b Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Tue, 19 Nov 2024 10:44:19 +0100 Subject: [PATCH] writableDirWrapper: init --- .../by-name/wr/writableDirWrapper/package.nix | 178 ++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 pkgs/by-name/wr/writableDirWrapper/package.nix diff --git a/pkgs/by-name/wr/writableDirWrapper/package.nix b/pkgs/by-name/wr/writableDirWrapper/package.nix new file mode 100644 index 0000000000000..99eb0ec4894ee --- /dev/null +++ b/pkgs/by-name/wr/writableDirWrapper/package.nix @@ -0,0 +1,178 @@ +{ + lib, + writeShellApplication, + xorg, +}: +/** + A wrapper for programs that require a writable directory to function, typically + games or proprietary software that expect read-only resources to be placed alongside + config files and state files (e.g. logs). + + # Inputs + + *`options`* (Attribute set) + + : Set of options for the wrapper. + + `name` (String) + + : File name of the wrapper. + + `version` (String, _optional_) + + : Version of the underlying program. When set, version checks will be enabled and existing links will be recreated if they belong to a different version. + + `path` (String) + + : Path of the directory that would hold the linked data. + + `links` (List of attrsets, _optional_) + + : List of links from a source directory to a directory relative to the output path. + + `src` (String) + + : Source directory of the link, usually from the Nix store. + + `dst` (String, _optional_) + + : Destination directory of the link. + + : _Default:_ the output path. + + `runtimeInputs` (List of strings or derivations, _optional_) + + : Paths containing executables that should be added to the script's `$PATH` variable, making them usable inside the script at runtime. + + `runtimeEnv` (Attribute sets of strings, _optional_) + + : Environment variables to be set at runtime. + + `postLink` (String, _optional_) + + : Command to run after (re-)linking. Since links are first created in a temporary directory, `postLink` can be used to remove or add files that will be copied to the output path (for example, when some files need to be excluded from the linked result). + + `exec` (String) + + : Command to run. + + # Type + + ``` + writableDirWrapper :: + { + name :: String; + version? :: String; + path :: String; + links? :: [{ src :: String; dst? :: String }]; + runtimeInputs? :: [String|Derivation]; + runtimeEnv? :: { ... :: String }; + postLink? :: String; + exec :: String + } -> Derivation + ``` + + # Examples + :::{.example} + ## `pkgs.writableDirWrapper` usage example + + ```nix + { + writableDirWrapper, + hello, + }: + writableDirWrapper { + name = "writable-hello"; + links = [ + { + src = hello; + dst = "hello"; + } + ]; + postLink = '' + echo "Hello world!" | base64 > hello.txt + ''; + exec = '' + ./hello/bin/hello --greeting=$(<"hello.txt") + ''; + } + ``` + + When built and run, this should output: + ```console + SGVsbG8gd29ybGQhCg== + ``` + ::: +*/ +{ + name, + version ? null, + path, + links ? [ ], + runtimeInputs ? [ ], + runtimeEnv ? { }, + postLink ? "", + exec, +}: +let + unlinkCommand = link: ''find "${path}/${link.dst or ""}" -type l -lname "${link.src}" -delete''; + linkCommand = link: ''lndir -silent ${link.src} "./${link.dst or ""}"''; +in +writeShellApplication { + inherit name runtimeEnv; + + runtimeInputs = runtimeInputs ++ [ xorg.lndir ]; + + text = + '' + relink=false + + for arg in "$@"; do + case "$arg" in + --relink-files) relink=true ;; + # Passthrough all other args + *) args+=("$arg") ;; + esac + done + + versionFile="${path}/.${name}.version" + + if [[ "$relink" = "true" ]]; then + echo "Manually relinking files" + elif [[ ! -d "${path}" ]]; then + echo "Directory ${path} not found. Linking files..." + relink=true + '' + + lib.optionalString (version != null) '' + elif [[ ! -e "$versionFile" ]]; then + echo "Version file not found. Relinking files just to be safe..." + relink=true + elif [[ "${version}" != "$(<"$versionFile")" ]]; then + echo "Current version is not the same as the newest version (${version}). Relinking files" + relink=true + '' + + '' + fi + + if [[ "$relink" = "true" ]]; then + mkdir -p "${path}" + if [[ -d "${path}" ]]; then + echo "Removing existing file links" + ${lib.concatMapStringsSep "\n" unlinkCommand links} + fi + + echo "Linking files" + ${lib.optionalString (version != null) ''echo "${version}" > "$versionFile"''} + + # In case that postLink needs to remove some links to avoid conflict, + # we first link everything into a temporary directory then move over + pushd "$(mktemp -d)" + ${lib.concatMapStringsSep "\n" linkCommand links} + ${postLink} + cp -r ./* -t "${path}" + popd + fi + + ${exec} "''${args[@]}" + ''; +}