Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package opentofu for openSUSE:Factory checked in at 2026-05-29 18:08:41 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/opentofu (Old) and /work/SRC/openSUSE:Factory/.opentofu.new.1937 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "opentofu" Fri May 29 18:08:41 2026 rev:49 rq:1355775 version:1.12.1 Changes: -------- --- /work/SRC/openSUSE:Factory/opentofu/opentofu.changes 2026-05-16 19:27:10.456550323 +0200 +++ /work/SRC/openSUSE:Factory/.opentofu.new.1937/opentofu.changes 2026-05-29 18:10:20.090418031 +0200 @@ -1,0 +2,24 @@ +Fri May 29 06:48:34 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 1.12.1: + Full Changelog: + https://github.com/opentofu/opentofu/blob/v1.12/CHANGELOG.md + * SECURITY ADVISORIES: + - Previous releases in the v1.12 series could be affected by + several vulnerabilities: + - ssh usage through OpenTofu generate hangs or panics. + - Previously, a revoked 'SignatureKey' belonging to a CA was + not correctly checked for revocation. Now, both the 'key' + and 'key.SignatureKey' are checked for @revoked. + This is fixed now by (#4145) + * BUG FIXES: + - Address a bug introduced in v1.12.0 causing excessive memory + usage by providers. (#4126) + - Address a bug introduced in v1.12.0 where + replace_triggered_by was validated incorrectly. (#4133 + - The Azure key provider will now accept the tenant_id, + subscription_id, environment, and metadata_host variables; a + bug previously only allowed these to be set through + environment variables. (#4091) + +------------------------------------------------------------------- Old: ---- opentofu-1.12.0.obscpio New: ---- opentofu-1.12.1.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ opentofu.spec ++++++ --- /var/tmp/diff_new_pack.J8wOX5/_old 2026-05-29 18:10:21.118462273 +0200 +++ /var/tmp/diff_new_pack.J8wOX5/_new 2026-05-29 18:10:21.122462445 +0200 @@ -19,7 +19,7 @@ %define executable_name tofu Name: opentofu -Version: 1.12.0 +Version: 1.12.1 Release: 0 Summary: Declaratively manage your cloud infrastructure License: MPL-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.J8wOX5/_old 2026-05-29 18:10:21.174464684 +0200 +++ /var/tmp/diff_new_pack.J8wOX5/_new 2026-05-29 18:10:21.178464856 +0200 @@ -1,9 +1,9 @@ <services> <service name="obs_scm" mode="manual"> - <param name="url">https://github.com/opentofu/opentofu/</param> + <param name="url">https://github.com/opentofu/opentofu.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">v1.12.0</param> + <param name="revision">refs/tags/v1.12.1</param> <param name="versionformat">@PARENT_TAG@</param> <param name="versionrewrite-pattern">v(.*)</param> <param name="changesgenerate">enable</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.J8wOX5/_old 2026-05-29 18:10:21.202465889 +0200 +++ /var/tmp/diff_new_pack.J8wOX5/_new 2026-05-29 18:10:21.206466061 +0200 @@ -1,6 +1,8 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/opentofu/opentofu/</param> - <param name="changesrevision">bdcbf091aab886499ed7718d7f558b428fffce39</param></service></servicedata> + <param name="changesrevision">bdcbf091aab886499ed7718d7f558b428fffce39</param></service><service name="tar_scm"> + <param name="url">https://github.com/opentofu/opentofu.git</param> + <param name="changesrevision">dc29882638869cdc60f871a112a04a415042caf1</param></service></servicedata> (No newline at EOF) ++++++ opentofu-1.12.0.obscpio -> opentofu-1.12.1.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/CHANGELOG.md new/opentofu-1.12.1/CHANGELOG.md --- old/opentofu-1.12.0/CHANGELOG.md 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/CHANGELOG.md 2026-05-27 10:51:46.000000000 +0200 @@ -1,6 +1,20 @@ The v1.12.x release series is supported until **February 1 2027**. -## 1.12.1 (Unreleased) +## 1.12.1 + +SECURITY ADVISORIES: + +* Previous releases in the v1.12 series could be affected by several vulnerabilities: + * ssh usage through OpenTofu generate hangs or panics. + * Previously, a revoked 'SignatureKey' belonging to a CA was not correctly checked for revocation. Now, both the 'key' and 'key.SignatureKey' are checked for @revoked. + + This is fixed now by ([#4145](https://github.com/opentofu/opentofu/pull/4145)) + +BUG FIXES: + +- Address a bug introduced in v1.12.0 causing excessive memory usage by providers. ([#4126](https://github.com/opentofu/opentofu/pull/4126)) +- Address a bug introduced in v1.12.0 where `replace_triggered_by` was validated incorrectly. ([#4133](https://github.com/opentofu/opentofu/pull/4133) +- The Azure key provider will now accept the `tenant_id`, `subscription_id`, `environment`, and `metadata_host` variables; a bug previously only allowed these to be set through environment variables. ([#4091](https://github.com/opentofu/opentofu/issues/4091)) ## 1.12.0 @@ -32,6 +46,7 @@ - New `lifecycle` meta-argument `destroy`: when set to `false` OpenTofu will plan to just remove the affected object from state without asking the provider to destroy it first, similar to `destroy = false` in `removed` blocks. ([#3409](https://github.com/opentofu/opentofu/pull/3409)) - Comparing an object or other complex-typed value to `null` using the `==` operator now returns a sensitive boolean result only if the object as a whole is sensitive, and not when the object merely contains a sensitive value nested inside one of its attributes. This means that comparisons to null can now be used in parts of the configuration where sensitive values are not allowed, such as in the `enabled` meta-argument on resources and modules. ([#3793](https://github.com/opentofu/opentofu/pull/3793)) - Resources using `replace_triggered_by` in their `lifecycle` block are now replaced when a resource they refer to is itself being replaced, whereas before this triggered only when it was being updated. ([#3714](https://github.com/opentofu/opentofu/issues/3714)) +- `import` blocks now allow describing the object to be imported using an object matching the resource type's _identity schema_ using the `identity` argument, instead of just a plain string using the `id` argument. ([#3671](https://github.com/opentofu/opentofu/pull/3671)) - OpenTofu now produces warnings for any references to attributes or blocks of a resource type that are marked as deprecated in the provider schema, unless disabled by the `-deprecation=` option. ([#3973](https://github.com/opentofu/opentofu/pull/3973/)) - The `yamldecode` function now supports the "merge" tag, most commonly written as `<<` where a map key would be expected, with sequences of mappings rather than just individual mappings. ([#3607](https://github.com/opentofu/opentofu/pull/3607)) - A new configuration block type `language` offers a more general way to define version constraints that separates OpenTofu constraints from other software. Note that module authors should delay adopting this new syntax until they are ready to require OpenTofu v1.12.0 or later, but there is an interim solution available that is backward-compatible with earlier OpenTofu versions. ([#3300](https://github.com/opentofu/opentofu/issues/3300)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/go.mod new/opentofu-1.12.1/go.mod --- old/opentofu-1.12.0/go.mod 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/go.mod 2026-05-27 10:51:46.000000000 +0200 @@ -102,14 +102,14 @@ go.opentelemetry.io/otel/sdk v1.42.0 go.opentelemetry.io/otel/trace v1.42.0 go.uber.org/mock v0.6.0 - golang.org/x/crypto v0.49.0 - golang.org/x/mod v0.34.0 - golang.org/x/net v0.52.0 + golang.org/x/crypto v0.52.0 + golang.org/x/mod v0.35.0 + golang.org/x/net v0.54.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 - golang.org/x/sys v0.42.0 - golang.org/x/term v0.41.0 - golang.org/x/text v0.35.0 + golang.org/x/sys v0.45.0 + golang.org/x/term v0.43.0 + golang.org/x/text v0.37.0 google.golang.org/api v0.271.0 google.golang.org/grpc v1.79.3 google.golang.org/protobuf v1.36.11 @@ -299,7 +299,7 @@ golang.org/x/exp v0.0.0-20250808145144-a408d31f581a // indirect golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/tools v0.44.0 // indirect google.golang.org/genproto v0.0.0-20260217215200-42d3e9bedb6d // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260226221140-a57be14db171 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/go.sum new/opentofu-1.12.1/go.sum --- old/opentofu-1.12.0/go.sum 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/go.sum 2026-05-27 10:51:46.000000000 +0200 @@ -926,8 +926,8 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -951,8 +951,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= -golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= +golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -974,8 +974,8 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= -golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1039,13 +1039,13 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= -golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= -golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -1053,8 +1053,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= @@ -1072,8 +1072,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/backend/remote-state/azure/auth/oidc_auth.go new/opentofu-1.12.1/internal/backend/remote-state/azure/auth/oidc_auth.go --- old/opentofu-1.12.0/internal/backend/remote-state/azure/auth/oidc_auth.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/backend/remote-state/azure/auth/oidc_auth.go 2026-05-27 10:51:46.000000000 +0200 @@ -13,6 +13,7 @@ "net/http" "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/opentofu/opentofu/internal/httpclient" "github.com/opentofu/opentofu/internal/tfdiags" @@ -53,9 +54,9 @@ // a fresh OIDC token as needed, enabling proper token refresh behavior. func(ctx context.Context) (string, error) { client := httpclient.New(ctx) - + if config.OIDCToken == "" && config.OIDCTokenFilePath == "" { - return getTokenFromRemote(client, config.OIDCAuthConfig) + return getTokenFromRemote(client, config.OIDCAuthConfig, config.CloudConfig) } return consolidateToken(config) }, @@ -69,7 +70,7 @@ Value string `json:"value"` } -func getTokenFromRemote(client *http.Client, config OIDCAuthConfig) (string, error) { +func getTokenFromRemote(client *http.Client, config OIDCAuthConfig, env cloud.Configuration) (string, error) { // GET from the request URL, using the bearer token req, err := http.NewRequest(http.MethodGet, config.OIDCRequestURL, nil) if err != nil { @@ -79,9 +80,11 @@ req.Header.Add("Accept", "application/json; api-version=2.0") req.Header.Add("Content-Type", "application/json") - query := req.URL.Query() - query.Set("audience", "api://AzureADTokenExchange") - req.URL.RawQuery = query.Encode() + if !req.URL.Query().Has("audience") { + query := req.URL.Query() + query.Set("audience", requestURLAudience(env)) + req.URL.RawQuery = query.Encode() + } // Read the response resp, err := client.Do(req) @@ -105,6 +108,22 @@ return token.Value, nil } +// requestURLAudience ensures the audience is configured appropriately for +// its target cloud authority +// +// Reference: +// https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-config-app-trust-managed-identity +func requestURLAudience(env cloud.Configuration) string { + switch env.ActiveDirectoryAuthorityHost { + case "https://login.chinacloudapi.cn/": + return "api://AzureADTokenExchangeChina" + case "https://login.microsoftonline.us/": + return "api://AzureADTokenExchangeUSGov" + default: + return "api://AzureADTokenExchange" + } +} + func consolidateToken(config *Config) (string, error) { return consolidateFileAndValue(config.OIDCToken, config.OIDCTokenFilePath, "token", true) } @@ -145,7 +164,7 @@ } if directTokenUnset { // check request URL and token - _, err := getTokenFromRemote(httpclient.New(ctx), config.OIDCAuthConfig) + _, err := getTokenFromRemote(httpclient.New(ctx), config.OIDCAuthConfig, config.CloudConfig) if err != nil { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/command/meta_providers.go new/opentofu-1.12.1/internal/command/meta_providers.go --- old/opentofu-1.12.0/internal/command/meta_providers.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/command/meta_providers.go 2026-05-27 10:51:46.000000000 +0200 @@ -307,6 +307,11 @@ checkedProvider := false var checkErr error + // This should only ever be called once per provider type. + // It creates the schema cache to be re-used in all of the subsequent + // provider instances. + factory := providerFactory(cached) + factories[provider] = func() (providers.Interface, error) { checkLock.Lock() if !checkedProvider { @@ -319,7 +324,7 @@ return nil, checkErr } - return providerFactory(cached)() + return factory() } } for provider, localDir := range devOverrideProviders { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/encryption/keyprovider/azure_vault/config.go new/opentofu-1.12.1/internal/encryption/keyprovider/azure_vault/config.go --- old/opentofu-1.12.0/internal/encryption/keyprovider/azure_vault/config.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/encryption/keyprovider/azure_vault/config.go 2026-05-27 10:51:46.000000000 +0200 @@ -130,8 +130,8 @@ return nil, nil, &keyprovider.ErrInvalidConfiguration{Message: "key_length must be at least 1"} } - environment := stringAttrDefaultEnvFallback(c.OIDCToken, "public", "ARM_ENVIRONMENT") - metadataHost := stringAttrEnvFallback(c.OIDCToken, "ARM_METADATA_HOST") + environment := stringAttrDefaultEnvFallback(c.Environment, "public", "ARM_ENVIRONMENT") + metadataHost := stringAttrEnvFallback(c.MetadataHost, "ARM_METADATA_HOST") cloudConfig, _, err := auth.CloudConfigFromAddresses( ctx, @@ -176,8 +176,8 @@ }, StorageAddresses: auth.StorageAddresses{ CloudConfig: cloudConfig, - SubscriptionID: stringAttrEnvFallback(c.OIDCToken, "ARM_SUBSCRIPTION_ID"), - TenantID: stringAttrEnvFallback(c.OIDCToken, "ARM_TENANT_ID"), + SubscriptionID: stringAttrEnvFallback(c.SubscriptionID, "ARM_SUBSCRIPTION_ID"), + TenantID: stringAttrEnvFallback(c.TenantID, "ARM_TENANT_ID"), }, WorkloadIdentityAuthConfig: auth.WorkloadIdentityAuthConfig{ UseAKSWorkloadIdentity: c.UseAKS, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/encryption/keyprovider/azure_vault/config_test.go new/opentofu-1.12.1/internal/encryption/keyprovider/azure_vault/config_test.go --- old/opentofu-1.12.0/internal/encryption/keyprovider/azure_vault/config_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/opentofu-1.12.1/internal/encryption/keyprovider/azure_vault/config_test.go 2026-05-27 10:51:46.000000000 +0200 @@ -0,0 +1,122 @@ +// Copyright (c) The OpenTofu Authors +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) 2023 HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package azure_vault + +import ( + "context" + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/gohcl" + "github.com/hashicorp/hcl/v2/hclsyntax" + + "github.com/google/go-cmp/cmp" + "github.com/opentofu/opentofu/internal/backend/remote-state/azure/auth" +) + +func getParsedConfig(c **auth.Config) authMethodGetter { + return func(ctx context.Context, authConfig *auth.Config) (auth.AuthMethod, error) { + *c = authConfig + return auth.GetAuthMethod(ctx, authConfig) + } +} + +// This does not test the validity of the configuration. +// It only checks that configuration variables passed in the key_provider block are recognized and used +func TestConfig_Build(t *testing.T) { + cloudConfig, _, err := auth.CloudConfigFromAddresses(t.Context(), "public", "") + if err != nil { + t.Fatal(err) + } + testCases := map[string]struct { + input string + expected *auth.Config + }{ + "AuthConfigurationVariables": { + input: ` + use_cli = false + vault_uri = "https://example-keys.vault.azure.net" + vault_key_name = "my-rsa-key" + key_length = 32 + subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-subscriptionID" + tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-tenantID" + client_id = "xxxxxxxx-xxxx-xxxx-xxxx-clientID" + client_id_file_path = "./client-id-file-path" + client_secret = "client-secret-string" + client_secret_file_path = "./client-secret-file-path" + client_certificate = "client-certificate-string" + client_certificate_password = "client-certificate-password" + client_certificate_path = "./client-certificate-path" + use_oidc = false + oidc_token = "oidc_token" + oidc_token_file_path = "./oidc-token-file-path" + oidc_request_url = "https://oidc-request-url" + oidc_request_token = "oidc-request-token" + use_msi = false + msi_endpoint = "https://msi-endpoint" + use_aks_workload_identity = false + `, + expected: &auth.Config{ + AzureCLIAuthConfig: auth.AzureCLIAuthConfig{ + CLIAuthEnabled: false, + }, + ClientSecretCredentialAuthConfig: auth.ClientSecretCredentialAuthConfig{ + ClientID: "xxxxxxxx-xxxx-xxxx-xxxx-clientID", + ClientIDFilePath: "./client-id-file-path", + ClientSecret: "client-secret-string", + ClientSecretFilePath: "./client-secret-file-path", + }, + ClientCertificateAuthConfig: auth.ClientCertificateAuthConfig{ + ClientCertificate: "client-certificate-string", + ClientCertificatePassword: "client-certificate-password", + ClientCertificatePath: "./client-certificate-path", + }, + OIDCAuthConfig: auth.OIDCAuthConfig{ + UseOIDC: false, + OIDCToken: "oidc_token", + OIDCTokenFilePath: "./oidc-token-file-path", + OIDCRequestURL: "https://oidc-request-url", + OIDCRequestToken: "oidc-request-token", + }, + MSIAuthConfig: auth.MSIAuthConfig{ + UseMsi: false, + Endpoint: "https://msi-endpoint", + }, + StorageAddresses: auth.StorageAddresses{ + CloudConfig: cloudConfig, + SubscriptionID: "xxxxxxxx-xxxx-xxxx-xxxx-subscriptionID", + TenantID: "xxxxxxxx-xxxx-xxxx-xxxx-tenantID", + }, + WorkloadIdentityAuthConfig: auth.WorkloadIdentityAuthConfig{ + UseAKSWorkloadIdentity: false, + }, + }, + }, + } + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + input, diags := hclsyntax.ParseConfig([]byte(testCase.input), "test_config", hcl.InitialPos) + if diags.HasErrors() { + t.Fatal(diags.Error()) + } + config := new(Config) + diags = gohcl.DecodeBody(input.Body, nil, config) + if diags.HasErrors() { + t.Fatal(diags.Error()) + } + var authConfig *auth.Config + getAuthMethod = getParsedConfig(&authConfig) + _, _, err := config.Build() + if authConfig == nil && err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(testCase.expected, authConfig); diff != "" { + t.Errorf("mismatch (-expected +received):\n%s", diff) + } + }) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/tofu/context_validate_test.go new/opentofu-1.12.1/internal/tofu/context_validate_test.go --- old/opentofu-1.12.0/internal/tofu/context_validate_test.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/tofu/context_validate_test.go 2026-05-27 10:51:46.000000000 +0200 @@ -2969,3 +2969,209 @@ }) } } + +// Test that replace_triggered_by validates attribute traversals against the +// schema of the referenced resource type, not the schema of the resource +// containing the lifecycle block. +func TestContext2Validate_replaceTriggeredByCrossResourceType(t *testing.T) { + // "test_instance" has a "value" attribute; "test_resource" does not. + // "test_resource" has a "output" attribute; "test_instance" does not. + p := testProvider("test") + p.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "value": {Type: cty.String, Optional: true}, + }, + }, + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "output": {Type: cty.String, Computed: true}, + }, + }, + }, + }) + p2 := testProvider("test2") + p2.GetProviderSchemaResponse = getProviderSchemaResponseFromProviderSchema(&ProviderSchema{ + ResourceTypes: map[string]*configschema.Block{ + "test_instance": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "value2": {Type: cty.String, Optional: true}, + }, + }, + "test_resource": { + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "output2": {Type: cty.String, Computed: true}, + }, + }, + }, + }) + + tests := map[string]struct { + config string + wantError bool + }{ + "valid attribute reference on different resource type": { + config: ` +resource "test_instance" "a" { + value = "hello" +} + +resource "test_resource" "b" { + lifecycle { + replace_triggered_by = [test_instance.a.value] + } +} +`, + wantError: false, + }, + "valid attribute reference on different resource type configured with a different provider": { + config: ` +resource "test_instance" "a" { + provider = test2 + value2 = "hello" +} +resource "test_resource" "b" { + provider = test + lifecycle { + replace_triggered_by = [test_instance.a.value2] + } +} +`, + wantError: false, + }, + "valid attribute reference on different resource type configured with a different provider and count": { + config: ` +resource "test_instance" "a" { + count = 2 + provider = test2 + value2 = "hello" +} +resource "test_resource" "b" { + provider = test + lifecycle { + replace_triggered_by = [test_instance.a[0].value2] + } +} +`, + wantError: false, + }, + "valid attribute reference on different resource type configured with a different provider and for_each": { + config: ` +resource "test_instance" "a" { + for_each = toset(["a", "b"]) + provider = test2 + value2 = "hello" +} +resource "test_resource" "b" { + provider = test + lifecycle { + replace_triggered_by = [test_instance.a["b"].value2] + } +} +`, + wantError: false, + }, + + "valid computed attribute reference on different resource type": { + config: ` +resource "test_resource" "a" {} + +resource "test_instance" "b" { + lifecycle { + replace_triggered_by = [test_resource.a.output] + } +} +`, + wantError: false, + }, + + "valid computed attribute references on multiple resources with the same type": { + config: ` +resource "test_resource" "a" {} +resource "test_resource" "b" {} +resource "test_resource" "c" {} +resource "test_instance" "d" {} + +resource "test_instance" "b" { + lifecycle { + replace_triggered_by = [ + test_resource.a.output, + test_resource.b.output, + test_resource.c.output, + test_instance.d.value + ] + } +} +`, + wantError: false, + }, + "invalid attribute reference on different resource type": { + config: ` +resource "test_instance" "a" { + value = "hello" +} + +resource "test_resource" "b" { + lifecycle { + replace_triggered_by = [test_instance.a.nonexistent] + } +} +`, + wantError: true, + }, + "attribute exists on containing resource type but not referenced resource type": { + config: ` +resource "test_instance" "a" { + value = "hello" +} + +resource "test_resource" "b" { + lifecycle { + replace_triggered_by = [test_instance.a.output] + } +} +`, + wantError: true, + }, + "missing resource referenced in replace_triggered_by": { + config: ` +resource "test_instance" "a" { + lifecycle { + replace_triggered_by = [ + test_resource.missing.output, + ] + } +} +`, + wantError: true, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": tc.config, + }) + + c := testContext2(t, &ContextOpts{ + Plugins: plugins.NewLibrary(map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + addrs.NewDefaultProvider("test2"): testProviderFuncFixed(p2), + }, nil), + }) + + diags := c.Validate(context.Background(), m) + if tc.wantError && !diags.HasErrors() { + t.Fatal("succeeded; want error") + } + if !tc.wantError && diags.HasErrors() { + t.Fatalf("unexpected error: %s", diags.Err()) + } + }) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/tofu/node_provider.go new/opentofu-1.12.1/internal/tofu/node_provider.go --- old/opentofu-1.12.0/internal/tofu/node_provider.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/tofu/node_provider.go 2026-05-27 10:51:46.000000000 +0200 @@ -103,9 +103,11 @@ if op == walkValidate { log.Printf("[TRACE] NodeApplyableProvider: validating configuration for %s", n.Addr) instance, newDiags := evalCtx.Providers().NewProvider(ctx, n.Addr.Provider) - n.instances[addrs.NoKey] = instance - for key, data := range instanceData { - diags = diags.Append(n.ValidateProvider(ctx, evalCtx, instance, key, data)) + if !newDiags.HasErrors() { // We can't validate if we didn't successfully start the provider plugin + n.instances[addrs.NoKey] = instance + for key, data := range instanceData { + diags = diags.Append(n.ValidateProvider(ctx, evalCtx, instance, key, data)) + } } return diags.Append(newDiags) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/tofu/node_resource_abstract.go new/opentofu-1.12.1/internal/tofu/node_resource_abstract.go --- old/opentofu-1.12.0/internal/tofu/node_resource_abstract.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/tofu/node_resource_abstract.go 2026-05-27 10:51:46.000000000 +0200 @@ -85,6 +85,8 @@ ProvisionerSchemas map[string]*configschema.Block + ReplaceTriggeredBySchemas map[addrs.Resource]*configschema.Block + // Set from GraphNodeTargetable Targets []addrs.Targetable @@ -117,18 +119,19 @@ } var ( - _ GraphNodeReferenceable = (*NodeAbstractResource)(nil) - _ GraphNodeReferencer = (*NodeAbstractResource)(nil) - _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil) - _ GraphNodeProvisionerConsumer = (*NodeAbstractResource)(nil) - _ GraphNodeConfigResource = (*NodeAbstractResource)(nil) - _ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil) - _ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil) - _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil) - _ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil) - _ GraphNodeTargetable = (*NodeAbstractResource)(nil) - _ graphNodeAttachResourceDependsOn = (*NodeAbstractResource)(nil) - _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) + _ GraphNodeReferenceable = (*NodeAbstractResource)(nil) + _ GraphNodeReferencer = (*NodeAbstractResource)(nil) + _ GraphNodeProviderConsumer = (*NodeAbstractResource)(nil) + _ GraphNodeProvisionerConsumer = (*NodeAbstractResource)(nil) + _ GraphNodeConfigResource = (*NodeAbstractResource)(nil) + _ GraphNodeAttachResourceConfig = (*NodeAbstractResource)(nil) + _ GraphNodeAttachResourceSchema = (*NodeAbstractResource)(nil) + _ GraphNodeAttachReplaceTriggeredBySchema = (*NodeAbstractResource)(nil) + _ GraphNodeAttachProvisionerSchema = (*NodeAbstractResource)(nil) + _ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResource)(nil) + _ GraphNodeTargetable = (*NodeAbstractResource)(nil) + _ graphNodeAttachResourceDependsOn = (*NodeAbstractResource)(nil) + _ dag.GraphNodeDotter = (*NodeAbstractResource)(nil) ) // NewNodeAbstractResource creates an abstract resource graph node for @@ -470,6 +473,29 @@ n.ProvisionerSchemas[name] = schema } +// GraphNodeAttachReplaceTriggeredBySchema +func (n *NodeAbstractResource) ReplaceTriggeredBy() []*addrs.Reference { + // If we have no configuration, then we have no replace_triggred_by + if n.Config == nil || len(n.Config.TriggersReplacement) == 0 { + return nil + } + result := make([]*addrs.Reference, 0, len(n.Config.TriggersReplacement)) + for _, expr := range n.Config.TriggersReplacement { + refs, _ := lang.ReferencesInExpr(addrs.ParseRef, expr) + result = append(result, refs...) + } + + return result +} + +// GraphNodeAttachReplaceTriggeredBySchema +func (n *NodeAbstractResource) AttachReplaceTriggeredBySchema(addr addrs.Resource, schema *configschema.Block) { + if n.ReplaceTriggeredBySchemas == nil { + n.ReplaceTriggeredBySchemas = make(map[addrs.Resource]*configschema.Block) + } + n.ReplaceTriggeredBySchemas[addr] = schema +} + // GraphNodeResource func (n *NodeAbstractResource) ResourceAddr() addrs.ConfigResource { return n.Addr diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/tofu/node_resource_validate.go new/opentofu-1.12.1/internal/tofu/node_resource_validate.go --- old/opentofu-1.12.0/internal/tofu/node_resource_validate.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/tofu/node_resource_validate.go 2026-05-27 10:51:46.000000000 +0200 @@ -360,15 +360,34 @@ continue } - if len(ref.Remaining) == 0 { + var refAddr addrs.Resource + switch rs := ref.Subject.(type) { + case addrs.Resource: + refAddr = rs + case addrs.ResourceInstance: + refAddr = rs.Resource + default: continue } - // Validate if rest of the reference is valid. The check above does not do that, - // it only checks the resource type and its primary attributes. - remainingDiags := schemaForType.Block.StaticValidateTraversal(ref.Remaining) - if remainingDiags.HasErrors() { - diags = diags.Append(remainingDiags) + refSchema, ok := n.ReplaceTriggeredBySchemas[refAddr] + if !ok { + // (We could also get here if [AttachSchemaTransformer] has a bug that + // makes it fail to register one of the needed schemas.) + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Undeclared resource in replace_triggered_by", + Detail: fmt.Sprintf("Resource %s refers to %s in replace_triggered_by, but it is not declared in this module.", n.Addr, refAddr), + Subject: expr.Range().Ptr(), + }) + continue + } + + if len(ref.Remaining) != 0 { + remainingDiags := refSchema.StaticValidateTraversal(ref.Remaining) + if remainingDiags.HasErrors() { + diags = diags.Append(remainingDiags) + } } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/internal/tofu/transform_attach_schema.go new/opentofu-1.12.1/internal/tofu/transform_attach_schema.go --- old/opentofu-1.12.0/internal/tofu/transform_attach_schema.go 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/internal/tofu/transform_attach_schema.go 2026-05-27 10:51:46.000000000 +0200 @@ -38,13 +38,26 @@ type GraphNodeAttachProvisionerSchema interface { ProvisionedBy() []string - // SetProvisionerSchema is called during transform for each provisioner + // AttachProvisionerSchema is called during transform for each provisioner // type returned from ProvisionedBy, providing the configuration schema // for each provisioner in turn. The implementer should save these for // later use in evaluating provisioner configuration blocks. AttachProvisionerSchema(name string, schema *configschema.Block) } +// GraphNodeAttachReplaceTriggeredBySchema is an interface implemented by node types +// that need one or more provider schemas attached for the replace_triggered_by. +type GraphNodeAttachReplaceTriggeredBySchema interface { + GraphNodeConfigResource + ReplaceTriggeredBy() []*addrs.Reference + + // AttachReplaceTriggeredBySchema is called during transform for each resource type + // type returned from ReplaceTriggeredBy, providing the configuration schema + // for each referenced provider in turn. The implementer should save these for + // later use in evaluating replace_triggered_by references. + AttachReplaceTriggeredBySchema(ref addrs.Resource, schema *configschema.Block) +} + // AttachSchemaTransformer finds nodes that implement // GraphNodeAttachResourceSchema, GraphNodeAttachProviderConfigSchema, or // GraphNodeAttachProvisionerSchema, looks up the needed schemas for each @@ -61,6 +74,8 @@ return fmt.Errorf("AttachSchemaTransformer used with nil Plugins") } + refMap := addrs.MakeMap[addrs.ConfigResource, *configschema.Block]() + for _, v := range g.Vertices() { if tv, ok := v.(GraphNodeAttachResourceSchema); ok { @@ -80,6 +95,8 @@ } log.Printf("[TRACE] AttachSchemaTransformer: attaching resource schema to %s", dag.VertexName(v)) tv.AttachResourceSchema(schema, version) + + refMap.Put(addr, schema) } if tv, ok := v.(GraphNodeAttachProviderConfigSchema); ok { @@ -113,6 +130,31 @@ } } } + + for _, v := range g.Vertices() { + if tv, ok := v.(GraphNodeAttachReplaceTriggeredBySchema); ok { + module := tv.ResourceAddr().Module + refs := tv.ReplaceTriggeredBy() + for _, ref := range refs { + var refAddr addrs.ConfigResource + switch rs := ref.Subject.(type) { + case addrs.Resource: + refAddr = rs.InModule(module) + case addrs.ResourceInstance: + refAddr = rs.Resource.InModule(module) + default: + continue + } + schema, ok := refMap.GetOk(refAddr) + if ok { + log.Printf("[TRACE] AttachSchemaTransformer: attaching replace_triggered_by %q config schema to %s", refAddr, dag.VertexName(v)) + tv.AttachReplaceTriggeredBySchema(refAddr.Resource, schema) + } else { + log.Printf("[WARN] AttachSchemaTransformer: missing replace_triggered_by %q in %s", refAddr, dag.VertexName(v)) + } + } + } + } return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/opentofu-1.12.0/version/VERSION new/opentofu-1.12.1/version/VERSION --- old/opentofu-1.12.0/version/VERSION 2026-05-14 11:57:57.000000000 +0200 +++ new/opentofu-1.12.1/version/VERSION 2026-05-27 10:51:46.000000000 +0200 @@ -1 +1 @@ -1.12.0 +1.12.1 ++++++ opentofu.obsinfo ++++++ --- /var/tmp/diff_new_pack.J8wOX5/_old 2026-05-29 18:10:29.226811216 +0200 +++ /var/tmp/diff_new_pack.J8wOX5/_new 2026-05-29 18:10:29.274813282 +0200 @@ -1,5 +1,5 @@ name: opentofu -version: 1.12.0 -mtime: 1778752677 -commit: bdcbf091aab886499ed7718d7f558b428fffce39 +version: 1.12.1 +mtime: 1779871906 +commit: dc29882638869cdc60f871a112a04a415042caf1 ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/opentofu/vendor.tar.gz /work/SRC/openSUSE:Factory/.opentofu.new.1937/vendor.tar.gz differ: char 13, line 1
