Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package terragrunt for openSUSE:Factory 
checked in at 2025-07-06 17:06:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/terragrunt (Old)
 and      /work/SRC/openSUSE:Factory/.terragrunt.new.1903 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "terragrunt"

Sun Jul  6 17:06:05 2025 rev:242 rq:1290070 version:0.82.4

Changes:
--------
--- /work/SRC/openSUSE:Factory/terragrunt/terragrunt.changes    2025-06-30 
14:02:04.612647206 +0200
+++ /work/SRC/openSUSE:Factory/.terragrunt.new.1903/terragrunt.changes  
2025-07-06 17:08:41.872911873 +0200
@@ -1,0 +2,43 @@
+Thu Jul 03 04:50:12 UTC 2025 - Johannes Kastl 
<opensuse_buildserv...@ojkastl.de>
+
+- Update to version 0.82.4:
+  * New Features
+    - Support for OpenTofu 1.10 Native S3 Locking
+      The remote_state S3 backend now integrates natively with the
+      OpenTofu 1.10 feature of state locking via S3 without the
+      usage of DynamoDB using the new use_lockfile attribute.
+
+        # Configure OpenTofu/Terraform state to be stored in S3
+        with native S3 locking instead of DynamoDB.
+        # This uses S3 object conditional writes for state locking,
+        which requires OpenTofu >= 1.10.
+        remote_state {
+          backend = "s3"
+          config = {
+            bucket       = "my-tofu-state"
+            key          = "${path_relative_to_include()}/tofu.tfstate"
+            region       = "us-east-1"
+            encrypt      = true
+            use_lockfile = true
+          }
+        }
+
+      In previous releases, if users wanted to integrate with this
+      OpenTofu feature, they would have to use the generate
+      attribute, which opts users out of more advanced features of
+      Terragrunt remote state management like automatic
+      provisioning of state resources.
+      By using the native attribute in config, users can retain the
+      benefits of automatic backend bootstrapping in addition to
+      native integration with the new OpenTofu feature.
+      You can learn more about backend configurations in the HCL
+      docs.
+      
https://terragrunt.gruntwork.io/docs/reference/config-blocks-and-attributes/#backend
+  * What's Changed
+    - feat: Adding support for native state locking (#4485)
+    - chore: update external dependencies (#4486)
+    - fix: Use a constant for min version of tofu for auto provider
+      cache dir (#4479)
+    - fix: resolve failing CAS & DAG tests (#4480)
+
+-------------------------------------------------------------------

Old:
----
  terragrunt-0.82.3.obscpio

New:
----
  terragrunt-0.82.4.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ terragrunt.spec ++++++
--- /var/tmp/diff_new_pack.xwPnAr/_old  2025-07-06 17:08:44.453018634 +0200
+++ /var/tmp/diff_new_pack.xwPnAr/_new  2025-07-06 17:08:44.457018799 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           terragrunt
-Version:        0.82.3
+Version:        0.82.4
 Release:        0
 Summary:        Thin wrapper for Terraform for working with multiple Terraform 
modules
 License:        MIT

++++++ _service ++++++
--- /var/tmp/diff_new_pack.xwPnAr/_old  2025-07-06 17:08:44.529021779 +0200
+++ /var/tmp/diff_new_pack.xwPnAr/_new  2025-07-06 17:08:44.533021944 +0200
@@ -3,7 +3,7 @@
     <param name="url">https://github.com/gruntwork-io/terragrunt</param>
     <param name="scm">git</param>
     <param name="exclude">.git</param>
-    <param name="revision">v0.82.3</param>
+    <param name="revision">v0.82.4</param>
     <param name="versionformat">@PARENT_TAG@</param>
     <param name="versionrewrite-pattern">v(.*)</param>
     <param name="changesgenerate">enable</param>

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.xwPnAr/_old  2025-07-06 17:08:44.553022772 +0200
+++ /var/tmp/diff_new_pack.xwPnAr/_new  2025-07-06 17:08:44.557022937 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param 
name="url">https://github.com/gruntwork-io/terragrunt</param>
-              <param 
name="changesrevision">a31ef0943c02cd68b648c81a0d1ed037a57e24e9</param></service></servicedata>
+              <param 
name="changesrevision">34af1f1518c336c96623fd3c6eb63c53b9afa7eb</param></service></servicedata>
 (No newline at EOF)
 

++++++ terragrunt-0.82.3.obscpio -> terragrunt-0.82.4.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/cli/commands/commands.go 
new/terragrunt-0.82.4/cli/commands/commands.go
--- old/terragrunt-0.82.3/cli/commands/commands.go      2025-06-27 
15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/cli/commands/commands.go      2025-07-02 
15:24:07.000000000 +0200
@@ -191,6 +191,8 @@
        return errGroup.Wait()
 }
 
+const minTofuVersionForAutoProviderCacheDir = "1.10.0"
+
 // setupAutoProviderCacheDir configures native provider caching by setting 
TF_PLUGIN_CACHE_DIR.
 //
 // Only works with OpenTofu version >= 1.10. Returns error if conditions 
aren't met.
@@ -222,7 +224,7 @@
                return errors.New("cannot determine OpenTofu version")
        }
 
-       requiredVersion, err := version.NewVersion("1.10.0")
+       requiredVersion, err := 
version.NewVersion(minTofuVersionForAutoProviderCacheDir)
        if err != nil {
                return fmt.Errorf("failed to parse required version: %w", err)
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/terragrunt-0.82.3/docs/_docs/04_reference/04-config-blocks-and-attributes.md
 
new/terragrunt-0.82.4/docs/_docs/04_reference/04-config-blocks-and-attributes.md
--- 
old/terragrunt-0.82.3/docs/_docs/04_reference/04-config-blocks-and-attributes.md
    2025-06-27 15:59:16.000000000 +0200
+++ 
new/terragrunt-0.82.4/docs/_docs/04_reference/04-config-blocks-and-attributes.md
    2025-07-02 15:24:07.000000000 +0200
@@ -459,6 +459,7 @@
 - `external_id` - (Optional) The external ID to use when assuming the role.
 - `session_name` - (Optional) The session name to use when assuming the role.
 - `dynamodb_table` - (Optional) The name of a DynamoDB table to use for state 
locking and consistency. The table must have a primary key named LockID. If not 
present, locking will be disabled.
+- `use_lockfile` - (Optional) When `true`, enables native S3 locking using S3 
object conditional writes for state locking. This feature requires OpenTofu >= 
1.10. Can be used simultaneously with `dynamodb_table` during migration (both 
locks must be acquired successfully), but typically used as a replacement for 
DynamoDB locking.
 - `skip_bucket_versioning`: When `true`, the S3 bucket that is created to 
store the state will not be versioned.
 - `skip_bucket_ssencryption`: When `true`, the S3 bucket that is created to 
store the state will not be configured with server-side encryption.
 - `skip_bucket_accesslogging`: *DEPRECATED* If provided, will be ignored. A 
log warning will be issued in the console output to notify the user.
@@ -601,6 +602,44 @@
 }
 ```
 
+Example with S3 using native S3 locking (OpenTofu >= 1.10):
+
+```hcl
+# Configure OpenTofu/Terraform state to be stored in S3 with native S3 locking 
instead of DynamoDB.
+# This uses S3 object conditional writes for state locking, which requires 
OpenTofu >= 1.10.
+remote_state {
+  backend = "s3"
+  config = {
+    bucket       = "my-tofu-state"
+    key          = "${path_relative_to_include()}/tofu.tfstate"
+    region       = "us-east-1"
+    encrypt      = true
+    use_lockfile = true
+  }
+}
+```
+
+Example with S3 using both DynamoDB and native S3 locking during migration 
(OpenTofu >= 1.10):
+
+```hcl
+# Configure OpenTofu/Terraform state with dual locking during migration from 
DynamoDB to S3 native locking.
+# Both locks must be successfully acquired before operations can proceed.
+# After the migration period, remove dynamodb_table to use only S3 native 
locking.
+# Note: This won't delete the DynamoDB table, it will just be unused.
+# You can delete it manually after the migration period.
+remote_state {
+  backend = "s3"
+  config = {
+    bucket         = "my-tofu-state"
+    key            = "${path_relative_to_include()}/tofu.tfstate"
+    region         = "us-east-1"
+    encrypt        = true
+    dynamodb_table = "my-lock-table"  # Remove this after migration period
+    use_lockfile   = true             # New native S3 locking
+  }
+}
+```
+
 #### encryption
 
 The encryption map needs a `key_provider` property, which can be set to one of 
`pbkdf2`, `aws_kms` or `gcp_kms`.
@@ -1365,7 +1404,7 @@
   allowed_account_ids = ["1234567890"]
 }
 EOF
-}
+  }
 ```
 
 Then in a `terragrunt.hcl` file, you could dynamically set `generate` as an 
attribute as follows:
@@ -1978,8 +2017,8 @@
 
 **DEPRECATED: Use [exclude](#exclude) instead.**
 
-The terragrunt `skip` boolean flag can be used to protect modules you don’t 
want any changes to or just to skip modules
-that don’t define any infrastructure by themselves. When set to true, all 
terragrunt commands will skip the selected
+The terragrunt `skip` boolean flag can be used to protect modules you don't 
want any changes to or just to skip modules
+that don't define any infrastructure by themselves. When set to true, all 
terragrunt commands will skip the selected
 module.
 
 Consider the following file structure:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/terragrunt-0.82.3/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx
 
new/terragrunt-0.82.4/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx
--- 
old/terragrunt-0.82.3/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx
     2025-06-27 15:59:16.000000000 +0200
+++ 
new/terragrunt-0.82.4/docs-starlight/src/content/docs/04-reference/01-hcl/02-blocks.mdx
     2025-07-02 15:24:07.000000000 +0200
@@ -405,6 +405,7 @@
 - `external_id` - (Optional) The external ID to use when assuming the role.
 - `session_name` - (Optional) The session name to use when assuming the role.
 - `dynamodb_table` - (Optional) The name of a DynamoDB table to use for state 
locking and consistency. The table must have a primary key named LockID. If not 
present, locking will be disabled.
+- `use_lockfile` - (Optional) When `true`, enables native S3 locking using S3 
object conditional writes for state locking. This feature requires OpenTofu >= 
1.10. Can be used simultaneously with `dynamodb_table` during migration (both 
locks must be acquired successfully), but typically used as a replacement for 
DynamoDB locking.
 - `skip_bucket_versioning`: When `true`, the S3 bucket that is created to 
store the state will not be versioned.
 - `skip_bucket_ssencryption`: When `true`, the S3 bucket that is created to 
store the state will not be configured with server-side encryption.
 - `skip_bucket_accesslogging`: *DEPRECATED* If provided, will be ignored. A 
log warning will be issued in the console output to notify the user.
@@ -555,6 +556,44 @@
 }
 ```
 
+Example with S3 using native S3 locking (OpenTofu >= 1.10):
+
+```hcl
+# Configure OpenTofu/Terraform state to be stored in S3 with native S3 locking 
instead of DynamoDB.
+# This uses S3 object conditional writes for state locking, which requires 
OpenTofu >= 1.10.
+remote_state {
+  backend = "s3"
+  config = {
+    bucket       = "my-tofu-state"
+    key          = "${path_relative_to_include()}/tofu.tfstate"
+    region       = "us-east-1"
+    encrypt      = true
+    use_lockfile = true
+  }
+}
+```
+
+Example with S3 using both DynamoDB and native S3 locking during migration 
(OpenTofu >= 1.10):
+
+```hcl
+# Configure OpenTofu/Terraform state with dual locking during migration from 
DynamoDB to S3 native locking.
+# Both locks must be successfully acquired before operations can proceed.
+# After the migration period, remove dynamodb_table to use only S3 native 
locking.
+# Note: This won't delete the DynamoDB table, it will just be unused.
+# You can delete it manually after the migration period.
+remote_state {
+  backend = "s3"
+  config = {
+    bucket         = "my-tofu-state"
+    key            = "${path_relative_to_include()}/tofu.tfstate"
+    region         = "us-east-1"
+    encrypt        = true
+    dynamodb_table = "my-lock-table"  # Remove this after migration period
+    use_lockfile   = true             # New native S3 locking
+  }
+}
+```
+
 ### encryption
 
 The encryption map needs a `key_provider` property, which can be set to one of 
`pbkdf2`, `aws_kms` or `gcp_kms`.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/go.mod new/terragrunt-0.82.4/go.mod
--- old/terragrunt-0.82.3/go.mod        2025-06-27 15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/go.mod        2025-07-02 15:24:07.000000000 +0200
@@ -10,9 +10,9 @@
        github.com/aws/aws-sdk-go v1.55.7
        github.com/aws/aws-sdk-go-v2 v1.36.5
        github.com/charmbracelet/bubbles v0.21.0
-       github.com/charmbracelet/bubbletea v1.3.4
+       github.com/charmbracelet/bubbletea v1.3.5
        github.com/charmbracelet/glamour v0.8.0
-       github.com/charmbracelet/lipgloss v1.1.0
+       github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834
        github.com/creack/pty v1.1.24
        github.com/fatih/structs v1.1.0
        github.com/getsops/sops/v3 v3.10.2
@@ -58,17 +58,17 @@
        github.com/terraform-linters/tflint v0.55.0
        github.com/urfave/cli/v2 v2.27.7
        github.com/zclconf/go-cty v1.16.3
-       go.opentelemetry.io/otel v1.36.0
-       go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc 
v1.36.0
-       go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp 
v1.36.0
-       go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0
-       go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0
-       go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0
-       go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0
-       go.opentelemetry.io/otel/metric v1.36.0
-       go.opentelemetry.io/otel/sdk v1.36.0
-       go.opentelemetry.io/otel/sdk/metric v1.36.0
-       go.opentelemetry.io/otel/trace v1.36.0
+       go.opentelemetry.io/otel v1.37.0
+       go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc 
v1.37.0
+       go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp 
v1.37.0
+       go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0
+       go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0
+       go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0
+       go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0
+       go.opentelemetry.io/otel/metric v1.37.0
+       go.opentelemetry.io/otel/sdk v1.37.0
+       go.opentelemetry.io/otel/sdk/metric v1.37.0
+       go.opentelemetry.io/otel/trace v1.37.0
        golang.org/x/mod v0.25.0
        golang.org/x/oauth2 v0.30.0
        golang.org/x/sync v0.15.0
@@ -82,10 +82,11 @@
 )
 
 require (
-       github.com/aws/aws-sdk-go-v2/service/s3 v1.81.0
+       github.com/aws/aws-sdk-go-v2/service/s3 v1.82.0
        github.com/charmbracelet/x/exp/teatest 
v0.0.0-20250611152503-f53cdd7e01ef
        github.com/charmbracelet/x/term v0.2.1
        github.com/invopop/jsonschema v0.13.0
+       github.com/wI2L/jsondiff v0.7.0
        github.com/xeipuuv/gojsonschema v1.2.0
        go.uber.org/mock v0.5.2
        golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b
@@ -206,7 +207,7 @@
        github.com/gookit/color v1.5.4 // indirect
        github.com/gorilla/css v1.0.1 // indirect
        github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // 
indirect
-       github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.0 // indirect
+       github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
        github.com/hashicorp/errwrap v1.1.0 // indirect
        github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
        github.com/hashicorp/go-rootcerts v1.0.2 // indirect
@@ -268,6 +269,10 @@
        github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
        github.com/terraform-linters/tflint-plugin-sdk v0.22.0 // indirect
        github.com/terraform-linters/tflint-ruleset-terraform v0.10.0 // 
indirect
+       github.com/tidwall/gjson v1.18.0 // indirect
+       github.com/tidwall/match v1.1.1 // indirect
+       github.com/tidwall/pretty v1.2.1 // indirect
+       github.com/tidwall/sjson v1.2.5 // indirect
        github.com/ulikunitz/xz v0.5.12 // indirect
        github.com/urfave/cli v1.22.16 // indirect
        github.com/valyala/bytebufferpool v1.0.0 // indirect
@@ -287,7 +292,7 @@
        go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
        
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.61.0 // indirect
        go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 
// indirect
-       go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 // indirect
+       go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
        go.opentelemetry.io/proto/otlp v1.7.0 // indirect
        golang.org/x/crypto v0.39.0 // indirect
        golang.org/x/net v0.41.0 // indirect
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/go.sum new/terragrunt-0.82.4/go.sum
--- old/terragrunt-0.82.3/go.sum        2025-06-27 15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/go.sum        2025-07-02 15:24:07.000000000 +0200
@@ -837,8 +837,8 @@
 github.com/aws/aws-sdk-go-v2/service/rds v1.91.0/go.mod 
h1:h2jc7IleH3xHY7y+h8FH7WAZcz3IVLOB6/jXotIQ/qU=
 github.com/aws/aws-sdk-go-v2/service/route53 v1.46.2 
h1:wmt05tPp/CaRZpPV5B4SaJ5TwkHKom07/BzHoLdkY1o=
 github.com/aws/aws-sdk-go-v2/service/route53 v1.46.2/go.mod 
h1:d+K9HESMpGb1EU9/UmmpInbGIUcAkwmcY6ZO/A3zZsw=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.81.0 
h1:1GmCadhKR3J2sMVKs2bAYq9VnwYeCqfRyZzD4RASGlA=
-github.com/aws/aws-sdk-go-v2/service/s3 v1.81.0/go.mod 
h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.82.0 
h1:JubM8CGDDFaAOmBrd8CRYNr49ZNgEAiLwGwgNMdS0nw=
+github.com/aws/aws-sdk-go-v2/service/s3 v1.82.0/go.mod 
h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU=
 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.6 
h1:1KDMKvOKNrpD667ORbZ/+4OgvUoaok1gg/MLzrHF9fw=
 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.6/go.mod 
h1:DmtyfCfONhOyVAJ6ZMTrDSFIeyCBlEO93Qkfhxwbxu0=
 github.com/aws/aws-sdk-go-v2/service/sns v1.33.6 
h1:lEUtRHICiXsd7VRwRjXaY7MApT2X4Ue0Mrwe6XbyBro=
@@ -896,14 +896,14 @@
 github.com/cespare/xxhash/v2 v2.3.0/go.mod 
h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/charmbracelet/bubbles v0.21.0 
h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
 github.com/charmbracelet/bubbles v0.21.0/go.mod 
h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
-github.com/charmbracelet/bubbletea v1.3.4 
h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
-github.com/charmbracelet/bubbletea v1.3.4/go.mod 
h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
+github.com/charmbracelet/bubbletea v1.3.5 
h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
+github.com/charmbracelet/bubbletea v1.3.5/go.mod 
h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
 github.com/charmbracelet/colorprofile v0.3.0 
h1:KtLh9uuu1RCt+Hml4s6Hz+kB1PfV3wi++1h5ia65yKQ=
 github.com/charmbracelet/colorprofile v0.3.0/go.mod 
h1:oHJ340RS2nmG1zRGPmhJKJ/jf4FPNNk0P39/wBPA1G0=
 github.com/charmbracelet/glamour v0.8.0 
h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs=
 github.com/charmbracelet/glamour v0.8.0/go.mod 
h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw=
-github.com/charmbracelet/lipgloss v1.1.0 
h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
-github.com/charmbracelet/lipgloss v1.1.0/go.mod 
h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
+github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 
h1:ZR7e0ro+SZZiIZD7msJyA+NjkCNNavuiPBLgerbOziE=
+github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834/go.mod 
h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
 github.com/charmbracelet/x/ansi v0.8.0 
h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
 github.com/charmbracelet/x/ansi v0.8.0/go.mod 
h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
 github.com/charmbracelet/x/cellbuf v0.0.13 
h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
@@ -1236,8 +1236,8 @@
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod 
h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod 
h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod 
h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.0 
h1:+epNPbD5EqgpEMm5wrl4Hqts3jZt8+kYaqUisuuIGTk=
-github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.0/go.mod 
h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 
h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod 
h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
 github.com/gruntwork-io/boilerplate v0.6.3 
h1:c7gdVH4U/z5ReK9+iCN2v/+MXnsWo6fLTrPiKqL+VQs=
 github.com/gruntwork-io/boilerplate v0.6.3/go.mod 
h1:eAuKtP/udtyVE0gSQWUzpKK+JpHzFS9th6bhlR8OqhA=
 github.com/gruntwork-io/go-commons v0.17.2 
h1:14dsCJ7M5Vv2X3BIPKeG9Kdy6vTMGhM8L4WZazxfTuY=
@@ -1689,6 +1689,16 @@
 github.com/terraform-linters/tflint-plugin-sdk v0.22.0/go.mod 
h1:Cag3YJjBpHdQzI/limZR+Cj7WYPLTIE61xsCdIXoeUI=
 github.com/terraform-linters/tflint-ruleset-terraform v0.10.0 
h1:L+3K3oGvZe5UdQ9F6PMQ6n69A2+Q11dBSg+5nTvxJi8=
 github.com/terraform-linters/tflint-ruleset-terraform v0.10.0/go.mod 
h1:wT8nMRBpCg1cIL0Td3LQ3XPcnTTHwBhbCNrFp4jWFrI=
+github.com/tidwall/gjson v1.14.2/go.mod 
h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 
h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod 
h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
+github.com/tidwall/match v1.1.1/go.mod 
h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
+github.com/tidwall/pretty v1.2.0/go.mod 
h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 
h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod 
h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
+github.com/tidwall/sjson v1.2.5/go.mod 
h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod 
h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tombuildsstuff/giovanni v0.15.1/go.mod 
h1:0TZugJPEtqzPlMpuJHYfXY6Dq2uLPrXf98D2XQSxNbA=
 github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod 
h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
@@ -1711,6 +1721,8 @@
 github.com/vmihailenco/tagparser v0.1.1/go.mod 
h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
 github.com/vmihailenco/tagparser/v2 v2.0.0 
h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod 
h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
+github.com/wI2L/jsondiff v0.7.0 h1:1lH1G37GhBPqCfp/lrs91rf/2j3DktX6qYAKZkLuCQQ=
+github.com/wI2L/jsondiff v0.7.0/go.mod 
h1:KAEIojdQq66oJiHhDyQez2x+sRit0vIzC9KeK0yizxM=
 github.com/wk8/go-ordered-map/v2 v2.1.8 
h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
 github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod 
h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
 github.com/xanzy/ssh-agent v0.2.1/go.mod 
h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
@@ -1774,30 +1786,30 @@
 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 
v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 
h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod 
h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
-go.opentelemetry.io/otel v1.36.0 
h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
-go.opentelemetry.io/otel v1.36.0/go.mod 
h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.36.0 
h1:zwdo1gS2eH26Rg+CoqVQpEK1h8gvt5qyU5Kk5Bixvow=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc 
v1.36.0/go.mod h1:rUKCPscaRWWcqGT6HnEmYrK+YNe5+Sw64xgQTOJ5b30=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.36.0 
h1:gAU726w9J8fwr4qRDqu1GYMNNs4gXrU+Pv20/N1UpB4=
-go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp 
v1.36.0/go.mod h1:RboSDkp7N292rgu+T0MgVt2qgFGu6qa1RpZDOtpL76w=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0 
h1:dNzwXjZKpMpE2JhmO+9HsPl42NIXFIFSUSSs0fiqra0=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.36.0/go.mod 
h1:90PoxvaEB5n6AOdZvi+yWJQoE95U8Dhhw2bSyRqnTD0=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0 
h1:JgtbA0xkWHnTmYk7YusopJFX6uleBmAuZ8n05NEh8nQ=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.36.0/go.mod 
h1:179AK5aar5R3eS9FucPy6rggvU0g52cvKId8pv4+v0c=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0 
h1:nRVXXvf78e00EwY6Wp0YII8ww2JVWshZ20HfTlE11AM=
-go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.36.0/go.mod 
h1:r49hO7CgrxY9Voaj3Xe8pANWtr0Oq916d0XAmOoCZAQ=
-go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 
h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
-go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod 
h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0 
h1:G8Xec/SgZQricwWBJF/mHZc7A02YHedfFDENwJEdRA0=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.36.0/go.mod 
h1:PD57idA/AiFD5aqoxGxCvT/ILJPeHy3MjqU/NS7KogY=
-go.opentelemetry.io/otel/metric v1.36.0 
h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
-go.opentelemetry.io/otel/metric v1.36.0/go.mod 
h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
-go.opentelemetry.io/otel/sdk v1.36.0 
h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
-go.opentelemetry.io/otel/sdk v1.36.0/go.mod 
h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
-go.opentelemetry.io/otel/sdk/metric v1.36.0 
h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
-go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod 
h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
-go.opentelemetry.io/otel/trace v1.36.0 
h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
-go.opentelemetry.io/otel/trace v1.36.0/go.mod 
h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
+go.opentelemetry.io/otel v1.37.0 
h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
+go.opentelemetry.io/otel v1.37.0/go.mod 
h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.37.0 
h1:zG8GlgXCJQd5BU98C0hZnBbElszTmUgCNCfYneaDL0A=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc 
v1.37.0/go.mod h1:hOfBCz8kv/wuq73Mx2H2QnWokh/kHZxkh6SNF2bdKtw=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 
h1:9PgnL3QNlj10uGxExowIDIZu66aVBwWhXmbOp1pa6RA=
+go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp 
v1.37.0/go.mod h1:0ineDcLELf6JmKfuo0wvvhAVMuxWFYvkTin2iV4ydPQ=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 
h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod 
h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 
h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod 
h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 
h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod 
h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
+go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0 
h1:6VjV6Et+1Hd2iLZEPtdV7vie80Yyqf7oikJLjQ/myi0=
+go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.37.0/go.mod 
h1:u8hcp8ji5gaM/RfcOo8z9NMnf1pVLfVY7lBY2VOGuUU=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 
h1:SNhVp/9q4Go/XHBkQ1/d5u9P/U+L1yaGPoi0x+mStaI=
+go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0/go.mod 
h1:tx8OOlGH6R4kLV67YaYO44GFXloEjGPZuMjEkaaqIp4=
+go.opentelemetry.io/otel/metric v1.37.0 
h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
+go.opentelemetry.io/otel/metric v1.37.0/go.mod 
h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
+go.opentelemetry.io/otel/sdk v1.37.0 
h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
+go.opentelemetry.io/otel/sdk v1.37.0/go.mod 
h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
+go.opentelemetry.io/otel/sdk/metric v1.37.0 
h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
+go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod 
h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
+go.opentelemetry.io/otel/trace v1.37.0 
h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
+go.opentelemetry.io/otel/trace v1.37.0/go.mod 
h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod 
h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.opentelemetry.io/proto/otlp v0.15.0/go.mod 
h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
 go.opentelemetry.io/proto/otlp v0.19.0/go.mod 
h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/internal/cas/cas.go 
new/terragrunt-0.82.4/internal/cas/cas.go
--- old/terragrunt-0.82.3/internal/cas/cas.go   2025-06-27 15:59:16.000000000 
+0200
+++ new/terragrunt-0.82.4/internal/cas/cas.go   2025-07-02 15:24:07.000000000 
+0200
@@ -63,6 +63,10 @@
                opts.StorePath = filepath.Join(home, ".cache", "terragrunt", 
"cas", "store")
        }
 
+       if err := os.MkdirAll(opts.StorePath, DefaultDirPerms); err != nil {
+               return nil, fmt.Errorf("failed to create CAS store path: %w", 
err)
+       }
+
        store := NewStore(opts.StorePath)
 
        git, err := NewGitRunner()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/terragrunt-0.82.3/internal/remotestate/backend/s3/backend_test.go 
new/terragrunt-0.82.4/internal/remotestate/backend/s3/backend_test.go
--- old/terragrunt-0.82.3/internal/remotestate/backend/s3/backend_test.go       
2025-06-27 15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/internal/remotestate/backend/s3/backend_test.go       
2025-07-02 15:24:07.000000000 +0200
@@ -138,6 +138,56 @@
                        },
                        true,
                },
+               {
+                       "use-lockfile-native-s3-locking",
+                       backend.Config{
+                               "bucket":       "foo",
+                               "key":          "bar",
+                               "region":       "us-east-1",
+                               "use_lockfile": true,
+                       },
+                       map[string]any{
+                               "bucket":       "foo",
+                               "key":          "bar",
+                               "region":       "us-east-1",
+                               "use_lockfile": true,
+                       },
+                       true,
+               },
+               {
+                       "use-lockfile-false",
+                       backend.Config{
+                               "bucket":       "foo",
+                               "key":          "bar",
+                               "region":       "us-east-1",
+                               "use_lockfile": false,
+                       },
+                       map[string]any{
+                               "bucket":       "foo",
+                               "key":          "bar",
+                               "region":       "us-east-1",
+                               "use_lockfile": false,
+                       },
+                       true,
+               },
+               {
+                       "dual-locking-dynamodb-and-s3",
+                       backend.Config{
+                               "bucket":         "foo",
+                               "key":            "bar",
+                               "region":         "us-east-1",
+                               "dynamodb_table": "my-lock-table",
+                               "use_lockfile":   true,
+                       },
+                       map[string]any{
+                               "bucket":         "foo",
+                               "key":            "bar",
+                               "region":         "us-east-1",
+                               "dynamodb_table": "my-lock-table",
+                               "use_lockfile":   true,
+                       },
+                       true,
+               },
        }
 
        for _, tc := range testCases {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/terragrunt-0.82.3/internal/remotestate/backend/s3/remote_state_config.go 
new/terragrunt-0.82.4/internal/remotestate/backend/s3/remote_state_config.go
--- 
old/terragrunt-0.82.3/internal/remotestate/backend/s3/remote_state_config.go    
    2025-06-27 15:59:16.000000000 +0200
+++ 
new/terragrunt-0.82.4/internal/remotestate/backend/s3/remote_state_config.go    
    2025-07-02 15:24:07.000000000 +0200
@@ -189,6 +189,7 @@
        AssumeRole       RemoteStateConfigS3AssumeRole 
`mapstructure:"assume_role"`
        Encrypt          bool                          `mapstructure:"encrypt"`
        S3ForcePathStyle bool                          
`mapstructure:"force_path_style"`
+       UseLockfile      bool                          
`mapstructure:"use_lockfile"`
 }
 
 // CacheKey returns a unique key for the given S3 config that can be used to 
cache the initialization
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/telemetry/meter.go 
new/terragrunt-0.82.4/telemetry/meter.go
--- old/terragrunt-0.82.3/telemetry/meter.go    2025-06-27 15:59:16.000000000 
+0200
+++ new/terragrunt-0.82.4/telemetry/meter.go    2025-07-02 15:24:07.000000000 
+0200
@@ -16,7 +16,7 @@
        "go.opentelemetry.io/otel/sdk/metric"
 
        "go.opentelemetry.io/otel/sdk/resource"
-       semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
+       semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
 )
 
 const (
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/telemetry/tracer.go 
new/terragrunt-0.82.4/telemetry/tracer.go
--- old/terragrunt-0.82.3/telemetry/tracer.go   2025-06-27 15:59:16.000000000 
+0200
+++ new/terragrunt-0.82.4/telemetry/tracer.go   2025-07-02 15:24:07.000000000 
+0200
@@ -16,7 +16,7 @@
        "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/sdk/resource"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
-       semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
+       semconv "go.opentelemetry.io/otel/semconv/v1.34.0"
        "go.opentelemetry.io/otel/trace"
 )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/terragrunt-0.82.3/test/fixtures/s3-backend/dual-locking/terragrunt.hcl 
new/terragrunt-0.82.4/test/fixtures/s3-backend/dual-locking/terragrunt.hcl
--- old/terragrunt-0.82.3/test/fixtures/s3-backend/dual-locking/terragrunt.hcl  
1970-01-01 01:00:00.000000000 +0100
+++ new/terragrunt-0.82.4/test/fixtures/s3-backend/dual-locking/terragrunt.hcl  
2025-07-02 15:24:07.000000000 +0200
@@ -0,0 +1,22 @@
+# Configure OpenTofu/Terraform state to be stored in S3 with DUAL locking 
(migration scenario)
+# This uses both DynamoDB and S3 native locking simultaneously
+# Both locks must be successfully acquired before operations can proceed
+remote_state {
+  backend = "s3"
+  generate = {
+    path      = "backend.tf"
+    if_exists = "overwrite"
+  }
+  config = {
+    bucket         = "__FILL_IN_BUCKET_NAME__"
+    key            = "dual-locking/terraform.tfstate"
+    region         = "__FILL_IN_REGION__"
+    encrypt        = true
+    dynamodb_table = "__FILL_IN_LOCK_TABLE_NAME__"  # Traditional DynamoDB 
locking
+    use_lockfile   = true                            # New S3 native locking
+  }
+}
+
+terraform {
+  source = 
"tfr://registry.terraform.io/yorinasub17/terragrunt-registry-test/null//modules/one?version=0.0.2"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/terragrunt-0.82.3/test/fixtures/s3-backend/use-lockfile/terragrunt.hcl 
new/terragrunt-0.82.4/test/fixtures/s3-backend/use-lockfile/terragrunt.hcl
--- old/terragrunt-0.82.3/test/fixtures/s3-backend/use-lockfile/terragrunt.hcl  
1970-01-01 01:00:00.000000000 +0100
+++ new/terragrunt-0.82.4/test/fixtures/s3-backend/use-lockfile/terragrunt.hcl  
2025-07-02 15:24:07.000000000 +0200
@@ -0,0 +1,20 @@
+# Configure OpenTofu/Terraform state to be stored in S3 with native S3 locking
+# This uses S3 object conditional writes for state locking, which requires 
OpenTofu >= 1.10
+remote_state {
+  backend = "s3"
+  generate = {
+    path      = "backend.tf"
+    if_exists = "overwrite"
+  }
+  config = {
+    bucket       = "__FILL_IN_BUCKET_NAME__"
+    key          = "use-lockfile/terraform.tfstate"
+    region       = "__FILL_IN_REGION__"
+    encrypt      = true
+    use_lockfile = true
+  }
+}
+
+terraform {
+  source = 
"tfr://registry.terraform.io/yorinasub17/terragrunt-registry-test/null//modules/one?version=0.0.2"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/test/helpers/package.go 
new/terragrunt-0.82.4/test/helpers/package.go
--- old/terragrunt-0.82.3/test/helpers/package.go       2025-06-27 
15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/test/helpers/package.go       2025-07-02 
15:24:07.000000000 +0200
@@ -73,6 +73,8 @@
        allPermissions       = 0777
 
        caKeyBits = 4096
+
+       semverPartsLen = 3
 )
 
 type TerraformOutput struct {
@@ -208,39 +210,87 @@
 
        t.Logf("Deleting test s3 bucket %s", bucketName)
 
-       out, err := 
client.ListObjectVersions(&s3.ListObjectVersionsInput{Bucket: 
aws.String(bucketName)})
-       if err != nil {
-               t.Logf("Failed to list object versions in s3 bucket %s: %v", 
bucketName, err)
-               return err
-       }
+       cleanS3Bucket(t, client, bucketName)
 
-       objectIdentifiers := []*s3.ObjectIdentifier{}
-       for _, version := range out.Versions {
-               objectIdentifiers = append(objectIdentifiers, 
&s3.ObjectIdentifier{
-                       Key:       version.Key,
-                       VersionId: version.VersionId,
-               })
-       }
+       if _, err := client.DeleteBucket(&s3.DeleteBucketInput{Bucket: 
aws.String(bucketName)}); err != nil {
+               t.Logf("Failed to delete S3 bucket %s: %v", bucketName, err)
 
-       if len(objectIdentifiers) > 0 {
-               deleteInput := &s3.DeleteObjectsInput{
-                       Bucket: aws.String(bucketName),
-                       Delete: &s3.Delete{Objects: objectIdentifiers},
-               }
-               if _, err := client.DeleteObjects(deleteInput); err != nil {
-                       t.Logf("Error deleting all versions of all objects in 
bucket %s: %v", bucketName, err)
+               // If the bucket is not empty, try to clean it again before 
deleting it.
+               // This is a workaround for a race condition in eventual 
consistency.
+               // Sleep for a little bit first to give the bucket a chance to 
be ready.
+               time.Sleep(1 * time.Second)
+
+               cleanS3Bucket(t, client, bucketName)
+
+               if _, err = client.DeleteBucket(&s3.DeleteBucketInput{Bucket: 
aws.String(bucketName)}); err != nil {
+                       t.Logf("Failed to delete S3 bucket %s: %v", bucketName, 
err)
                        return err
                }
-       }
 
-       if _, err := client.DeleteBucket(&s3.DeleteBucketInput{Bucket: 
aws.String(bucketName)}); err != nil {
-               t.Logf("Failed to delete S3 bucket %s: %v", bucketName, err)
                return err
        }
 
        return nil
 }
 
+func cleanS3Bucket(t *testing.T, client *s3.S3, bucketName string) {
+       t.Helper()
+
+       t.Logf("Cleaning S3 bucket %s", bucketName)
+
+       // Use pagination to handle large numbers of objects/versions
+       versionsInput := &s3.ListObjectVersionsInput{Bucket: 
aws.String(bucketName)}
+
+       for {
+               out, err := client.ListObjectVersions(versionsInput)
+               require.NoError(t, err)
+
+               objectIdentifiers := []*s3.ObjectIdentifier{}
+
+               // Handle delete markers (created when versioned objects are 
deleted)
+               for _, deleteMarker := range out.DeleteMarkers {
+                       objectIdentifiers = append(objectIdentifiers, 
&s3.ObjectIdentifier{
+                               Key:       deleteMarker.Key,
+                               VersionId: deleteMarker.VersionId,
+                       })
+               }
+
+               // Handle object versions
+               for _, version := range out.Versions {
+                       objectIdentifiers = append(objectIdentifiers, 
&s3.ObjectIdentifier{
+                               Key:       version.Key,
+                               VersionId: version.VersionId,
+                       })
+               }
+
+               // Delete objects in batches (AWS limit is 1000 per request)
+               if len(objectIdentifiers) > 0 {
+                       const maxBatchSize = 1000
+                       for i := 0; i < len(objectIdentifiers); i += 
maxBatchSize {
+                               end := min(i+maxBatchSize, 
len(objectIdentifiers))
+
+                               batch := objectIdentifiers[i:end]
+                               deleteInput := &s3.DeleteObjectsInput{
+                                       Bucket: aws.String(bucketName),
+                                       Delete: &s3.Delete{Objects: batch},
+                               }
+
+                               _, err := client.DeleteObjects(deleteInput)
+                               require.NoError(t, err)
+                       }
+               }
+
+               // Check if there are more objects to process (pagination)
+               if out.IsTruncated == nil || !*out.IsTruncated {
+                       break
+               }
+
+               // Set up for next page
+               versionsInput.KeyMarker = out.NextKeyMarker
+               versionsInput.VersionIdMarker = out.NextVersionIdMarker
+       }
+}
+
 func FileIsInFolder(t *testing.T, name string, path string) bool {
        t.Helper()
 
@@ -669,7 +719,9 @@
 }
 
 // IsTerraform110OrHigher checks if the installed Terraform binary is version 
1.10.0 or higher.
-func IsTerraform110OrHigher() bool {
+func IsTerraform110OrHigher(t *testing.T) bool {
+       t.Helper()
+
        const (
                requiredMajor = 1
                requiredMinor = 10
@@ -680,18 +732,63 @@
        }
 
        output, err := exec.Command(WrappedBinary(), "-version").Output()
-       if err != nil {
-               return false
-       }
+       require.NoError(t, err)
 
        matches := regexp.MustCompile(`Terraform 
v(\d+)\.(\d+)\.`).FindStringSubmatch(string(output))
+       require.Len(t, matches, semverPartsLen, "Expected Terraform version to 
be in the format 'Terraform v1.10.0'")
 
-       major, _ := strconv.Atoi(matches[1])
-       minor, _ := strconv.Atoi(matches[2])
+       major, err := strconv.Atoi(matches[1])
+       require.NoError(t, err)
+
+       minor, err := strconv.Atoi(matches[2])
+       require.NoError(t, err)
 
        return major > requiredMajor || (major == requiredMajor && minor >= 
requiredMinor)
 }
 
+// IsNativeS3LockingSupported checks if the installed Terraform binary 
supports native S3 locking.
+// This is the case when using Terraform 1.11 or higher, or using OpenTofu 
1.10 or higher.
+func IsNativeS3LockingSupported(t *testing.T) bool {
+       t.Helper()
+
+       const (
+               terraformRequiredMajor = 1
+               terraformRequiredMinor = 11
+               tofuRequiredMajor      = 1
+               tofuRequiredMinor      = 10
+       )
+
+       if IsTerraform() {
+               output, err := exec.Command(TerraformBinary, 
"-version").Output()
+               require.NoError(t, err)
+
+               matches := regexp.MustCompile(`Terraform 
v(\d+)\.(\d+)\.`).FindStringSubmatch(string(output))
+               require.Len(t, matches, semverPartsLen, "Expected Terraform 
version to be in the format 'Terraform v1.10.0'")
+
+               major, err := strconv.Atoi(matches[1])
+               require.NoError(t, err)
+
+               minor, err := strconv.Atoi(matches[2])
+               require.NoError(t, err)
+
+               return major > terraformRequiredMajor || (major == 
terraformRequiredMajor && minor >= terraformRequiredMinor)
+       }
+
+       output, err := exec.Command(TofuBinary, "-version").Output()
+       require.NoError(t, err)
+
+       matches := regexp.MustCompile(`OpenTofu 
v(\d+)\.(\d+)\.`).FindStringSubmatch(string(output))
+       require.Len(t, matches, semverPartsLen, "Expected OpenTofu version to 
be in the format 'OpenTofu v1.10.0'")
+
+       major, err := strconv.Atoi(matches[1])
+       require.NoError(t, err)
+
+       minor, err := strconv.Atoi(matches[2])
+       require.NoError(t, err)
+
+       return major > tofuRequiredMajor || (major == tofuRequiredMajor && 
minor >= tofuRequiredMinor)
+}
+
 func FindFilesWithExtension(dir string, ext string) ([]string, error) {
        var files []string
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/test/integration_aws_test.go 
new/terragrunt-0.82.4/test/integration_aws_test.go
--- old/terragrunt-0.82.3/test/integration_aws_test.go  2025-06-27 
15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/test/integration_aws_test.go  2025-07-02 
15:24:07.000000000 +0200
@@ -49,6 +49,8 @@
        testFixtureOutputFromRemoteState             = 
"fixtures/output-from-remote-state"
        testFixtureOutputFromDependency              = 
"fixtures/output-from-dependency"
        testFixtureS3Backend                         = "fixtures/s3-backend"
+       testFixtureS3BackendDualLocking              = 
"fixtures/s3-backend/dual-locking"
+       testFixtureS3BackendUseLockfile              = 
"fixtures/s3-backend/use-lockfile"
        testFixtureAssumeRoleWithExternalIDWithComma = 
"fixtures/assume-role/external-id-with-comma"
 
        qaMyAppRelPath = "qa/my-app"
@@ -116,10 +118,12 @@
                        s3BucketName := "terragrunt-test-bucket-" + testID
                        dynamoDBName := "terragrunt-test-dynamodb-" + testID
 
-                       defer func() {
-                               deleteS3Bucket(t, s3BucketName, 
helpers.TerraformRemoteStateS3Region)
-                               cleanupTableForTest(t, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
-                       }()
+                       if tc.name != "no bootstrap s3 backend without flag" {
+                               defer func() {
+                                       deleteS3Bucket(t, 
helpers.TerraformRemoteStateS3Region, s3BucketName)
+                                       cleanupTableForTest(t, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
+                               }()
+                       }
 
                        commonConfigPath := util.JoinPath(rootPath, 
"common.hcl")
                        helpers.CopyTerragruntConfigAndFillPlaceholders(t, 
commonConfigPath, commonConfigPath, s3BucketName, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
@@ -145,7 +149,7 @@
        dynamoDBName := "terragrunt-test-dynamodb-" + testID
 
        defer func() {
-               deleteS3Bucket(t, s3BucketName, 
helpers.TerraformRemoteStateS3Region)
+               deleteS3Bucket(t, helpers.TerraformRemoteStateS3Region, 
s3BucketName)
                cleanupTableForTest(t, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
        }()
 
@@ -175,6 +179,91 @@
        assert.NotContains(t, stderr, "Use the explicit `--backend-bootstrap` 
flag to automatically provision backend resources before they're needed.")
 }
 
+func TestAwsDualLockingBackend(t *testing.T) {
+       t.Parallel()
+
+       if !helpers.IsNativeS3LockingSupported(t) {
+               t.Skip("Wrapped binary does not support native S3 locking")
+               return
+       }
+
+       helpers.CleanupTerraformFolder(t, testFixtureS3BackendDualLocking)
+       tmpEnvPath := helpers.CopyEnvironment(t, 
testFixtureS3BackendDualLocking)
+       rootPath := util.JoinPath(tmpEnvPath, testFixtureS3BackendDualLocking)
+
+       testID := strings.ToLower(helpers.UniqueID())
+
+       s3BucketName := "terragrunt-test-bucket-" + testID
+       dynamoDBName := "terragrunt-test-dynamodb-" + testID
+
+       defer func() {
+               deleteS3Bucket(t, helpers.TerraformRemoteStateS3Region, 
s3BucketName)
+               cleanupTableForTest(t, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
+       }()
+
+       terragruntConfigPath := util.JoinPath(rootPath, "terragrunt.hcl")
+       helpers.CopyTerragruntConfigAndFillPlaceholders(t, 
terragruntConfigPath, terragruntConfigPath, s3BucketName, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
+
+       // Test backend bootstrap with dual locking
+       stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, 
"terragrunt run apply --backend-bootstrap --non-interactive --log-level debug 
--working-dir "+rootPath+" -- -auto-approve")
+       require.NoError(t, err)
+
+       // Validate both S3 bucket and DynamoDB table are created
+       validateS3BucketExistsAndIsTaggedAndVersioning(t, 
helpers.TerraformRemoteStateS3Region, s3BucketName, true, nil)
+       validateDynamoDBTableExistsAndIsTaggedAndIsSSEncrypted(t, 
helpers.TerraformRemoteStateS3Region, dynamoDBName, nil, false)
+
+       t.Logf("Dual locking test completed successfully. Output: %s, Errors: 
%s", stdout, stderr)
+
+       // Test that subsequent runs work with dual locking (both locks should 
be acquired)
+       stdout2, stderr2, err2 := helpers.RunTerragruntCommandWithOutput(t, 
"terragrunt run plan --non-interactive --log-level debug --working-dir 
"+rootPath)
+       require.NoError(t, err2)
+
+       t.Logf("Dual locking plan test completed successfully. Output: %s, 
Errors: %s", stdout2, stderr2)
+}
+
+func TestAwsNativeS3LockingBackend(t *testing.T) {
+       t.Parallel()
+
+       if !helpers.IsNativeS3LockingSupported(t) {
+               t.Skip("Wrapped binary does not support native S3 locking")
+               return
+       }
+
+       helpers.CleanupTerraformFolder(t, testFixtureS3BackendUseLockfile)
+       tmpEnvPath := helpers.CopyEnvironment(t, 
testFixtureS3BackendUseLockfile)
+       rootPath := util.JoinPath(tmpEnvPath, testFixtureS3BackendUseLockfile)
+
+       testID := strings.ToLower(helpers.UniqueID())
+
+       s3BucketName := "terragrunt-test-bucket-" + testID
+       // Note: No DynamoDB table needed for native S3 locking
+
+       defer func() {
+               deleteS3Bucket(t, helpers.TerraformRemoteStateS3Region, 
s3BucketName)
+               // Note: No DynamoDB cleanup needed for S3 native locking
+       }()
+
+       terragruntConfigPath := util.JoinPath(rootPath, "terragrunt.hcl")
+       helpers.CopyTerragruntConfigAndFillPlaceholders(t, 
terragruntConfigPath, terragruntConfigPath, s3BucketName, 
"unused-dynamodb-name", helpers.TerraformRemoteStateS3Region)
+
+       // Test backend bootstrap with S3 native locking only
+       stdout, stderr, err := helpers.RunTerragruntCommandWithOutput(t, 
"terragrunt run apply --backend-bootstrap --non-interactive --log-level debug 
--working-dir "+rootPath+" -- -auto-approve")
+       require.NoError(t, err)
+
+       // Validate S3 bucket is created and versioned
+       validateS3BucketExistsAndIsTaggedAndVersioning(t, 
helpers.TerraformRemoteStateS3Region, s3BucketName, true, nil)
+
+       // Note: No DynamoDB table validation - S3 native locking doesn't use 
DynamoDB
+
+       t.Logf("S3 native locking test completed successfully. Output: %s, 
Errors: %s", stdout, stderr)
+
+       // Test that subsequent runs work with S3 native locking only
+       stdout2, stderr2, err2 := helpers.RunTerragruntCommandWithOutput(t, 
"terragrunt run plan --non-interactive --log-level debug --working-dir 
"+rootPath)
+       require.NoError(t, err2)
+
+       t.Logf("S3 native locking plan test completed successfully. Output: %s, 
Errors: %s", stdout2, stderr2)
+}
+
 func TestAwsBootstrapBackendWithoutVersioning(t *testing.T) {
        t.Parallel()
 
@@ -188,7 +277,7 @@
        dynamoDBName := "terragrunt-test-dynamodb-" + testID
 
        defer func() {
-               deleteS3Bucket(t, s3BucketName, 
helpers.TerraformRemoteStateS3Region)
+               deleteS3Bucket(t, helpers.TerraformRemoteStateS3Region, 
s3BucketName)
                cleanupTableForTest(t, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
        }()
 
@@ -222,8 +311,8 @@
        dynamoDBName := "terragrunt-test-dynamodb-" + testID
 
        defer func() {
-               deleteS3Bucket(t, s3BucketName, 
helpers.TerraformRemoteStateS3Region)
-               deleteS3Bucket(t, s3AccessLogsBucketName, 
helpers.TerraformRemoteStateS3Region)
+               deleteS3Bucket(t, helpers.TerraformRemoteStateS3Region, 
s3BucketName)
+               deleteS3Bucket(t, helpers.TerraformRemoteStateS3Region, 
s3AccessLogsBucketName)
                cleanupTableForTest(t, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
        }()
 
@@ -252,7 +341,7 @@
        dynamoDBName := "terragrunt-test-dynamodb-" + testID
 
        defer func() {
-               deleteS3Bucket(t, s3BucketName, 
helpers.TerraformRemoteStateS3Region)
+               deleteS3Bucket(t, helpers.TerraformRemoteStateS3Region, 
s3BucketName)
                cleanupTableForTest(t, dynamoDBName, 
helpers.TerraformRemoteStateS3Region)
        }()
 
@@ -1760,7 +1849,7 @@
 }
 
 // createS3Bucket create test S3 bucket.
-func createS3Bucket(t *testing.T, awsRegion string, bucketName string) {
+func createS3Bucket(t *testing.T, awsRegion, bucketName string) {
        t.Helper()
 
        client := helpers.CreateS3ClientForTest(t, awsRegion)
@@ -1771,7 +1860,7 @@
        require.NoError(t, err, "Failed to create S3 bucket")
 }
 
-func deleteS3Bucket(t *testing.T, bucketName string, awsRegion string) {
+func deleteS3Bucket(t *testing.T, awsRegion, bucketName string) {
        t.Helper()
 
        helpers.DeleteS3Bucket(t, awsRegion, bucketName)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/test/integration_find_test.go 
new/terragrunt-0.82.4/test/integration_find_test.go
--- old/terragrunt-0.82.3/test/integration_find_test.go 2025-06-27 
15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/test/integration_find_test.go 2025-07-02 
15:24:07.000000000 +0200
@@ -11,6 +11,7 @@
        "github.com/gruntwork-io/terragrunt/test/helpers"
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
+       "github.com/wI2L/jsondiff"
 )
 
 const (
@@ -156,7 +157,7 @@
 
                        assert.Empty(t, stderr)
                        if strings.Contains(tc.args, "--json") {
-                               assert.JSONEq(t, tc.expected, stdout)
+                               jsonStringsEqual(t, tc.expected, stdout)
                        } else {
                                assert.Equal(t, tc.expected, stdout)
                        }
@@ -164,6 +165,17 @@
        }
 }
 
+// jsonStringsEqual compares two JSON strings for equivalence, ignoring the 
order of nested arrays.
+func jsonStringsEqual(t *testing.T, expected, actual string, msgAndArgs 
...interface{}) bool {
+       t.Helper()
+
+       patch, err := jsondiff.CompareJSON([]byte(expected), []byte(actual), 
jsondiff.Equivalent())
+       require.NoErrorf(t, err, fmt.Sprintf("Error comparing JSON strings: 
%v", err), msgAndArgs...)
+       require.Emptyf(t, patch, fmt.Sprintf("JSON strings are not 
equal\nExpected: %s\nActual: %s", expected, actual), msgAndArgs...)
+
+       return true
+}
+
 func TestFindExternalDependencies(t *testing.T) {
        t.Parallel()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/terragrunt-0.82.3/test/integration_test.go 
new/terragrunt-0.82.4/test/integration_test.go
--- old/terragrunt-0.82.3/test/integration_test.go      2025-06-27 
15:59:16.000000000 +0200
+++ new/terragrunt-0.82.4/test/integration_test.go      2025-07-02 
15:24:07.000000000 +0200
@@ -4102,7 +4102,7 @@
 
 func TestTF110EphemeralVars(t *testing.T) {
        t.Parallel()
-       if !helpers.IsTerraform110OrHigher() {
+       if !helpers.IsTerraform110OrHigher(t) {
                t.Skip("This test requires Terraform 1.10 or higher")
 
                return

++++++ terragrunt.obsinfo ++++++
--- /var/tmp/diff_new_pack.xwPnAr/_old  2025-07-06 17:08:45.841076069 +0200
+++ /var/tmp/diff_new_pack.xwPnAr/_new  2025-07-06 17:08:45.849076400 +0200
@@ -1,5 +1,5 @@
 name: terragrunt
-version: 0.82.3
-mtime: 1751032756
-commit: a31ef0943c02cd68b648c81a0d1ed037a57e24e9
+version: 0.82.4
+mtime: 1751462647
+commit: 34af1f1518c336c96623fd3c6eb63c53b9afa7eb
 

++++++ vendor.tar.gz ++++++
/work/SRC/openSUSE:Factory/terragrunt/vendor.tar.gz 
/work/SRC/openSUSE:Factory/.terragrunt.new.1903/vendor.tar.gz differ: char 13, 
line 1

Reply via email to