postInstall on opentofu.withPlugins

I want to override OpenTofu with an own postInstall parameter and add some plug-ins in addition. According to opentofu/default.nix:114-133 it is just not possible, see list-item 3..

My question here is if someone can help me to understand why. Here are my thoughts following the implementation and having written the following flake-definition for testing purposes:

  inputs = {
    nixpkgs-repo.url = "github:NixOS/nixpkgs/nixos-23.11";

  outputs = { self, nixpkgs-repo, flake-utils }:
    flake-utils.lib.eachSystem ["x86_64-linux" "x86_64-darwin" "aarch64-linux" "aarch64-darwin"] (system:
        nixpkgs = import nixpkgs-repo
      in {
        packages = {
          openTofuDist = (nixpkgs.opentofu.withPlugins (otfPlugins: )
            # It does not matter what is here – just that there is at least one item.
          ).overrideAttrs (finalAttrs: previousAttrs: {
            postInstall = previousAttrs.postInstall + ''
              pushd "''${out}/bin" &> /dev/null
                ln -s 'terraform 'tofu' # Many programs want this name (hardcoded).
        }; # packages end
      }); # outputs end

When executing nix build '.#openTofuDist' && ls -lah ./result/bin I see, that the symbolic link is not visible in the resulting derivation. (It’s – according to list-item 3 in the comment mentioned above – probably just in the derivation the wrapper references.)

My expectation regarding the control-flow in the package-definition in “default.nix” is as follows:

  1. In line 12 on nixpkgs.opentofu.withPlugins the control-flow in the “default.nix” goes via package (line 183) → passthru.withPlugins (line 38) → withPlugins (line 101) to a function that produces a derivation.
  2. This derivation is build (because at least one plug-in has been chosen) in the else-branch (see lines 147-148) with a stdenv.mkDerivation-call to build all the wrappers.
  3. On that derivation the above flake-definition calls in the lines 15 to 21 an overrideAttrs (modifying the name of the binary) which I expect to be defined via passthru = package.passthru // passthru; (line 156) in the line 136f as overrideAttrs = f: (package.overrideAttrs f).withPlugins plugins;.

:question: Question 1: Is this understanding correct so far?

And here, we pass overrideAttrs f through to the base-definition (lines 15 to 66) again and call on that result withPlugins again, which is the reason, why I don’t see the renaming on the built derivation containing the wrapper. :question: Question 2: Is this correct as well?

If correct, here then the overrideAttrs are called as defined by buildGoModule (and transitively probably stdenv.mkDerivation).

:question: Question 3: I guess, the whole thing would work, if passthru on the wrapper-derivation was not re-defined in line 156, correct?

:question: Question 4: Any idea what I can do to achieve my goal (i.e. get a working symlink for the wrapped program)? In @tazjin mentions some possibility (“let users modify the wrapper if there is a wrapper”) which I don’t understand.