From d6b1a1a80e0683c6ce434a1278057a0f0077350f Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Thu, 19 Jun 2025 12:24:51 +0200 Subject: [PATCH 1/8] v2 API --- .github/workflows/doc.yaml | 12 +- default.nix | 45 +++-- docs/api.md | 7 +- flake.lock | 22 +-- flake.nix | 94 +---------- modules/base.nix | 337 ------------------------------------- modules/build.nix | 43 ----- modules/common-wrapper.nix | 82 +++++++++ modules/default.nix | 8 - modules/env-type.nix | 60 ++++++- modules/many-wrappers.nix | 29 ++++ modules/wrapper-impl.nix | 183 ++++++++++++++++++++ modules/wrapper.nix | 58 +++++++ tests/gitconfig | 2 - tests/multi.nix | 64 +++++++ tests/standalone.nix | 12 ++ tests/test-module.nix | 85 ---------- 17 files changed, 529 insertions(+), 614 deletions(-) delete mode 100644 modules/base.nix delete mode 100644 modules/build.nix create mode 100644 modules/common-wrapper.nix delete mode 100644 modules/default.nix create mode 100644 modules/many-wrappers.nix create mode 100644 modules/wrapper-impl.nix create mode 100644 modules/wrapper.nix delete mode 100644 tests/gitconfig create mode 100644 tests/multi.nix create mode 100644 tests/standalone.nix delete mode 100644 tests/test-module.nix diff --git a/.github/workflows/doc.yaml b/.github/workflows/doc.yaml index 66debfe..29ff7df 100644 --- a/.github/workflows/doc.yaml +++ b/.github/workflows/doc.yaml @@ -7,6 +7,10 @@ on: paths: - 'docs/**' - 'modules/**' + pull_request: + paths: + - 'docs/**' + - 'modules/**' workflow_dispatch: permissions: @@ -27,10 +31,10 @@ jobs: with: github_access_token: ${{ secrets.GITHUB_TOKEN }} extra_nix_config: | - extra-experimental-features = nix-command flakes pipe-operators + extra-experimental-features = nix-command flakes - name: 🏗️ Build - run: nix build github:${{ github.repository }}#docs -L + run: nix build github:${{ github.repository }}/${{ github.sha }}#docs -L - name: 📤 Upload artifacts id: deployment @@ -40,7 +44,7 @@ jobs: deploy: environment: - name: github-pages + name: ${{ github.event_name == 'pull_request' && format('github-pages-pr-{0}', github.event.pull_request.number) || 'github-pages' }} url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build @@ -48,3 +52,5 @@ jobs: - name: 🚀 Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 + with: + preview: ${{ github.event_name == 'pull_request' }} diff --git a/default.nix b/default.nix index e8d3720..87e9530 100644 --- a/default.nix +++ b/default.nix @@ -1,21 +1,36 @@ -{lib}: let - eval = { - pkgs, - modules ? [], - specialArgs ? {}, - }: +let + eval = + { + pkgs, + lib ? pkgs.lib, + modules ? [ ], + specialArgs ? { }, + }: lib.evalModules { - modules = - [ - ./modules - ] - ++ modules; - specialArgs = {inherit pkgs;} // specialArgs; + modules = [ + ./modules/many-wrappers.nix + ] ++ modules; + specialArgs = { + inherit pkgs; + } // specialArgs; }; -in { - lib = { +in +{ + lib = builtins.abort "wrapper-manager.lib is deprecated, please upgrade to the .v2 api: https://github.com/viperML/wrapper-manager/pull/26"; + + v2 = { inherit eval; __functor = _: eval; - build = args: (eval args).config.build.toplevel; + wrapWith = + pkgs: module: + (pkgs.lib.evalModules { + modules = [ + ./modules/wrapper.nix + module + ]; + specialArgs = { + inherit pkgs; + }; + }).config.wrapped; }; } diff --git a/docs/api.md b/docs/api.md index fd3a92a..b1f982b 100644 --- a/docs/api.md +++ b/docs/api.md @@ -12,8 +12,13 @@ import { data } from "./wm.data.js"; import { RenderDocs } from "easy-nix-documentation"; +## Main API - + + +## Program configuration + + ## Outputs diff --git a/flake.lock b/flake.lock index 41abcde..5999137 100644 --- a/flake.lock +++ b/flake.lock @@ -1,26 +1,6 @@ { "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1743964447, - "narHash": "sha256-nEo1t3Q0F+0jQ36HJfbJtiRU4OI+/0jX/iITURKe3EE=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "063dece00c5a77e4a0ea24e5e5a5bd75232806f8", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } + "root": {} }, "root": "root", "version": 7 diff --git a/flake.nix b/flake.nix index d1f4618..31d093e 100644 --- a/flake.nix +++ b/flake.nix @@ -1,95 +1,3 @@ { - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - }; - - outputs = - { - self, - nixpkgs, - }: - let - forAllSystems = - function: - nixpkgs.lib.genAttrs - [ - "x86_64-linux" - "aarch64-linux" - ] - ( - system: - function ( - import nixpkgs { - inherit system; - config.allowUnfree = true; - } - ) - ); - in - (import ./default.nix { - inherit (nixpkgs) lib; - }) - // { - formatter = forAllSystems (pkgs: pkgs.alejandra); - - devShells = forAllSystems (pkgs: { - default = pkgs.mkShell { - packages = [ - pkgs.nodejs - ]; - }; - }); - - packages = forAllSystems (pkgs: { - optionsJSON = - (pkgs.nixosOptionsDoc { - options = - (self.lib { - inherit pkgs; - modules = [ ]; - }).options; - }).optionsJSON; - - docs = - with pkgs; - buildNpmPackage { - name = "docs"; - src = ./docs; - npmDeps = importNpmLock { - npmRoot = ./docs; - }; - - inherit (importNpmLock) npmConfigHook; - env.WRAPPER_MANAGER_OPTIONS_JSON = self.packages.${pkgs.system}.optionsJSON; - - buildPhase = '' - runHook preBuild - - # Vitepress hangs when printing normally - npm run build -- --base=/wrapper-manager/ 2>&1 | cat - - runHook postBuild - ''; - - installPhase = '' - runHook preInstall - - mv .vitepress/dist $out - - runHook postInstall - ''; - }; - }); - - checks = forAllSystems ( - pkgs: - (self.lib { - inherit pkgs; - modules = [ ./tests/test-module.nix ]; - specialArgs = { - some-special-arg = "foo"; - }; - }).config.build.packages - ); - }; + outputs = _: import ./.; } diff --git a/modules/base.nix b/modules/base.nix deleted file mode 100644 index c442110..0000000 --- a/modules/base.nix +++ /dev/null @@ -1,337 +0,0 @@ -{ - lib, - pkgs, - ... -}: -let - inherit (lib) - mkOption - types - ; - - envToWrapperArg = - _: - { - name, - force, - value, - }: - let - unsetArg = - if !force then - (lib.warn '' - ${ - lib.showOption [ - "env" - name - "value" - ] - } is null (indicating unsetting the variable), but ${ - lib.showOption [ - "env" - name - "force" - ] - } is false. This option will have no effect - '' [ ]) - else - [ - "--unset" - name - ]; - setArg = - let - arg = if force then "--set" else "--set-default"; - in - [ - arg - name - value - ]; - in - if value == null then unsetArg else setArg; - - wrapperOpts = - { config, ... }: - { - imports = [ - (lib.mkAliasOptionModuleMD [ "flags" ] [ "prependFlags" ]) - ]; - - options = { - basePackage = mkOption { - type = with types; package; - description = '' - Program to be wrapped. - ''; - example = lib.literalExpression "pkgs.nix"; - }; - - extraPackages = mkOption { - type = with types; listOf package; - description = '' - Extra packages to also wrap. - ''; - example = lib.literalExpression "[ pkgs.git-extras pkgs.delta ]"; - default = [ ]; - }; - - env = mkOption { - # This is a hack to display a helpful error message to the user about the changed api. - # Should be changed to just `attrsOf submodule` at some point. - type = - let - inherit (lib) any isStringLike showOption; - actualType = types.submodule ./env-type.nix; - forgedType = actualType // { - # There's special handling if this value is present which makes merging treat this type as any other submodule type, - # so we lie about there being no sub-modules so that our `check` and `merge` get called. - getSubModules = null; - check = v: isStringLike v || actualType.check v; - merge = - loc: defs: - if any (def: isStringLike def.value) defs then - throw '' - ${showOption loc} has been changed to an attribute set. - Instead of assigning value directly, use ${showOption (loc ++ [ "value" ])} = ; - '' - else - (actualType.merge loc defs); - }; - in - types.attrsOf forgedType; - description = '' - Structured environment variables. - ''; - default = { }; - example = { - NIX_CONFIG.value = "allow-import-from-derivation = false"; - }; - }; - - prependFlags = mkOption { - type = with types; listOf (coercedTo anything (x: "${x}") str); - description = '' - Prepend a flag to the invocation of the program, **before** any arguments passed to the wrapped executable. - ''; - default = [ ]; - example = lib.literalExpression '' - [ - "--config" ./config.sh - "--ascii" ./ascii - ] - ''; - }; - - appendFlags = mkOption { - type = with types; listOf (coercedTo anything (x: "${x}") str); - description = '' - Append a flag to the invocation of the program, **after** any arguments passed to the wrapped executable. - ''; - default = [ ]; - example = lib.literalExpression '' - [ - "--config" ./config.sh - "--ascii" ./ascii - ] - ''; - }; - - pathAdd = mkOption { - type = with types; listOf package; - description = '' - Packages to append to PATH. - ''; - default = [ ]; - example = lib.literalExpression "[ pkgs.starship ]"; - }; - - extraWrapperFlags = mkOption { - type = with types; separatedString " "; - description = '' - Raw flags passed to makeWrapper. - - See upstream documentation for make-wrapper.sh : https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/setup-hooks/make-wrapper.sh - ''; - default = ""; - example = "--argv0 foo --set BAR value"; - }; - - wrapped = mkOption { - type = with types; package; - readOnly = true; - description = '' - (Output) Final wrapped package. - ''; - }; - - renames = mkOption { - type = with types; attrsOf str; - description = '' - Map of renames FROM = TO. Renames every binary /bin/FROM to /bin/TO, adjusting other - necessary files. - ''; - default = { }; - example = { - "nvim" = "custom-nvim"; - }; - }; - - overrideAttrs = mkOption { - type = with types; functionTo attrs; - description = '' - Function to override attributes from the final package. - ''; - default = lib.id; - example = '' - old: { - pname = "''${old.pname}-wrapped"; - } - ''; - }; - - postBuild = mkOption { - type = with types; str; - description = '' - Extra fragment of bash to be run after the main wrapper-manager code. - ''; - default = ""; - example = '' - $out/bin/nvim -l ''${./check.lua} - ''; - }; - }; - - config = { - wrapped = - let - mkWrapper = - basePackage: - let - hasMan = builtins.any (builtins.hasAttr "man") ([ basePackage ] ++ config.extraPackages); - in - ( - ( - (pkgs.symlinkJoin { - inherit (basePackage) name; - paths = [ basePackage ] ++ config.extraPackages; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = - let - envArgs = lib.mapAttrsToList envToWrapperArg config.env; - # Yes, the arguments are escaped later, yes, this is intended to "double escape", - # so that they are escaped for wrapProgram and for the final binary too. - prependFlagArgs = map (args: [ - "--add-flags" - (lib.escapeShellArg args) - ]) config.prependFlags; - appendFlagArgs = map (args: [ - "--append-flags" - (lib.escapeShellArg args) - ]) config.appendFlags; - pathArgs = map (p: [ - "--prefix" - "PATH" - ":" - "${p}/bin" - ]) config.pathAdd; - allArgs = lib.flatten (envArgs ++ prependFlagArgs ++ appendFlagArgs ++ pathArgs); - in - '' - for file in $out/bin/*; do - echo "Wrapping $file" - wrapProgram \ - $file \ - ${lib.escapeShellArgs allArgs} \ - ${config.extraWrapperFlags} - done - - # Some derivations have nested symlinks here - if [[ -d $out/share/applications && ! -w $out/share/applications ]]; then - echo "Detected nested symlink, fixing" - temp=$(mktemp -d) - cp -v $out/share/applications/* $temp - rm -vf $out/share/applications - mkdir -pv $out/share/applications - cp -v $temp/* $out/share/applications - fi - - cd $out/bin - for exe in *; do - - if false; then - exit 2 - ${lib.concatStringsSep "\n" ( - lib.mapAttrsToList (name: value: '' - elif [[ $exe == ${lib.escapeShellArg name} ]]; then - newexe=${lib.escapeShellArg value} - mv -vf $exe $newexe - '') config.renames - )} - else - newexe=$exe - fi - - # Fix .desktop files - # This list of fixes might not be exhaustive - for file in $out/share/applications/*; do - echo "Fixing file=$file for exe=$exe" - set -x - trap "set +x" ERR - sed -i "s#/nix/store/.*/bin/$exe #$out/bin/$newexe #" "$file" - sed -i -E "s#Exec=$exe([[:space:]]*)#Exec=$out/bin/$newexe\1#g" "$file" - sed -i -E "s#TryExec=$exe([[:space:]]*)#TryExec=$out/bin/$newexe\1#g" "$file" - set +x - done - done - - ${lib.optionalString hasMan '' - mkdir -p ''${!outputMan} - ${lib.concatMapStringsSep "\n" ( - # p: if lib.hasAttr "man" p then "${pkgs.xorg.lndir}/bin/lndir -silent ${p.man} $out" else "#" - p: - if p ? "man" then - "${lib.getExe pkgs.xorg.lndir} -silent ${p.man} \${!outputMan}" - else - "echo \"No man output for ${lib.getName p}\"" - ) ([ basePackage ] ++ config.extraPackages)} - ''} - - ${config.postBuild} - ''; - passthru = (basePackage.passthru or { }) // { - unwrapped = basePackage; - }; - outputs = [ - "out" - ] ++ (lib.optional hasMan "man"); - meta = basePackage.meta // { - outputsToInstall = [ - "out" - ] ++ (lib.optional hasMan "man"); - }; - }) - // { - override = newAttrs: mkWrapper (basePackage.override newAttrs); - } - ) - ); - in - mkWrapper config.basePackage; - }; - }; -in -{ - options = { - wrappers = mkOption { - type = with types; attrsOf (submodule wrapperOpts); - default = { }; - description = '' - Wrapper configuration. See the suboptions for configuration. - ''; - }; - }; - - config = { - }; -} diff --git a/modules/build.nix b/modules/build.nix deleted file mode 100644 index e79fa12..0000000 --- a/modules/build.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ - lib, - pkgs, - config, - ... -}: let - inherit - (lib) - mkOption - types - ; -in { - options = { - build = { - toplevel = mkOption { - type = with types; package; - readOnly = true; - description = '' - (Output) Derivation that merges all the wrappers into a single package. - ''; - }; - - packages = mkOption { - type = with types; attrsOf package; - readOnly = true; - description = '' - (Output) Attribute set of name=pkg. Useful for adding them to a flake's packages output. - ''; - }; - }; - }; - - config = { - build = { - toplevel = pkgs.buildEnv { - name = "wrapper-manager"; - paths = builtins.attrValues config.build.packages; - }; - - packages = builtins.mapAttrs (_: value: value.wrapped) config.wrappers; - }; - }; -} diff --git a/modules/common-wrapper.nix b/modules/common-wrapper.nix new file mode 100644 index 0000000..8df9f36 --- /dev/null +++ b/modules/common-wrapper.nix @@ -0,0 +1,82 @@ +{ + lib, + config, + ... +}: +let + inherit (lib) mkOption types flatten; + inherit (builtins) attrValues; + flagsType = with types; listOf (coercedTo anything (x: "${x}") str); +in +{ + options = { + wrapFlags = mkOption { + type = flagsType; + default = [ ]; + description = "Flags passed to makeWrapper."; + }; + appendFlags = mkOption { + type = flagsType; + default = [ ]; + description = "Flags passed after any arguments to the wrapped program."; + }; + prependFlags = mkOption { + type = flagsType; + default = [ ]; + description = "Flags passed before any arguments to the wrapped program."; + }; + env = mkOption { + type = with types; attrsOf (submodule ./env-type.nix); + default = { }; + description = "FIXME env"; + }; + extraWrapperFlags = mkOption { + type = with types; separatedString " "; + description = '' + Raw flags passed to makeWrapper. + + See upstream documentation for make-wrapper.sh : https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/setup-hooks/make-wrapper.sh + ''; + default = ""; + example = "--argv0 foo --set BAR value"; + }; + pathAdd = mkOption { + type = with types; listOf package; + description = '' + Packages to append to PATH. + ''; + default = [ ]; + example = lib.literalExpression "[ pkgs.starship ]"; + }; + wrapperType = mkOption { + type = types.enum [ + "shell" + "binary" + ]; + default = "binary"; + }; + }; + + config = { + wrapFlags = + (flatten ( + map (f: [ + "--add-flag" + f + ]) config.prependFlags + )) + ++ (flatten ( + map (f: [ + "--append-flag" + f + ]) config.appendFlags + )) + ++ (lib.optionals (config.pathAdd != [ ]) [ + "--prefix" + "PATH" + ":" + (lib.makeBinPath config.pathAdd) + ]) + ++ (flatten (map (e: e.asFlags) (attrValues config.env))); + }; +} diff --git a/modules/default.nix b/modules/default.nix deleted file mode 100644 index c58d5a6..0000000 --- a/modules/default.nix +++ /dev/null @@ -1,8 +0,0 @@ -{ - imports = [ - ./base.nix - ./build.nix - - # ./git.nix - ]; -} diff --git a/modules/env-type.nix b/modules/env-type.nix index 81eeaca..4d410ae 100644 --- a/modules/env-type.nix +++ b/modules/env-type.nix @@ -3,9 +3,11 @@ lib, name, ... -}: let +}: +let inherit (lib) mkOption types; -in { +in +{ options = { name = mkOption { type = types.str; @@ -16,10 +18,16 @@ in { }; value = mkOption { - type = let - inherit (types) coercedTo anything str nullOr; - strLike = coercedTo anything (x: "${x}") str; - in + type = + let + inherit (types) + coercedTo + anything + str + nullOr + ; + strLike = coercedTo anything (x: "${x}") str; + in nullOr strLike; description = '' Value of the variable to be set. @@ -43,5 +51,45 @@ in { default = config.value == null; defaultText = lib.literalMD "true if `value` is null, otherwise false"; }; + + asFlags = mkOption { + type = with types; listOf str; + internal = true; + readOnly = true; + }; + }; + + config = { + asFlags = + let + unsetArgs = + if !config.force then + (lib.warn '' + ${ + lib.showOption [ + "env" + config.name + "value" + ] + } is null (indicating unsetting the variable), but ${ + lib.showOption [ + "env" + config.name + "force" + ] + } is false. This option will have no effect + '' [ ]) + else + [ + "--unset" + config.name + ]; + setArgs = [ + (if config.force then "--set" else "--set-default") + config.name + config.value + ]; + in + (if config.value == null then unsetArgs else setArgs); }; } diff --git a/modules/many-wrappers.nix b/modules/many-wrappers.nix new file mode 100644 index 0000000..9da2f0e --- /dev/null +++ b/modules/many-wrappers.nix @@ -0,0 +1,29 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + wrapperType = mkOption { + type = types.enum ["shell" "binary"]; + default = "binary"; + }; + + wrappers = mkOption { + type = types.attrsOf ( + types.submoduleWith { + modules = [ + ./wrapper.nix + { + wrapperType = lib.mkDefault config.wrapperType; + } + ]; + specialArgs = { + inherit pkgs; + }; + } + ); + description = "Wrappers to create"; + }; + }; +} diff --git a/modules/wrapper-impl.nix b/modules/wrapper-impl.nix new file mode 100644 index 0000000..df3812d --- /dev/null +++ b/modules/wrapper-impl.nix @@ -0,0 +1,183 @@ +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) mkOption types; + inherit (builtins) attrValues; + + printAndRun = cmd: '' + echo ":: ${cmd}" + eval "${cmd}" + ''; + + hasMan = builtins.any (builtins.hasAttr "man") ([ config.basePackage ] ++ config.extraPackages); +in +{ + options = { + wrapped = mkOption { + type = types.package; + readOnly = true; + description = "The wrapped package"; + }; + + overrideAttrs = mkOption { + type = with types; functionTo attrs; + description = '' + Function to override attributes from the final package. + ''; + default = lib.id; + defaultText = lib.literalExpression "lib.id"; + example = lib.literalExpression '' + old: { + pname = "''${old.pname}-wrapped"; + } + ''; + }; + + postBuild = mkOption { + type = types.str; + default = ""; + }; + }; + + config = { + wrapped = + ( + (pkgs.symlinkJoin { + # inherit (config.basePackage) name; + pname = lib.getName config.basePackage; + version = lib.getVersion config.basePackage; + __intentionallyOverridingVersion = true; + paths = [ config.basePackage ] ++ config.extraPackages; + nativeBuildInputs = [ + pkgs.makeBinaryWrapper + pkgs.makeWrapper + ]; + passthru = (config.basePackage.passthru or { }) // { + unwrapped = config.basePackage; + }; + outputs = [ + "out" + ] ++ (lib.optional hasMan "man"); + meta = (config.basePackage.meta or { }) // { + outputsToInstall = [ + "out" + ] ++ (lib.optional hasMan "man"); + }; + postBuild = '' + pushd "$out/bin" > /dev/null + + echo "::: Wrapping explicit .programs ..." + already_wrapped=() + ${lib.concatMapStringsSep "\n" ( + program: + let + name = program.name; + target = if program.target == null then "" else program.target; + wrapProgram = if program.wrapperType == "shell" then "wrapProgramShell" else "wrapProgramBinary"; + makeWrapper = if program.wrapperType == "shell" then "makeShellWrapper" else "makeBinaryWrapper"; + in + # bash + '' + already_wrapped+="${program.name}" + + # If target is empty, use makeWrapper + # If target is not empty, but the same as name, use makeWrapper + # If target is not empty, is different from name, and doesn't exist, use wrapProgram + # If target is not empty, is different from name, and exists, error out + + cmd=() + if [[ -z "${target}" ]]; then + cmd=(${wrapProgram} '${name}') + elif [[ -e "$out/bin/${name}" ]]; then + echo ":: Error: Target '${name}' already exists" + exit 1 + else + cmd=(${makeWrapper} "$out/bin/${target}" '${name}') + fi + + ${ + if program.wrapFlags == [ ] && program.extraWrapperFlags == "" then + "echo ':: (${name} skipped: no wrapper configuration)'" + else + printAndRun "\${cmd[@]} ${lib.escapeShellArgs program.wrapFlags} ${program.extraWrapperFlags}" + } + '' + ) (attrValues config.programs)} + + echo "::: Wrapping packages in out/bin ..." + + for file in "$out/bin/"*; do + # check if $file is in $already_wrapped + prog="$(basename "$file")" + if [[ " ''${already_wrapped[@]} " =~ " $prog " ]]; then + continue + fi + + ${ + if config.wrapFlags == [ ] && config.extraWrapperFlags == "" then + "echo \":: ($prog skipped: no wrapper configuration)\"" + else + printAndRun ( + let + wrapProgram = if config.wrapperType == "shell" then "wrapProgramShell" else "wrapProgramBinary"; + in + ''${wrapProgram} "$file" ${lib.escapeShellArgs config.wrapFlags} ${config.extraWrapperFlags}'' + ) + } + done + popd > /dev/null + + ## Fix desktop files + + # Some derivations have nested symlinks here + if [[ -d $out/share/applications && ! -w $out/share/applications ]]; then + echo "Detected nested symlink, fixing" + temp=$(mktemp -d) + cp -v $out/share/applications/* $temp + rm -vf $out/share/applications + mkdir -pv $out/share/applications + cp -v $temp/* $out/share/applications + fi + + pushd "$out/bin" > /dev/null + for exe in *; do + # Fix .desktop files + # This list of fixes might not be exhaustive + for file in $out/share/applications/*; do + trap "set +x" ERR + set -x + sed -i "s#/nix/store/.*/bin/$exe #$out/bin/$exe #" "$file" + sed -i -E "s#Exec=$exe([[:space:]]*)#Exec=$out/bin/$exe\1#g" "$file" + sed -i -E "s#TryExec=$exe([[:space:]]*)#TryExec=$out/bin/$exe\1#g" "$file" + set +x + done + done + popd > /dev/null + + ${lib.optionalString hasMan '' + mkdir -p ''${!outputMan} + ${lib.concatMapStringsSep "\n" ( + p: + if p ? "man" then + "${lib.getExe pkgs.xorg.lndir} -silent ${p.man} \${!outputMan}" + else + "echo \"No man output for ${lib.getName p}\"" + ) ([ config.basePackage ] ++ config.extraPackages)} + ''} + + ${config.postBuild} + ''; + }).overrideAttrs + ( + final: prev: { + name = "${final.pname}-${final.version}"; + } + ) + ).overrideAttrs + config.overrideAttrs; + }; +} diff --git a/modules/wrapper.nix b/modules/wrapper.nix new file mode 100644 index 0000000..cc386cf --- /dev/null +++ b/modules/wrapper.nix @@ -0,0 +1,58 @@ +{ pkgs, lib, config, ... }: +let + inherit (lib) mkOption types; + +in +{ + imports = [ + ./common-wrapper.nix + ./wrapper-impl.nix + ]; + + options = { + basePackage = mkOption { + type = with types; package; + description = "Program to be wrapped"; + }; + + extraPackages = mkOption { + type = with types; listOf package; + default = [ ]; + description = "Optional extra packages to also wrap"; + }; + + programs = mkOption { + default = { }; + description = "Programs to wrap"; + type = types.attrsOf ( + types.submoduleWith { + modules = [ + ./common-wrapper.nix + ( + { name, ... }: + { + options = { + name = mkOption { + type = types.str; + default = name; + description = "Name of the program"; + }; + + target = mkOption { + type = with types; nullOr str; + default = null; + description = "Target of the program"; + }; + }; + + config = { + wrapperType = lib.mkDefault config.wrapperType; + }; + } + ) + ]; + } + ); + }; + }; +} diff --git a/tests/gitconfig b/tests/gitconfig deleted file mode 100644 index df68d0a..0000000 --- a/tests/gitconfig +++ /dev/null @@ -1,2 +0,0 @@ -[core] - pager=delta \ No newline at end of file diff --git a/tests/multi.nix b/tests/multi.nix new file mode 100644 index 0000000..f799429 --- /dev/null +++ b/tests/multi.nix @@ -0,0 +1,64 @@ +let + pkgs = import { + config.allowUnfree = true; + }; + wrapper-manager = import ../.; +in +wrapper-manager.v2 { + inherit pkgs; + modules = [ + { + wrapperType = "shell"; + + wrappers.discord = { + basePackage = pkgs.discord; + + env.NIXOS_OZONE_WL.value = "1"; + }; + + wrappers.hello = { + basePackage = pkgs.hello; + postBuild = "echo goodbye"; + overrideAttrs = old: { + pname = "goodbye"; + }; + programs.hello = { + appendFlags = [ + "-g" + "Goodbye World" + ]; + }; + }; + + wrappers.zellij = { + # Checks for __intentionallyOverridingVersion + basePackage = pkgs.zellij; + }; + + wrappers.hello-bad = { + basePackage = pkgs.hello; + flags = [ + "-g" + "g" + ]; + }; + + wrappers.neofetch = { + basePackage = pkgs.neofetch.override { x11Support = false; }; + programs.guixfetch = { + target = "neofetch"; + prependFlags = [ + "--ascii_distro" + "guix" + ]; + }; + }; + + wrappers.git = { + basePackage = pkgs.git; + env.FOO.value = "BAR"; + programs.scalar = { }; + }; + } + ]; +} diff --git a/tests/standalone.nix b/tests/standalone.nix new file mode 100644 index 0000000..af7d186 --- /dev/null +++ b/tests/standalone.nix @@ -0,0 +1,12 @@ +let + pkgs = import { }; + wrapper-manager = import ../.; + wrap = wrapper-manager.v2.wrapWith pkgs; +in +wrap { + basePackage = pkgs.hello; + prependFlags = [ + "-g" + "Goodbye" + ]; +} diff --git a/tests/test-module.nix b/tests/test-module.nix deleted file mode 100644 index db77f06..0000000 --- a/tests/test-module.nix +++ /dev/null @@ -1,85 +0,0 @@ -{ - pkgs, - lib, - some-special-arg, - config, - ... -}: -{ - wrappers.hello = { - env.FOO.value = "foo"; - env.BAR.value = "bar"; - basePackage = pkgs.hello; - flags = [ - "-g" - some-special-arg - ]; - }; - - wrappers.neofetch = { - basePackage = pkgs.neofetch.override { x11Support = false; }; - flags = [ - "--ascii_distro" - "guix" - ]; - renames = { - "neofetch" = "neofetch2"; - }; - }; - - wrappers.git = { - basePackage = pkgs.git; - extraPackages = [ pkgs.git-extras ]; - env.GIT_CONFIG_GLOBAL.value = pkgs.writeText "gitconfig" (lib.fileContents ./gitconfig); - }; - - wrappers.nushell = { - basePackage = pkgs.nushell; - pathAdd = [ pkgs.starship ]; - }; - - wrappers.wezterm = { - basePackage = pkgs.wezterm; - renames = { - "wezterm" = "wezterm2"; - }; - }; - - wrappers.neovim = { - basePackage = pkgs.neovim; - renames = { - "nvim" = "nvim2"; - }; - }; - - wrappers.discord = { - basePackage = pkgs.discord; - flags = [ - "--disable-gpu" - ]; - }; - - wrappers.hello-wrapped = { - basePackage = pkgs.hello; - overrideAttrs = old: { - name = "hello-wrapped"; - pname = "hello-wrapped-bad"; - }; - }; - - wrappers.git-minimal-custom = { - basePackage = config.wrappers.git.wrapped.override { - # Same as gitMinimal - withManual = false; - osxkeychainSupport = false; - pythonSupport = false; - perlSupport = false; - withpcre2 = false; - }; - }; - - # Test for meta.outputsToInstall - wrappers.pkg-config = { - basePackage = pkgs.pkg-config; - }; -} From 1d29cc21aab142dc4092167c7056578f87ff1f1c Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Thu, 19 Jun 2025 13:11:35 +0200 Subject: [PATCH 2/8] Refactor docs --- .envrc | 1 - .github/workflows/ci.yaml | 30 -------------------- .github/workflows/doc.yaml | 56 -------------------------------------- docs/.envrc | 1 + docs/package.nix | 46 +++++++++++++++++++++++++++++++ docs/readme.md | 13 +++++---- docs/shell.nix | 10 +++++++ docs/wm.data.js | 23 +++++++--------- flake.nix | 9 +++++- 9 files changed, 83 insertions(+), 106 deletions(-) delete mode 100644 .envrc delete mode 100644 .github/workflows/ci.yaml delete mode 100644 .github/workflows/doc.yaml create mode 100644 docs/.envrc create mode 100644 docs/package.nix create mode 100644 docs/shell.nix diff --git a/.envrc b/.envrc deleted file mode 100644 index 3550a30..0000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 01fd49d..0000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,30 +0,0 @@ -name: ci - -on: - push: - pull_request: - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - - permissions: - pages: write - id-token: write - - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@main - - - name: Install Magic Nix Cache - uses: DeterminateSystems/magic-nix-cache-action@main - - - name: Check - run: nix flake check -L - - - name: Show - run: nix flake show diff --git a/.github/workflows/doc.yaml b/.github/workflows/doc.yaml deleted file mode 100644 index 29ff7df..0000000 --- a/.github/workflows/doc.yaml +++ /dev/null @@ -1,56 +0,0 @@ -name: doc - -on: - push: - branches: - - master - paths: - - 'docs/**' - - 'modules/**' - pull_request: - paths: - - 'docs/**' - - 'modules/**' - workflow_dispatch: - -permissions: - id-token: write - pages: write - contents: write - -jobs: - build: - runs-on: ubuntu-latest - - environment: - name: github-pages - - steps: - - name: 📦 Install Nix - uses: cachix/install-nix-action@master - with: - github_access_token: ${{ secrets.GITHUB_TOKEN }} - extra_nix_config: | - extra-experimental-features = nix-command flakes - - - name: 🏗️ Build - run: nix build github:${{ github.repository }}/${{ github.sha }}#docs -L - - - name: 📤 Upload artifacts - id: deployment - uses: actions/upload-pages-artifact@v3 - with: - path: ./result - - deploy: - environment: - name: ${{ github.event_name == 'pull_request' && format('github-pages-pr-{0}', github.event.pull_request.number) || 'github-pages' }} - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: 🚀 Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 - with: - preview: ${{ github.event_name == 'pull_request' }} diff --git a/docs/.envrc b/docs/.envrc new file mode 100644 index 0000000..65326bb --- /dev/null +++ b/docs/.envrc @@ -0,0 +1 @@ +use nix \ No newline at end of file diff --git a/docs/package.nix b/docs/package.nix new file mode 100644 index 0000000..2c305d0 --- /dev/null +++ b/docs/package.nix @@ -0,0 +1,46 @@ +{ + buildNpmPackage, + importNpmLock, + nixosOptionsDoc, + lib, + pkgs, +}: +let + options_json = + (nixosOptionsDoc { + options = + ((import ../.).v2 { + inherit pkgs; + modules = [ ]; + }).options; + }).optionsJSON; +in +buildNpmPackage { + name = "wrapper-manager-docs"; + src = lib.cleanSource ./.; + npmDeps = importNpmLock { + npmRoot = lib.cleanSource ./.; + }; + + inherit (importNpmLock) npmConfigHook; + + env.WRAPPER_MANAGER_OPTIONS_JSON = options_json; + passthru = {inherit options_json;}; + + buildPhase = '' + runHook preBuild + + # Vitepress hangs when printing normally + npm run build -- --base=/wrapper-manager/ 2>&1 | cat + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mv .vitepress/dist $out + + runHook postInstall + ''; +} diff --git a/docs/readme.md b/docs/readme.md index 2b6f5d2..492cab7 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -14,11 +14,6 @@ wrapped nixos logo

- ```nix {pkgs, ...}: { @@ -228,3 +223,11 @@ https://github.com/viperML/wrapper-manager/issues - 2023-08-12 - Added wrappers.name.renames option. + +
+ +
\ No newline at end of file diff --git a/docs/shell.nix b/docs/shell.nix new file mode 100644 index 0000000..b754185 --- /dev/null +++ b/docs/shell.nix @@ -0,0 +1,10 @@ +with import { }; +let + pkg = callPackage ./package.nix { }; +in +mkShell { + packages = [ + nodejs + ]; + env.WRAPPER_MANAGER_OPTIONS_JSON = pkg.options_json; +} diff --git a/docs/wm.data.js b/docs/wm.data.js index 9167367..2adfecd 100644 --- a/docs/wm.data.js +++ b/docs/wm.data.js @@ -2,25 +2,22 @@ import { dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { loadOptions, stripNixStore } from "easy-nix-documentation/loader"; -import { env } from "node:process"; - -const var_name = "WRAPPER_MANAGER_OPTIONS_JSON"; +import { env, exit } from "node:process"; export default { async load() { - const built = env[var_name]; - const settings = { + const options_json = env["WRAPPER_MANAGER_OPTIONS_JSON"]; + + if (options_json === undefined) { + console.error("WRAPPER_MANAGER_OPTIONS_JSON not set"); + exit(1); + } + + return await loadOptions(options_json, { mapDeclarations: (declaration) => { const relDecl = stripNixStore(declaration); return `<wrapper-manager/${relDecl}>`; }, - }; - if (built === undefined) { - console.log(var_name, "not set, falling back with nix build"); - const __dirname = dirname(fileURLToPath(import.meta.url)); - return await loadOptions(`${__dirname}#optionsJSON`, settings); - } else { - return await loadOptions(built, settings); - } + }); }, }; diff --git a/flake.nix b/flake.nix index 31d093e..2e6ff1c 100644 --- a/flake.nix +++ b/flake.nix @@ -1,3 +1,10 @@ { - outputs = _: import ./.; + outputs = + _: + let + toplevel = import ./default.nix; + in + { + lib = toplevel.v2; + }; } From 70dd2efc7b021089a4d97e195cc3fa25e117035a Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Thu, 19 Jun 2025 14:22:29 +0200 Subject: [PATCH 3/8] Fix documentation --- docs/default.nix | 1 + modules/common-wrapper.nix | 22 +++++++++++---- modules/env-type.nix | 3 ++ modules/many-wrappers.nix | 57 ++++++++++++++++++++++++++++++++++++-- modules/wrapper-impl.nix | 9 ++++-- tests/multi.nix | 14 +++++----- 6 files changed, 88 insertions(+), 18 deletions(-) create mode 100644 docs/default.nix diff --git a/docs/default.nix b/docs/default.nix new file mode 100644 index 0000000..c78dc4c --- /dev/null +++ b/docs/default.nix @@ -0,0 +1 @@ +(import { }).callPackage ./package.nix { } diff --git a/modules/common-wrapper.nix b/modules/common-wrapper.nix index 8df9f36..391bd92 100644 --- a/modules/common-wrapper.nix +++ b/modules/common-wrapper.nix @@ -13,29 +13,37 @@ in wrapFlags = mkOption { type = flagsType; default = [ ]; - description = "Flags passed to makeWrapper."; + description = "Structured flags passed to makeWrapper."; + example = [ + "--argv0" + "myprog" + ]; }; appendFlags = mkOption { type = flagsType; default = [ ]; - description = "Flags passed after any arguments to the wrapped program."; + description = "Flags passed after any arguments to the wrapped program. Usually you want to use prependFlags instead."; + example = lib.literalExpression '' + ["--config-file" ./config.toml] + ''; }; prependFlags = mkOption { type = flagsType; default = [ ]; description = "Flags passed before any arguments to the wrapped program."; + example = lib.literalExpression '' + ["--config-file" ./config.toml] + ''; }; env = mkOption { type = with types; attrsOf (submodule ./env-type.nix); default = { }; - description = "FIXME env"; + description = "Structured configuration for environment variables."; }; extraWrapperFlags = mkOption { type = with types; separatedString " "; description = '' - Raw flags passed to makeWrapper. - - See upstream documentation for make-wrapper.sh : https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/setup-hooks/make-wrapper.sh + Raw flags passed to makeWrapper. You may want to use wrapFlags instead. ''; default = ""; example = "--argv0 foo --set BAR value"; @@ -49,11 +57,13 @@ in example = lib.literalExpression "[ pkgs.starship ]"; }; wrapperType = mkOption { + description = "Whether to use a binary or a shell wrapper."; type = types.enum [ "shell" "binary" ]; default = "binary"; + example = "shell"; }; }; diff --git a/modules/env-type.nix b/modules/env-type.nix index 4d410ae..85954ab 100644 --- a/modules/env-type.nix +++ b/modules/env-type.nix @@ -15,6 +15,7 @@ in Name of the variable. ''; default = name; + example = "GIT_CONFIG"; }; value = mkOption { @@ -36,6 +37,7 @@ in Note that any environment variable will be escaped. For example, `value = "$HOME"` will be converted to the literal `$HOME`, with its dollar sign. ''; + example = lib.literalExpression "./gitconfig"; }; force = mkOption { @@ -50,6 +52,7 @@ in ''; default = config.value == null; defaultText = lib.literalMD "true if `value` is null, otherwise false"; + example = true; }; asFlags = mkOption { diff --git a/modules/many-wrappers.nix b/modules/many-wrappers.nix index 9da2f0e..1d04beb 100644 --- a/modules/many-wrappers.nix +++ b/modules/many-wrappers.nix @@ -1,12 +1,22 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let inherit (lib) mkOption types; in { options = { wrapperType = mkOption { - type = types.enum ["shell" "binary"]; + description = "Which wrapper type to use by default for all wrappers."; + type = types.enum [ + "shell" + "binary" + ]; default = "binary"; + example = "shell"; }; wrappers = mkOption { @@ -23,7 +33,48 @@ in }; } ); - description = "Wrappers to create"; + description = "Wrappers to create."; + example = lib.literalExpression '' + { + hello = { + basePackage = pkgs.hello; + prependFlags = [ + "-g" + "Hi" + ]; + }; + } + ''; + }; + + build = { + toplevel = mkOption { + type = types.package; + readOnly = true; + description = '' + (Read-only) Package that merges all the wrappers into a single derivation. + You may want to use build.packages instead. + ''; + }; + + packages = mkOption { + type = with types; attrsOf package; + readOnly = true; + description = '' + (Read-only) Attribute set of name=pkg, for every wrapper. + ''; + }; + }; + }; + + config = { + build = { + toplevel = pkgs.buildEnv { + name = "wrapper-manager-bundle"; + paths = builtins.attrValues config.build.packages; + }; + + packages = builtins.mapAttrs (_: value: value.wrapped) config.wrappers; }; }; } diff --git a/modules/wrapper-impl.nix b/modules/wrapper-impl.nix index df3812d..0917271 100644 --- a/modules/wrapper-impl.nix +++ b/modules/wrapper-impl.nix @@ -20,7 +20,7 @@ in wrapped = mkOption { type = types.package; readOnly = true; - description = "The wrapped package"; + description = "(Read-only) The final wrapped package"; }; overrideAttrs = mkOption { @@ -32,7 +32,7 @@ in defaultText = lib.literalExpression "lib.id"; example = lib.literalExpression '' old: { - pname = "''${old.pname}-wrapped"; + pname = "''${pname}-with-settings"; } ''; }; @@ -40,6 +40,11 @@ in postBuild = mkOption { type = types.str; default = ""; + description = "Raw commands to execute after the wrapping process has finished"; + example = '' + echo "Running sanity check" + $out/bin/nvim '+q' + ''; }; }; diff --git a/tests/multi.nix b/tests/multi.nix index f799429..162d4c7 100644 --- a/tests/multi.nix +++ b/tests/multi.nix @@ -35,13 +35,13 @@ wrapper-manager.v2 { basePackage = pkgs.zellij; }; - wrappers.hello-bad = { - basePackage = pkgs.hello; - flags = [ - "-g" - "g" - ]; - }; + # wrappers.hello-bad = { + # basePackage = pkgs.hello; + # flags = [ + # "-g" + # "g" + # ]; + # }; wrappers.neofetch = { basePackage = pkgs.neofetch.override { x11Support = false; }; From 351ed418ac618416e0f092bd75ca74548d21f83e Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Thu, 19 Jun 2025 14:25:19 +0200 Subject: [PATCH 4/8] Add ci workflow back --- .github/workflows/ci.yaml | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..650cf4a --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,46 @@ +name: ci + +on: + push: + branches: + - master + workflow_dispatch: + +permissions: + id-token: write + pages: write + contents: write + +jobs: + docs-build: + runs-on: ubuntu-latest + + environment: + name: github-pages + + steps: + - name: 📦 Install Nix + uses: cachix/install-nix-action@master + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + nix_path: nixpkgs=channel:nixos-unstable + + - name: 🏗️ Build + run: nix build -f ./docs -L + + - name: 📤 Upload artifacts + id: deployment + uses: actions/upload-pages-artifact@v3 + with: + path: ./result + + docs-deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: docs-build + steps: + - name: 🚀 Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 From 548aa2079e442e52d39423268deac7ab2304b9b3 Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Thu, 19 Jun 2025 15:39:58 +0200 Subject: [PATCH 5/8] Move back to .lib entrypoint --- default.nix | 4 +- docs/package.nix | 2 +- docs/readme.md | 161 ++++++++++++++++++------------------------- flake.nix | 9 +-- tests/multi.nix | 2 +- tests/standalone.nix | 2 +- 6 files changed, 72 insertions(+), 108 deletions(-) diff --git a/default.nix b/default.nix index 87e9530..2f9e050 100644 --- a/default.nix +++ b/default.nix @@ -16,9 +16,7 @@ let }; in { - lib = builtins.abort "wrapper-manager.lib is deprecated, please upgrade to the .v2 api: https://github.com/viperML/wrapper-manager/pull/26"; - - v2 = { + lib = { inherit eval; __functor = _: eval; wrapWith = diff --git a/docs/package.nix b/docs/package.nix index 2c305d0..1a1eef2 100644 --- a/docs/package.nix +++ b/docs/package.nix @@ -9,7 +9,7 @@ let options_json = (nixosOptionsDoc { options = - ((import ../.).v2 { + ((import ../.).lib { inherit pkgs; modules = [ ]; }).options; diff --git a/docs/readme.md b/docs/readme.md index 492cab7..af05e9f 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -22,7 +22,7 @@ # ~/.config/nushell is not neeeded! wrappers.nushell = { basePackage = pkgs.nushell; - flags = [ + prependFlags = [ "--env-config" ./env.nu "--config" @@ -74,117 +74,88 @@ around your applications, providing an easy-to use interface, and also getting around some of their shortcomings. -## **Module documentation** +## **Documentation** -https://viperml.github.io/wrapper-manager/docs/module +https://viperml.github.io/wrapper-manager -## **Installation/usage** +## **Installation and usage** -First, you need to instantiate wrapper-manager's lib. This can be done by pulling the WM flake, or by pulling the repo tarball directly. - -### Flake +Wrapper-manager is a library for creating wrappers. What you do with the wrappers is up to you, +you can use them to be installed with `nix-env -i`, add them to your NixOS config, to a devshell, +etc. ```nix -# flake.nix -{ - inputs = { - nixpkgs.url = "..."; - - # Add the wrapper-manager flake - wrapper-manager = { - url = "github:viperML/wrapper-manager"; - # WM's nixpkgs is only used for tests, you can safely drop this if needed. - inputs.nixpkgs.follows = "nixpkgs"; - }; +let + # Evaluate the module system + + wm-eval = wrapper-manager.lib { + inherit pkgs; + modules = [ + ./other-module.nix + { + wrappers.hello = { + basePackage = pkgs.hello; + prependFlags = ["-g" "Hi"]; + }; + } + ]; }; - outputs = {self, nixpkgs, wrapper-manager}: { ... }; -} -``` + # Extract one of the wrapped packages + myHello = wm-eval.config.wrappers.hello.wrapped; + #=> «derivation /nix/store/...» -### Classic + # Extract all wrapped packages + allWrappers = wm-eval.config.build.packages; + #=> { hello = «derivation /nix/store/...»; } -Wrapper-manager can be pulled in a classic (non-flake) setup for a dev-shell or NixOS configuration, like so: + # Add all the wrappers to systemPackages: + environment.systemPackages = [ ] ++ (builtins.attrValues wm-eval-config.build.packages); + # or using the bundle: + environment.systemPackages = [ wm-eval.config.build.toplevel ]; -```nix -# shell.nix -let - pkgs = import {}; - # optionally, pin a commit instead of using master - wrapper-manager = import (builtins.fetchTarball "https://github.com/viperML/wrapper-manager/archive/refs/heads/master.tar.gz") { - inherit (pkgs) lib; + + # Wrap a singular package + myGit = wrapper-manager.lib.wrapWith pkgs { + basePackage = pkgs.git; + env.GIT_CONFIG.value = ./gitconfig; }; + #=> «derivation /nix/store/...» in - mkShell { ..... } + ... ``` -```nix -# configuration.nix -{ config, pkgs, lib, ... }: let - # optionally, pin a commit instead of using master - wrapper-manager = import (builtins.fetchTarball "https://github.com/viperML/wrapper-manager/archive/refs/heads/master.tar.gz") { - inherit (pkgs) lib; - }; -in { - ..... -} -``` +### How do I get `wrapper-manager.lib` ? +The main entrypoint is `wrapper-manager.lib`. To get it: -### Evaluating +### Flakes -Now that you already have `wrapper-manager` in scope, you need to evaluate `wrapper-manager.lib`. The argument is an attrset with following elements: +```nix +{ + inputs.wrapper-manager.url = "github.com:viperML/wrapper-manager"; -- `pkgs`: your nixpkgs instance used to bring `symlinkJoin` and `makeWrapper`, as well as passing it through the modules for convenience. -- `modules`: a list of wrapper-manager modules. As with NixOS, a module can be passed as a path to a module or directly. A proper module is either an attrset, or a function to attrset. -- `specialArgs` (optional): extra arguments passed to the module system. + outputs = {self, wrapper-manager}: let + # wrapper-manager.lib { ... } + in {}; +} +``` -A convenience shorthand for `(wrapper-manager.lib {...}).config.build.toplevel` is available through: `wrapper-manager.lib.build {}`, which is probably what you want in 99% of the cases. +### Npins -```nix -# This expression outputs a package, which collects all wrappers. -# You can add it to: -# - environment.systemPackages -# - home.packages -# - mkShell { packages = [...]; } -# - etc - -(wrapper-manager.lib.build { - inherit pkgs; - modules = [ - ./my-module.nix - { - wrappers.foo = { ... }; - } - ]; -}) -# => «derivation /nix/store/...» +``` +$ npins add github viperML wrapper-manager ``` -For example, if you want to use wrapper-manager in the context of a dev-shell, you can instantiate it directly like so: ```nix -# pkgs and wrapper-manager in scope, see previous steps -# ... -mkShell { - packages = [ - - (wrapper-manager.lib.build { - inherit pkgs; - modules = [{ - wrappers.stack = { - basePackage = pkgs.stack; - flags = [ - "--resolver" - "lts" - ]; - env.NO_COLOR.value = "1"; - }; - }]; - }) +let + sources = import ./npins; + wrapper-manager = import sources.wrapper-manager; - ]; -} + # wrapper-manager.lib { ... } +in + ... ``` @@ -201,6 +172,10 @@ https://github.com/viperML/wrapper-manager/issues ## Changelog +- 2025-06-19 + - Full rewrite + - `flags` has been removed in favor of `prependFlags` + - 2024-08-24 - Added `postBuild` option @@ -224,10 +199,8 @@ https://github.com/viperML/wrapper-manager/issues - 2023-08-12 - Added wrappers.name.renames option. -
- -
\ No newline at end of file + \ No newline at end of file diff --git a/flake.nix b/flake.nix index 2e6ff1c..31d093e 100644 --- a/flake.nix +++ b/flake.nix @@ -1,10 +1,3 @@ { - outputs = - _: - let - toplevel = import ./default.nix; - in - { - lib = toplevel.v2; - }; + outputs = _: import ./.; } diff --git a/tests/multi.nix b/tests/multi.nix index 162d4c7..f8ea11d 100644 --- a/tests/multi.nix +++ b/tests/multi.nix @@ -4,7 +4,7 @@ let }; wrapper-manager = import ../.; in -wrapper-manager.v2 { +wrapper-manager.lib { inherit pkgs; modules = [ { diff --git a/tests/standalone.nix b/tests/standalone.nix index af7d186..a0e1f66 100644 --- a/tests/standalone.nix +++ b/tests/standalone.nix @@ -1,7 +1,7 @@ let pkgs = import { }; wrapper-manager = import ../.; - wrap = wrapper-manager.v2.wrapWith pkgs; + wrap = wrapper-manager.lib.wrapWith pkgs; in wrap { basePackage = pkgs.hello; From 4010339b5ca556737d2e5fdcbd6f8b19fdce2fba Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Thu, 19 Jun 2025 15:49:46 +0200 Subject: [PATCH 6/8] Add more examples --- modules/common-wrapper.nix | 5 +++++ modules/wrapper.nix | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/common-wrapper.nix b/modules/common-wrapper.nix index 391bd92..309accf 100644 --- a/modules/common-wrapper.nix +++ b/modules/common-wrapper.nix @@ -39,6 +39,11 @@ in type = with types; attrsOf (submodule ./env-type.nix); default = { }; description = "Structured configuration for environment variables."; + example = lib.literalExpression '' + { + GIT_CONFIG.value = ./gitconfig; + } + ''; }; extraWrapperFlags = mkOption { type = with types; separatedString " "; diff --git a/modules/wrapper.nix b/modules/wrapper.nix index cc386cf..a5d5852 100644 --- a/modules/wrapper.nix +++ b/modules/wrapper.nix @@ -23,7 +23,21 @@ in programs = mkOption { default = { }; - description = "Programs to wrap"; + description = "Wrap specific binaries with specific options. You may use it to skip wrapping some program."; + example = lib.literalExpression '' + { + supervim = { + target = "neovim"; + }; + + git = { + env.GIT_CONFIG.value = ./gitconfig; + }; + + # Don't wrap scalar + scalar = {}; + } + ''; type = types.attrsOf ( types.submoduleWith { modules = [ From 3da75e331e1b9f03b1507d522b79c799d3561ba0 Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Fri, 20 Jun 2025 12:59:14 +0200 Subject: [PATCH 7/8] Deprecate .flags --- modules/common-wrapper.nix | 20 +++++++++++++++++++- tests/multi.nix | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/common-wrapper.nix b/modules/common-wrapper.nix index 309accf..c16a6e0 100644 --- a/modules/common-wrapper.nix +++ b/modules/common-wrapper.nix @@ -1,6 +1,7 @@ { lib, config, + options, ... }: let @@ -27,6 +28,16 @@ in ["--config-file" ./config.toml] ''; }; + # Poor's man mkRemovedOptionModule + # As we don't have assertions + flags = mkOption { + type = flagsType; + default = [ ]; + description = "(Deprecated) Flags passed before any arguments to the wrapped program. Use prependFlags instead"; + apply = throw "The option `${lib.showOption [ "flags" ]}' used in ${lib.showFiles options.flags.files} is deprecated. Use `${ + lib.showOption [ "prependFlags" ] + }' instead."; + }; prependFlags = mkOption { type = flagsType; default = [ ]; @@ -39,7 +50,7 @@ in type = with types; attrsOf (submodule ./env-type.nix); default = { }; description = "Structured configuration for environment variables."; - example = lib.literalExpression '' + example = lib.literalExpression '' { GIT_CONFIG.value = ./gitconfig; } @@ -80,6 +91,13 @@ in f ]) config.prependFlags )) + # Force the eval of config.flags to trigger throw + ++ (flatten ( + map (f: [ + "--add-flag" + f + ]) config.flags + )) ++ (flatten ( map (f: [ "--append-flag" diff --git a/tests/multi.nix b/tests/multi.nix index f8ea11d..9e94d03 100644 --- a/tests/multi.nix +++ b/tests/multi.nix @@ -8,6 +8,7 @@ wrapper-manager.lib { inherit pkgs; modules = [ { + _file = ./multi.nix; wrapperType = "shell"; wrappers.discord = { From bb57881fb6fce082afd035d63d5441f31acacd10 Mon Sep 17 00:00:00 2001 From: Fernando Ayats Date: Fri, 20 Jun 2025 13:02:53 +0200 Subject: [PATCH 8/8] Fix .flags deprecation --- modules/common-wrapper.nix | 11 ++++++++--- tests/multi.nix | 17 ++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/modules/common-wrapper.nix b/modules/common-wrapper.nix index c16a6e0..529b243 100644 --- a/modules/common-wrapper.nix +++ b/modules/common-wrapper.nix @@ -34,9 +34,14 @@ in type = flagsType; default = [ ]; description = "(Deprecated) Flags passed before any arguments to the wrapped program. Use prependFlags instead"; - apply = throw "The option `${lib.showOption [ "flags" ]}' used in ${lib.showFiles options.flags.files} is deprecated. Use `${ - lib.showOption [ "prependFlags" ] - }' instead."; + apply = + flags: + if flags == [ ] then + [ ] + else + throw "The option `${lib.showOption [ "flags" ]}' used in ${lib.showFiles options.flags.files} is deprecated. Use `${ + lib.showOption [ "prependFlags" ] + }' instead."; }; prependFlags = mkOption { type = flagsType; diff --git a/tests/multi.nix b/tests/multi.nix index 9e94d03..ec4d2c0 100644 --- a/tests/multi.nix +++ b/tests/multi.nix @@ -15,6 +15,9 @@ wrapper-manager.lib { basePackage = pkgs.discord; env.NIXOS_OZONE_WL.value = "1"; + prependFlags = [ + "--disable-gpu" + ]; }; wrappers.hello = { @@ -36,13 +39,13 @@ wrapper-manager.lib { basePackage = pkgs.zellij; }; - # wrappers.hello-bad = { - # basePackage = pkgs.hello; - # flags = [ - # "-g" - # "g" - # ]; - # }; + wrappers.hello-bad = { + basePackage = pkgs.hello; + flags = [ + "-g" + "g" + ]; + }; wrappers.neofetch = { basePackage = pkgs.neofetch.override { x11Support = false; };