From 16451ff3aa20e400e71c6ba7f41d66c31bee1a81 Mon Sep 17 00:00:00 2001 From: cinereal Date: Thu, 2 Oct 2025 00:56:32 +0200 Subject: [PATCH] opentofu: fix `withPlugins` to properly propagate package Adjusts the OpenTofu package in line with the `pluggable` construct used over at the Terraform package. This resolves a bug preventing package overrides from properly getting propagated. This triggered particularly in constructs invoking `overrideAttrs` after `withPlugins`, e.g. `(pkgs.terraform.withPlugins (p: with p; [external])).overrideAttrs { patches = [ ... ]; }`. Signed-off-by: cinereal --- pkgs/by-name/op/opentofu/package.nix | 172 ++++++++++++++------------- 1 file changed, 88 insertions(+), 84 deletions(-) diff --git a/pkgs/by-name/op/opentofu/package.nix b/pkgs/by-name/op/opentofu/package.nix index 67bb63b137a9..eed5a19636f1 100644 --- a/pkgs/by-name/op/opentofu/package.nix +++ b/pkgs/by-name/op/opentofu/package.nix @@ -42,7 +42,7 @@ let patches = [ ./provider-path-0_15.patch ]; passthru = { - inherit full plugins withPlugins; + inherit plugins; tests = { inherit opentofu_plugins_test; }; @@ -73,8 +73,6 @@ let }; }; - full = withPlugins (p: lib.filter lib.isDerivation (lib.attrValues p.actualProviders)); - opentofu_plugins_test = let mainTf = writeText "main.tf" '' @@ -88,7 +86,7 @@ let resource "random_id" "test" {} ''; - opentofu = package.withPlugins (p: [ p.random ]); + opentofu = (pluggable package).withPlugins (p: [ p.random ]); test = runCommand "opentofu-plugin-test" { buildInputs = [ opentofu ]; } '' # make it fail outside of sandbox export HTTP_PROXY=http://127.0.0.1:0 HTTPS_PROXY=https://127.0.0.1:0 @@ -105,89 +103,95 @@ let "recurseForDerivations" ]; - withPlugins = - plugins: + pluggable = + opentofu: let - actualPlugins = plugins package.plugins; + withPlugins = + plugins: + let + actualPlugins = plugins opentofu.plugins; - # Wrap PATH of plugins propagatedBuildInputs, plugins may have runtime dependencies on external binaries - wrapperInputs = lib.unique ( - lib.flatten (lib.catAttrs "propagatedBuildInputs" (builtins.filter (x: x != null) actualPlugins)) - ); + # Wrap PATH of plugins propagatedBuildInputs, plugins may have runtime dependencies on external binaries + wrapperInputs = lib.unique ( + lib.flatten (lib.catAttrs "propagatedBuildInputs" (builtins.filter (x: x != null) actualPlugins)) + ); - passthru = { - withPlugins = newplugins: withPlugins (x: newplugins x ++ actualPlugins); + passthru = { + withPlugins = newplugins: withPlugins (x: newplugins x ++ actualPlugins); + full = withPlugins (p: lib.filter lib.isDerivation (lib.attrValues p.actualProviders)); - # Expose wrappers around the override* functions of the terraform - # derivation. - # - # Note that this does not behave as anyone would expect if plugins - # are specified. The overrides are not on the user-visible wrapper - # derivation but instead on the function application that eventually - # generates the wrapper. This means: - # - # 1. When using overrideAttrs, only `passthru` attributes will - # become visible on the wrapper derivation. Other overrides that - # modify the derivation *may* still have an effect, but it can be - # difficult to follow. - # - # 2. Other overrides may work if they modify the terraform - # derivation, or they may have no effect, depending on what - # exactly is being changed. - # - # 3. Specifying overrides on the wrapper is unsupported. - # - # See nixpkgs#158620 for details. - overrideDerivation = f: (package.overrideDerivation f).withPlugins plugins; - overrideAttrs = f: (package.overrideAttrs f).withPlugins plugins; - override = x: (package.override x).withPlugins plugins; - }; + # Expose wrappers around the override* functions of the terraform + # derivation. + # + # Note that this does not behave as anyone would expect if plugins + # are specified. The overrides are not on the user-visible wrapper + # derivation but instead on the function application that eventually + # generates the wrapper. This means: + # + # 1. When using overrideAttrs, only `passthru` attributes will + # become visible on the wrapper derivation. Other overrides that + # modify the derivation *may* still have an effect, but it can be + # difficult to follow. + # + # 2. Other overrides may work if they modify the terraform + # derivation, or they may have no effect, depending on what + # exactly is being changed. + # + # 3. Specifying overrides on the wrapper is unsupported. + # + # See nixpkgs#158620 for details. + overrideDerivation = f: (pluggable (opentofu.overrideDerivation f)).withPlugins plugins; + overrideAttrs = f: (pluggable (opentofu.overrideAttrs f)).withPlugins plugins; + override = x: (pluggable (opentofu.override x)).withPlugins plugins; + }; + in + # Don't bother wrapping unless we actually have plugins, since the wrapper will stop automatic downloading + # of plugins, which might be counterintuitive if someone just wants a vanilla Terraform. + if actualPlugins == [ ] then + opentofu.overrideAttrs (orig: { + passthru = orig.passthru // passthru; + }) + else + lib.appendToName "with-plugins" ( + stdenv.mkDerivation { + inherit (opentofu) meta pname version; + nativeBuildInputs = [ makeWrapper ]; + + # Expose the passthru set with the override functions + # defined above, as well as any passthru values already + # set on `terraform` at this point (relevant in case a + # user overrides attributes). + passthru = opentofu.passthru // passthru; + + buildCommand = '' + # Create wrappers for terraform plugins because OpenTofu only + # walks inside of a tree of files. + # Also replace registry.terraform.io dir with registry.opentofu.org, + # so OpenTofu can find the plugins. + for providerDir in ${toString actualPlugins} + do + for file in $(find $providerDir/libexec/terraform-providers -type f) + do + relFile=''${file#$providerDir/} + relFile=''${relFile/registry.terraform.io/registry.opentofu.org} + mkdir -p $out/$(dirname $relFile) + cat < $out/$relFile + #!${runtimeShell} + exec "$file" "$@" + WRAPPER + chmod +x $out/$relFile + done + done + + # Create a wrapper for opentofu to point it to the plugins dir. + mkdir -p $out/bin/ + makeWrapper "${opentofu}/bin/tofu" "$out/bin/tofu" \ + --set NIX_TERRAFORM_PLUGIN_DIR $out/libexec/terraform-providers \ + --prefix PATH : "${lib.makeBinPath wrapperInputs}" + ''; + } + ); in - # Don't bother wrapping unless we actually have plugins, since the wrapper will stop automatic downloading - # of plugins, which might be counterintuitive if someone just wants a vanilla Terraform. - if actualPlugins == [ ] then - package.overrideAttrs (orig: { - passthru = orig.passthru // passthru; - }) - else - lib.appendToName "with-plugins" ( - stdenv.mkDerivation { - inherit (package) meta pname version; - nativeBuildInputs = [ makeWrapper ]; - - # Expose the passthru set with the override functions - # defined above, as well as any passthru values already - # set on `terraform` at this point (relevant in case a - # user overrides attributes). - passthru = package.passthru // passthru; - - buildCommand = '' - # Create wrappers for terraform plugins because OpenTofu only - # walks inside of a tree of files. - # Also replace registry.terraform.io dir with registry.opentofu.org, - # so OpenTofu can find the plugins. - for providerDir in ${toString actualPlugins} - do - for file in $(find $providerDir/libexec/terraform-providers -type f) - do - relFile=''${file#$providerDir/} - relFile=''${relFile/registry.terraform.io/registry.opentofu.org} - mkdir -p $out/$(dirname $relFile) - cat < $out/$relFile - #!${runtimeShell} - exec "$file" "$@" - WRAPPER - chmod +x $out/$relFile - done - done - - # Create a wrapper for opentofu to point it to the plugins dir. - mkdir -p $out/bin/ - makeWrapper "${package}/bin/tofu" "$out/bin/tofu" \ - --set NIX_TERRAFORM_PLUGIN_DIR $out/libexec/terraform-providers \ - --prefix PATH : "${lib.makeBinPath wrapperInputs}" - ''; - } - ); + withPlugins (_: [ ]); in -package +pluggable package