🎉 v1.2.0 Release Candidate
This is the second release candidate for Terragrunt v1.2.
It completes the same eleven experiments as v1.2.0-rc1, and adds bug fixes found since then, most of them in the CAS, the catalog, the Provider Cache Server, and exclude blocks.
Future release candidates for v1.2.0 will include bug fixes related to these experiments or other urgent bug fixes as necessary, and documentation improvements.
Please try out this release candidate in lower environments and share your feedback in the associated GitHub discussion.
🛠️ Breaking Changes
base64gzip() uses the current Go encoder
Go 1.27 changed the compressed output of its gzip encoder. Terragrunt v1.1.5 kept base64gzip() on the older output and warned once per run that this was legacy behavior. base64gzip() now returns what the Go 1.27 encoder produces.
A resource that compares the encoded value can plan a replacement on the first run after upgrading. An aws_instance with user_data_base64 set from base64gzip() and user_data_replace_on_change set to true is one such resource.
Where the encoded value has to stay stable, call the experimental base64gzip_compat(), which returns the v1.1.3 output permanently. It is behind the base64gzip-compat experiment and may be renamed or removed:
terragrunt run --all --experiment base64gzip-compat -- planinputs = {
user_data_base64 = base64gzip_compat(file("${get_terragrunt_dir()}/user-data.sh"))
}This completes the legacy-base64gzip strict control.
Note
Within the 1.0 guarantees
The 1.0 guarantees make promises about how Terragrunt remains backwards compatible. This change does not break those promises. It is listed under breaking changes so you are aware of it, in case it affects your workflows.
base64gzip() still takes a string and returns valid gzipped base64 content that decompresses to the same value, but the encoded value itself changes. base64gzip_compat() keeps the old one.
S3 state buckets no longer get the RootAccess bucket policy statement
When bootstrapping an S3 state bucket, Terragrunt attached a bucket policy statement with the Sid RootAccess that granted s3:* on the bucket and its objects to arn:aws:iam::<account-id>:root, an ARN that grants access to the AWS account as a whole rather than only to its root user. That statement has been removed. It widened reach to state files that routinely hold secrets, and the account already owns the bucket.
The skip_bucket_root_access config no longer has anything to skip, and is now deprecated. Terragrunt still accepts it, and warns about it when bootstrapping a backend whose config sets it. Enable the skip-bucket-root-access strict control to turn that warning into an error.
To grant the AWS account root user access to the state bucket, set the new enable_bucket_root_access config:
# root.hcl
remote_state {
# ... other args omitted for brevity ...
config = {
# ... other config omitted for brevity ...
enable_bucket_root_access = true
}
}Buckets that already have the statement keep it. The state backend docs cover how to remove it yourself.
Note
Within the 1.0 guarantees
The 1.0 guarantees make promises about how Terragrunt remains backwards compatible. This change does not break those promises. It is listed under breaking changes so you are aware of it, in case it affects your workflows.
The bucket policy Terragrunt writes is not part of the CLI, HCL, or output schemas the guarantees pin, and skip_bucket_root_access remains valid configuration. Removing the statement is a bug fix, and enable_bucket_root_access restores it. See Bugs in the guarantees for how a bug fix in 1.x can change your workflows.
✨ New Features
Non-interactive terragrunt catalog output with --format
The catalog TUI needs a terminal. The --format flag (env: TG_FORMAT) writes what the catalog discovers to standard output, so a script or an agent can read the catalog without one.
--format=jsonl writes one JSON object per catalog entry, following a published JSON schema:
terragrunt catalog --format=jsonl | jq -c '{kind, title, component_source}'--format=md writes a Markdown document with a section per entry:
terragrunt catalog --format=md > catalog.mdWithout --format, terragrunt catalog opens the TUI only when standard input and standard output are both terminals. Anywhere else it writes jsonl, so piping the command needs no flag:
terragrunt catalog | jq -c '{kind, title, component_source}'Terragrunt writes each entry as it discovers it. See Non-interactive catalog for the structure of each format and how streaming behaves.
Previously gated behind the catalog-format experiment, non-interactive catalog output no longer requires --experiment catalog-format.
Collect runtime profiles
Terragrunt writes CPU, heap, and goroutine profiles on request, so you can see where a slow run spends its time. Pass --profile-cpu, --profile-mem or --profile-goroutine with a path, or --profile-dir to collect all three into one directory under conventional names. Each flag has a matching TG_PROFILE_* environment variable.
terragrunt --profile-dir /tmp/profiles run --all -- planRead the result with go tool pprof. The profiles cover Terragrunt itself, not the OpenTofu/Terraform processes it runs.
Previously gated behind the profiling experiment, the profile flags no longer require --experiment profiling.
Bound discovery with --discovery-boundary and (dir) filters
Graph filters search up to the Git repository root for dependents and follow dependencies wherever they point, so in a monorepo they can parse sibling environments a command never needed.
A (dir) operand in a graph filter stops traversal at that directory:
cd environments/staging
terragrunt find --filter '(.)...vpc'From the same directory, the --discovery-boundary flag (env: TG_DISCOVERY_BOUNDARY) applies one boundary to every --filter expression on the command:
terragrunt run --all --filter '...vpc' --discovery-boundary . -- planPreviously gated behind the bounded-discovery experiment, bounded discovery no longer requires --experiment bounded-discovery.
Terragrunt docs MCP server
Terragrunt now publishes the read-only Terragrunt docs MCP server, which answers Terragrunt questions from the official docs, the CLI reference, a curated design-pattern library, and real example config. The server is public and unauthenticated. Results are pinned to a Terragrunt version, and docs pages can be read at any release tag from v0.80 onward.
For Claude Code:
claude mcp add -s user --transport http terragrunt-docs https://mcp.docs.terragrunt.com/mcpCursor and other MCP clients that read an mcp.json point at https://mcp.docs.terragrunt.com/mcp instead.
The server is in public beta. It has no availability guarantee and may change significantly.
See the install docs for the full setup.
Download modules from OCI registries
An oci:// source downloads a module from an OCI Distribution registry, such as Amazon ECR, GitHub Container Registry, Azure Container Registry, Google Artifact Registry, or a self-hosted one. It works in a terraform block:
# terragrunt.hcl
terraform {
source = "oci://ghcr.io/acme/tofu-modules/vpc?tag=1.0.0"
}And in the unit and stack blocks of a terragrunt.stack.hcl:
# terragrunt.stack.hcl
unit "vpc" {
source = "oci://ghcr.io/acme/terragrunt-units/vpc?tag=1.0.0"
path = "vpc"
}Pin the artifact with tag or digest. Setting neither selects the latest tag, and a //subdir selector reaches a directory inside the module, unit, or stack. Credentials come from OpenTofu's CLI config, from ambient Docker config, and from credential helpers such as ecr-login, so one source string resolves the same way under both tofu and Terragrunt.
Previously gated behind the oci experiment, these sources no longer require --experiment oci. See OCI registries for the publishing contract and the full authentication order.
Track the files that OpenTofu file functions read
Terragrunt records the files that the built-in file functions read, so reading-based filters select the units that read them without a mark_as_read call:
filetemplatefilefilesetfileexists- the
file*hash functions, such asfilesha256
A file read from inside a template counts too.
mark_as_read remains the way to record a file that only OpenTofu/Terraform reads, such as one passed to a module as an input, or one a run_cmd script reads.
Generated files stored in the CAS
The Content Addressable Store (CAS) now stores the files that generate blocks produce. Each unit's working directory gets a hard link to the stored copy, so every unit that includes this block shares one provider.tf on disk:
# root.hcl
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
contents = "provider \"aws\" {}"
}Generated files are read-only by default, so an existing hook or script that edits a generated file in place fails with a permission error. Set mutable = true on that generate block to give each unit a writable file of its own:
generate "provider" {
path = "provider.tf"
if_exists = "overwrite_terragrunt"
mutable = true
contents = "provider \"aws\" {}"
}Passing --no-cas turns off the CAS for a run, and Terragrunt writes generated files as plain files:
terragrunt run --all --no-cas -- planSee Generate blocks and Immutable by default for details.
Previously gated behind the mutable-generate experiment, CAS storage for generated files no longer requires --experiment mutable-generate.
Iterate unit, stack, and dependency blocks with expansion
An expansion block declares a count or a for_each, and Terragrunt reads the block it sits in once per element. This unit block generates two units, at .terragrunt-stack/aurora/web and .terragrunt-stack/aurora/api:
# terragrunt.stack.hcl
unit "aurora" {
expansion {
for_each = toset(["web", "api"])
}
source = "../units/app"
path = "aurora/${each.key}"
values = {
role = each.key
}
}A stack block expands the same way, generating one stack per element.
An expanded dependency block produces one dependency per element, and inputs reads each one by its key:
# terragrunt.hcl
dependency "aurora" {
expansion {
for_each = toset(["web", "api"])
}
config_path = "../aurora-${each.key}"
}
inputs = {
web_id = dependency.aurora["web"].outputs.id
}unit and stack blocks also accept an enabled attribute. Setting it to false skips the component during stack generation:
# terragrunt.stack.hcl
unit "canary" {
enabled = false
source = "../units/app"
path = "canary"
}Adding an expansion block to an existing block, or shrinking one, changes the addresses of the components it produces. Read the expansion reference before changing one that has already been applied.
Previously gated behind the block-iteration experiment, expansion blocks and the enabled attribute no longer require --experiment block-iteration.
Bootstrap, delete, and migrate Azure Storage state backends
Terragrunt provisions the resource group, storage account, and blob container backing an azurerm state, and converges blob versioning and soft delete on both new and pre-existing accounts. It also deletes state blobs and containers, and migrates state within a storage account. Dependency outputs of Azure-backed units are read straight from the state blob, the same as S3 and GCS.
If you already pass --backend-bootstrap, Terragrunt now creates Azure resources it skipped before.
Previously gated behind the azure-backend experiment, these operations no longer require --experiment azure-backend. See State Backend for configuration keys and authentication.
Set the minimum TLS version on a bootstrapped Azure storage account
The ARM API treats an unset minimum TLS version as TLS1_0, but Azure deprecated TLS1_0 and TLS1_1 in August 2025.
Terragrunt now provisions a new storage account with a minimum TLS version of TLS1_2. The minimum_tls_version option raises it to TLS1_3:
remote_state {
backend = "azurerm"
config = {
storage_account_name = "myterragruntstate"
container_name = "tfstate"
key = "${path_relative_to_include()}/tofu.tfstate"
resource_group_name = "tofu-rg"
use_azuread_auth = true
minimum_tls_version = "TLS1_3"
}
}TLS1_2 and TLS1_3 are the only accepted values. Terragrunt rejects the deprecated TLS1_0 and TLS1_1.
The setting applies only when Terragrunt creates the account. Terragrunt leaves an existing account's setting alone, so change it there with the Azure portal or CLI.
Call OpenTofu 1.13 built-in functions in Terragrunt configurations
Terragrunt evaluates the built-in functions in configurations using its own copy of the OpenTofu implementations, which tracks OpenTofu 1.13.
These functions are now available:
assumeequalassumelistlengthassumelistlengthmaxassumelistlengthminassumemaplengthassumemaplengthmaxassumemaplengthminassumenotnullassumesetlengthassumesetlengthmaxassumesetlengthminassumestringprefixbase64gunzipcidrcontainsephemeralasnullissensitivetemplatestringurldecode
Read dependency outputs from state by default
Terragrunt reads dependency outputs straight from the remote state object, without initializing each dependency to run tofu output or terraform output against it. This covers the S3, GCS, and Azure Storage (azurerm) backends.
When a direct read is unsupported, such as for a backend Terragrunt has no reader for, or fails on a permissions or network error, Terragrunt falls back to tofu/terraform output -json. The outputs are the same either way, so only the speedup is lost.
Pass --no-dependency-fetch-output-from-state (env: TG_NO_DEPENDENCY_FETCH_OUTPUT_FROM_STATE) to always load dependency outputs through tofu/terraform output -json.
Previously gated behind the dependency-fetch-output-from-state experiment, direct state reads no longer require --experiment dependency-fetch-output-from-state. Passing --dependency-fetch-output-from-state still works, and the dependency-fetch-output-from-state strict control turns its deprecation warning into an error.
Version constraints for registry modules
The terraform block accepts a version attribute holding a version constraint for a tfr:// registry module. Terragrunt downloads the highest published version that satisfies the constraint, using the same syntax as the version argument on OpenTofu and Terraform module blocks:
terraform {
source = "tfr://registry.opentofu.org/terraform-aws-modules/vpc/aws"
version = "~> 3.3"
}See the terraform block reference for the full rules.
Previously gated behind the version-attribute experiment, version constraints for registry modules no longer require --experiment version-attribute.
S3 buckets can be created in your account regional namespace
An account regional namespace is a reserved subdivision of the S3 bucket namespace that only your account can create buckets in, so no one else can take or re-create those names. Bucket names in it end with your account ID, the region, and -an.
When a bucket name matches that convention, Terragrunt creates the bucket in the account regional namespace:
# root.hcl
remote_state {
backend = "s3"
config = {
bucket = "my-tofu-state-111122223333-us-east-1-an"
key = "${path_relative_to_include()}/tofu.tfstate"
region = "us-east-1"
}
}There is no setting to enable this. S3 accepts the -an suffix only for account regional buckets, so the name alone decides. accesslogging_bucket_name is read the same way. Buckets named any other way are created in the global namespace, and the namespace is left out of the request entirely, so S3-compatible object stores are unaffected.
A name that fits the convention but names a region other than the bucket's own fails immediately.
Skip dependency outputs with --no-dependency-outputs
The --no-dependency-outputs flag (env: TG_NO_DEPENDENCY_OUTPUTS) skips output resolution for every dependency block in a run, so Terragrunt does not call tofu output on dependencies that may not be applied yet:
terragrunt run --all --no-dependency-outputs -- validateWarning
Use this flag with commands that do not read dependency outputs, such as init and validate. While it is set, references to dependency outputs get no real value, so plan and apply can pass empty values to OpenTofu/Terraform in their place.
Previously gated behind the optional-dependency-outputs experiment, the flag no longer requires --experiment optional-dependency-outputs.
Skip hooks for a run with --no-hooks
The --no-hooks flag (env: TG_NO_HOOKS) skips every hook for a run: before_hook, after_hook, and error_hook blocks.
terragrunt run --no-hooks -- planPreviously gated behind the optional-hooks experiment, --no-hooks no longer requires --experiment optional-hooks.
🐛 Bug Fixes
S3 access log delivery is granted with a bucket policy
S3 disables ACLs on new buckets by default, and a bucket with ACLs disabled rejects the ACL grant Terragrunt wrote to make an access logging bucket accept logs. AWS recommends a bucket policy over an ACL for this grant, and recommends keeping ACLs disabled in general.
Terragrunt now creates buckets with ACLs disabled and grants access log delivery through the logging bucket's policy instead, allowing s3:PutObject for the logging.s3.amazonaws.com service principal on behalf of buckets in the same AWS account:
remote_state {
backend = "s3"
config = {
bucket = "my-state-bucket"
key = "${path_relative_to_include()}/tofu.tfstate"
region = "us-east-1"
accesslogging_bucket_name = "my-logs-bucket"
}
}This only applies to a logging bucket Terragrunt creates. One that already exists keeps the permissions it has, whether that is the ACL grant from an earlier Terragrunt version or something you set up yourself, and Terragrunt neither reads nor writes its policy.
skip_accesslogging_bucket_policy opts out of that grant. skip_accesslogging_bucket_acl is deprecated and now has no effect: Terragrunt puts no ACL on the logging bucket, so there is nothing left for it to skip.
If you set skip_accesslogging_bucket_acl to work around an AccessControlListNotSupported failure on a bucket with ACLs disabled, drop it. The bucket policy covers that bucket, and the attribute now suppresses nothing. Set skip_accesslogging_bucket_policy only if you grant log delivery yourself. Terragrunt warns when the deprecated attribute is used, and the skip-accesslogging-bucket-acl strict control turns that warning into an error.
Bare include blocks warn again
Terragrunt stopped logging the deprecation warning for an include block without a label, so a configuration using one gave no hint that the bare-include strict control would reject it. The parser setup suppressed the warning on the shared control before any file was read, and the check that finds the bare include then had nothing left to log.
A run over a configuration with a bare include now logs the warning once:
WARN Using an `include` block without a label is deprecated. Please use the `include` block with a label instead. For more information, see https://docs.terragrunt.com/migrate/bare-include/
With --strict-mode or --strict-control bare-include, the run still fails with an error and logs no warning. See the bare include migration guide for how to label the block.
expansion works with autoinclude and stack dependencies
terragrunt stack generate failed with There is no variable named "each" when a unit or stack block declared both an expansion block and an autoinclude block. The error pointed at each.key in path, even when autoinclude never referenced each.
Generation now writes an autoinclude file for each element, and each.key, each.value, and count.index inside autoinclude resolve to that element:
# terragrunt.stack.hcl
unit "repo" {
source = "../units/repo"
path = "repo"
}
unit "environment" {
expansion {
for_each = toset(["dev", "prod"])
}
source = "../units/environment"
path = "environment/${each.key}"
autoinclude {
dependency "repo" {
config_path = unit.repo.path
}
inputs = {
environment = each.key
repository = dependency.repo.outputs.name
}
}
}The prod element gets this terragrunt.autoinclude.hcl:
dependency "repo" {
config_path = "../../repo"
}
inputs = {
environment = "prod"
repository = dependency.repo.outputs.name
}A dependency block inside autoinclude that declared its own expansion block failed generation with the same error. A unit whose terragrunt.autoinclude.hcl contained one also failed to parse. An expanded unit could not be referenced from the stack file at all, since unit.<name>.path skipped it.
Each element of an expanded unit or stack is now referenced as unit.<name>[key].path. The generated dependency block keeps its expansion block. Generation evaluates for_each or count in the stack file, where local.* and values.* are available, writes the result as a literal, and resolves config_path for each element. The generated unit expands the dependency when it is parsed:
# terragrunt.stack.hcl
locals {
regions = toset(["us-east-1", "us-west-1"])
}
unit "vpc" {
expansion {
for_each = local.regions
}
source = "../units/vpc"
path = "vpc/${each.key}"
values = {
region = each.key
}
}
unit "app" {
source = "../units/app"
path = "app"
autoinclude {
dependency "vpc" {
expansion {
for_each = local.regions
}
config_path = unit.vpc[each.key].path
mock_outputs = { vpc_id = "vpc-mock-${each.key}" }
}
inputs = {
vpc_ids = { for region, vpc in dependency.vpc : region => vpc.outputs.vpc_id }
}
}
}# .terragrunt-stack/app/terragrunt.autoinclude.hcl
dependency "vpc" {
expansion {
for_each = toset(["us-east-1", "us-west-1"])
}
config_path = {
us-east-1 = "../vpc/us-east-1"
us-west-1 = "../vpc/us-west-1"
}[each.key]
mock_outputs = { vpc_id = "vpc-mock-${each.key}" }
}
inputs = {
vpc_ids = { for region, vpc in dependency.vpc : region => vpc.outputs.vpc_id }
}A stack file that declares the same unit or stack label both with and without an expansion now fails to parse, because unit.<name> cannot refer to both.
Discovery failed the same way on a dependency whose config_path pointed at a stack directory containing an expanded unit. It dropped the dependency instead of reporting the error, so run --all did not wait for the units in that stack. The dependency now covers every element of the expanded unit.
CAS fetches leave no git process behind
After a fetch that brings in more than about a hundred commits, git starts git maintenance run --auto in the background and returns without waiting for it. That process kept writing commit graphs into the CAS git store for a few seconds after Terragrunt had moved on, outside the lock Terragrunt holds on that store entry, and into temporary clones Terragrunt was already deleting.
Every git fetch Terragrunt runs now turns automatic maintenance off, so the fetch is finished when it returns.
The CAS clones a tag when a branch name ends in it
A source pinned to a tag, such as ?ref=v1.2.3, failed to clone through the CAS when the repository also had a branch whose name ends in the tag name, such as release/v1.2.3, and that branch had moved past the tag:
WARN central git store unavailable for https://github.com/acme/modules.git, falling back to temporary clone: object <release/v1.2.3 commit> not present in central git store after fetching v1.2.3 from https://github.com/acme/modules.git
WARN CAS processing failed for unit "service": failed to CAS clone "https://github.com/acme/modules.git": ... fatal: not a tree object
Terragrunt then downloaded the source without the CAS on every run. A unit generated from a stack with update_source_with_cas = true and a relative terraform.source kept that relative source, which pointed outside .terragrunt-stack and failed init.
Terragrunt now resolves a ref to the same commit git fetch downloads, so v1.2.3 resolves to the tag. When a branch and a tag share the exact name, the tag wins, as it does for git fetch.
The CAS refuses tree entries that escape the download directory
A repository with a hand-edited git tree can list a file at a path git would never write, such as ../../x.tf or a path under a symbolic link in the same tree. The CAS wrote the file where the path pointed, which could be outside the module's download directory.
Terragrunt now checks each path before writing to it and refuses the tree when one escapes the download directory. A path that passes through a symbolic link already present in the download directory is refused too, because the link decides where the write lands.
update_source_with_cas = !false now counts as true
Terragrunt now evaluates the update_source_with_cas expression instead of reading it as a literal value.
Catalog clone directories drop the .git suffix
The catalog named each clone directory after the clone URL without removing the .git suffix, so https://github.com/acme/terraform-aws-modules.git cloned into a directory called terraform-aws-modules.git. That name appeared in the log line reporting where the repository was cloned, and in the module paths the catalog shows when a repository has no remote to link to. Terragrunt now strips the suffix for HTTPS and SSH clone URLs, git:: sources, and URLs that carry a ref query parameter or a fragment.
Catalog components link to their repository when cloned through the CAS
When the catalog cloned a repository through the CAS, it linked each component to a path inside the temporary clone directory instead of to the repository. terragrunt catalog --format=jsonl showed that path in the url field:
{"kind":"module","title":"VPC","url":"/tmp/catalog-7USjxQ-804451847/infrastructure-modules/modules/vpc"}The catalog read the remote and branch from the clone's git metadata, which under the CAS describes the CAS store's own bare repository. That repository records no remote, and its branch is the store's default rather than the source's.
Terragrunt now takes the remote from the clone URL, and the branch from the ref the URL asks for or from the branch the remote's HEAD points at. Under --cas-offline the link uses HEAD, which GitHub and GitLab resolve to the default branch.
A catalog URL that selects a subdirectory, such as github.com/acme/catalog//modules, now loads through the CAS. The url and component_source of each component include that subdirectory.
exclude blocks no longer crash on null, unknown or sensitive strings
Terragrunt panicked when if, no_run or exclude_dependencies in an exclude block evaluated to a null, unknown or sensitive string, such as if = tostring(dependency.vpc.outputs.skip).
These strings now behave like the matching bool values:
- A null
ifreportsnull value is not allowed. - A null
no_runorexclude_dependenciescounts as unset. - An unknown value, such as a dependency output during discovery, skips the block with a warning.
- A sensitive value reports an invalid
excludeblock error.
find and list honor if in exclude blocks
Running terragrunt find --queue-construct-as apply or terragrunt list --queue-construct-as apply dropped every unit whose exclude block listed apply, even when the block's if was false:
exclude {
if = false
actions = ["plan", "apply", "destroy"]
}The run queue for run --all apply kept such a unit, so the two commands disagreed about what would run. Both commands now drop a unit only when its exclude block has if = true and lists the action.
Malformed {} groups in glob patterns no longer crash Terragrunt
Some glob patterns with an empty or unclosed {} group crashed Terragrunt when it matched them, e.g. terragrunt find --filter '{./a{}'. Others silently failed to match, so {}a did not match a.
Terragrunt now refuses these patterns with an invalid pattern error. This covers filter queries, include_in_copy and exclude_from_copy in the terraform block, and .terragrunt-catalog-ignore files. A group with one empty option next to a non-empty one, such as main.tf{,.bak}, still works.
hcl validate reports dependencies on deleted units
When a dependency block pointed at a deleted or moved unit, run --all failed, but find, list and hcl validate passed. hcl validate now fails for every enabled dependency block whose config_path has no unit or stack configuration, so you can catch the problem before a run.
run --all with a Git-based filter, such as --filter '[main...HEAD]', still fails when a dependency block points at a unit or stack that the diff deleted. The error now names the deleted path and the Git reference that still has it:
a dependency points at dep, which exists at main but was deleted or moved in the Git diff
The new missing-dependency-config tip also suggests running hcl validate. find and list behave as before.
hcl validate --inputs reads -var and -var-file arguments verbatim
hcl validate --inputs applied shell quoting rules to each entry in extra_arguments before reading -var and -var-file from it. Those rules treat a backslash as an escape character, so on Windows a var file path such as "-var-file=${get_terragrunt_dir()}\\varfiles\\main.tfvars" lost its separators, and validation failed to open the file.
Terragrunt now reads each entry in arguments exactly as written, as the single argument it becomes on the OpenTofu/Terraform command line.
Units with identical configs each resolve their own iam_role
When two units had the same terragrunt.hcl content, Terragrunt could assume the first unit's IAM role for both. This hit any iam_role that depends on the unit's directory, such as:
iam_role = "arn:aws:iam::123456789012:role/${basename(get_terragrunt_dir())}"With this config in a/ and b/, b assumed role/a instead of role/b. Terragrunt now evaluates iam_role in each unit's own directory, so get_terragrunt_dir(), find_in_parent_folders(), and similar functions return that unit's paths.
A relative source inherited through include is marked as read from the unit's directory
When a terraform block with a relative source lived in an included config, find --reading and reading= filters resolved the path against the included file instead of the unit, which is where a run resolves it. The module's files went missing from the unit's reading list, so a change to the module did not select the unit:
# root.hcl
terraform {
source = "${path_relative_from_include()}/modules//foo"
}$ terragrunt find --json --reading
[{"type":"unit","path":"live/unit","reading":["root.hcl"]}]Terragrunt now resolves the source against the unit's directory, so modules/foo/main.tf appears in reading and reading-based filters select the unit the same way they do for an absolute source.
terragrunt info print --all writes JSON Lines and reports each unit's own download directory
info print --all wrote each unit's info indented over several lines, one object after another, and gave every unit the root's download_dir:
$ terragrunt info print --all
{
"config_path": "/example/live/db/terragrunt.hcl",
"download_dir": "/example/live/.terragrunt-cache",
"iam_role": "",
"terraform_binary": "tofu",
"terraform_command": "print",
"working_dir": "/example/live/.terragrunt-cache/EfNrjc2equLKYmOZbwT2qu1dO9c/ByrgT1vMBQjFneXYgAxchposVZ0"
}
{
"config_path": "/example/live/vpc/terragrunt.hcl",
"download_dir": "/example/live/.terragrunt-cache",
...
}A line-oriented reader could not take one entry at a time:
$ terragrunt info print --all | head -1 | jq .
jq: parse error: Unfinished JSON term at EOF at line 2, column 0The download_dir was wrong as well. run --all creates each unit's .terragrunt-cache next to that unit's configuration, so the directory reported here was not the one the unit runs against.
With --all, Terragrunt now writes one object per line, so the output is JSON Lines, and builds each unit's context the way run --all does:
$ terragrunt info print --all | jq -c '{config_path, download_dir}'
{"config_path":"/example/live/db/terragrunt.hcl","download_dir":"/example/live/db/.terragrunt-cache"}
{"config_path":"/example/live/vpc/terragrunt.hcl","download_dir":"/example/live/vpc/.terragrunt-cache"}Printing a single unit is unchanged: one indented object.
Malformed exclude blocks report an error
Terragrunt silently dropped an exclude block with an attribute of the wrong type, such as actions = "plan" where a list belongs, and ran the unit as if the block weren't there. The parse now fails with an error that names the file and the attribute:
exclude block in /live/unit/terragrunt.hcl: json: cannot unmarshal string into Go struct field ExcludeConfig.actions of type []string
Discovery doesn't fetch dependency outputs, so it can't evaluate an exclude block that reads one. Terragrunt still skips that block during discovery, and now logs a warning naming the file.
Negative error patterns apply regardless of their position in the list
A negative pattern in ignorable_errors or retryable_errors, written with a leading !, only took effect when it came before every positive pattern the error also matched. An error matching both ".*Error: safe warning.*" and "!.*Error: do not ignore.*", listed in that order, was ignored anyway. A matching negative pattern now keeps the error from being ignored or retried, wherever it sits in the list.
Nested stacks now generate inside Git filter worktrees
A stack file can reference another stack with a stack block. For a brand-new stack like that, terragrunt find --filter '[a...b]' reported the nested stack, but terragrunt run with the same filter warned No units discovered and did nothing. Terragrunt never generated the nested stack in the temporary worktree, so no units existed there to run.
Worktree generation now recurses into nested stacks, the same way it does in the working directory, including nested stacks that set no_dot_terragrunt_stack = true. find and run agree again, and Terragrunt discovers and runs the units of added, changed, and removed nested stacks.
After each level of generation, Terragrunt now only walks the directories of the stacks it just generated, instead of walking everything again.
Arguments ending in -no-color are no longer dropped
After the first --no-color, Terragrunt discarded every later argument ending in -no-color. In terragrunt run --no-color -- plan -out=plan-no-color, the -out argument never reached OpenTofu, so no plan file was written and nothing in the output said why.
Terragrunt now drops a duplicate only when the argument is the flag itself, --no-color with or without an =value. Everything after -- passes through as written.
Provider cache requires checksums from registries
The Provider Cache Server checks each provider archive it downloads against the checksum its registry publishes. When a registry's download response left out shasums_url or shasums_signature_url, the cache server skipped that check, cached the archive, and logged it as unauthenticated.
The cache server now requires shasum and shasums_url in a registry's download response, and shasums_signature_url when the response includes signing keys. A response without them fails the run with an error naming the missing field. The provider registry protocol requires all three, so a registry that implements it is unaffected. Providers installed through a network or filesystem mirror are cached as before.
Fixed read_terragrunt_config on suffixed values files and dependency cycles
read_terragrunt_config treated any file whose name ended in terragrunt.values.hcl, such as prod.terragrunt.values.hcl, as the values file of its directory. It returned the contents of the terragrunt.values.hcl next to it, or failed with panic in function implementation: runtime error: invalid memory address or nil pointer dereference when there was none.
Only a file named exactly terragrunt.values.hcl is now read as a values file. A file such as prod.terragrunt.values.hcl is parsed as a Terragrunt configuration, so its attributes belong in blocks such as locals.
When a dependency block makes a read_terragrunt_config call repeat itself, for example when unit a reads ../b/terragrunt.hcl and b has a dependency on a, Terragrunt now fails with a read_terragrunt_config cycle detected error that lists the chain. Before, it recursed until it failed with maximum parse depth of 1000 exceeded, buried in a long chain of nested errors. A dependency can still read the same file again when the file evaluates differently there, for example through get_original_terragrunt_dir().
Shorthand required_providers entries are no longer ignored
A provider declared with the shorthand form of required_providers, as in aws = ">= 5.0", looked unconstrained to Terragrunt. Only the object form with source and version was read.
With the Provider Cache Server, the constraints line for such a provider in .terraform.lock.hcl was the resolved version rather than the declared range, so the lock file pinned the provider to one exact version.
Terragrunt now reads the shorthand and records the declared constraint under hashicorp/<name> (e.g. hashicorp/aws), the same address it already uses for an object with a version and no source.
Dependencies on a stack skip its disabled units
A dependency whose config_path pointed at a stack directory also depended on the units and stacks in that stack set to enabled = false. Stack generation never writes a disabled unit, so run --all failed on the missing directory:
You attempted to run terragrunt in a folder that does not contain a terragrunt.hcl file. Please add a terragrunt.hcl file and try again.
find --dependencies and dag graph listed the same missing path as a dependency.
The dependency now covers only enabled units. The units of a disabled stack are left out, including a tree generated before the stack was disabled.
Stack commands respect --discovery-boundary
stack generate, stack run, and stack output scanned the whole working directory for stack files and ignored --discovery-boundary. In a monorepo with a catalog next to live infrastructure, a catalog stack referencing files that exist only in the live tree failed the command, even though the command never asked for that stack.
The boundary now applies to these commands, including an inline (dir) operand, and it holds when a Git expression such as [main...HEAD] generates stacks for both compared commits. This works from the repository root:
terragrunt stack run plan --filter '(./live/)...[main...HEAD]'Terragrunt skips the catalog units outside ./live. It still scans the whole working directory when a positive filter has no dependent-side boundary and --discovery-boundary is unset, or when the boundaries fall in separate directories.
Dependent discovery had the same gap and parsed units outside the dependent-side boundary. It now starts the search for dependents at that boundary, which can be a directory inside the working directory.
In a Git expression, a relative boundary resolves against the repository root like any other path in the expression. Changed units outside a dependent-side boundary are ignored, and a boundary that exists in neither compared commit is an error. A dependency-side boundary only limits dependency traversal.
Unit names with non-ASCII characters no longer break run summary alignment
With --summary-per-unit, Terragrunt prints each unit with a dotted leader running from its name to its duration. Terragrunt measured names in bytes, so a name with non-ASCII characters got a shorter leader than it needed, and its duration landed left of the others:
❯❯ Run Summary 3 units 6s
────────────────────────────
Succeeded (3)
database ......... 4s
base-de-données . 4s
データベース 4s
Terragrunt now measures unit names by the terminal columns they fill, so the durations line up again. A character that fills two columns, as in Japanese or Chinese, counts as two:
❯❯ Run Summary 3 units 6s
────────────────────────────
Succeeded (3)
database ......... 4s
base-de-données .. 4s
データベース ..... 4s
The width option in a custom log format measures the same way, so a column padded to a width stays aligned when a value fills two columns per character.
--auth-provider-cmd and --queue-construct-as reject unquoted shell operators
Terragrunt splits --auth-provider-cmd and --queue-construct-as values into words without running a shell. An unquoted shell operator such as |, ;, &&, or > used to end the value, and Terragrunt used only the words before it, so --auth-provider-cmd 'get-creds | jq .creds' ran get-creds on its own.
A value with an unquoted shell operator is now an error. Quote the operator to pass it as part of an argument. To run a pipeline as the auth provider, put it in a script and pass the script.
🧪 Experiments Added
mcp-command — Serve Terragrunt operations to AI agents
The new mcp-command experiment adds the mcp command, which serves Terragrunt operations to AI agents over the Model Context Protocol.
An agent can ask which units exist, how they depend on each other, whether configurations pass validation, what order the units run in, what a unit's applied outputs are, and more.
Point an MCP client at the Terragrunt binary and make sure that it enables the experiment:
// .mcp.json
{
"mcpServers": {
"terragrunt": {
"command": "terragrunt",
"args": ["mcp"],
"env": {
"TG_EXPERIMENT": "mcp-command"
}
}
}
}By default, the server refuses to start any subprocess (e.g. tofu, terraform, git, or a run_cmd program). Wherever Terragrunt would have started one, the result substitutes a stand-in for its output, such as mock_outputs for a dependency output that tofu output -json would have fetched, and lists each substitution in a degraded field.
Pass --allow=exec to let the server run the tofu, terraform, and git that Terragrunt starts on its own during a run. A program a configuration names, through run_cmd(), a before_hook, or --auth-provider-cmd, is still refused, including a tofu, terraform, or git the configuration names for itself, so pointing the server at a repository does not hand it those programs. Allow the commands you want with --allow-cmd, a pattern matched against the program and each of its arguments (e.g. --allow-cmd='jq **'), or let the read-only tools ask: when discover, render_config, validate, or run_order meets a refused program, it sends the client an elicitation naming it, which the client usually shows the person operating the agent, and runs again with whatever they accept. plan, apply, and destroy never ask, since answering would mean running them a second time.
The remaining capabilities are denied the same way, each granted on its own:
-
--allow=httplets Terragrunt make HTTP requests on its own.This includes downloading a unit's remote
terraform { source }, fetching a stack's sources, reaching a cloud API to assume a role or read a bucket, and reading remote state directly from blob stores. -
--allow=sopslets it decrypt SOPS-encrypted files.Unless it is granted,
sops_decrypt_filefails rather than handing an agent the cleartext of your secrets. -
--allow=envpasses the server's environment variables to configurations and the commands the tools run.Unless it is granted, tool calls start from an empty environment, so
get_env()returns its default. The server also clears its own environment variables and pointsHOMEat an empty directory, so cloud SDKs, the SOPS decrypter, andgitcommands Terragrunt runs will find no credentials in environment variables or your home directory.
Granting a capability only changes the capabilities of Terragrunt. A process started under --allow=exec can still reach out on the network on its own, so tofu init will download providers whether or not --allow=http was passed to allow Terragrunt to make network requests. The directory the server is launched in is its root. A tool call targeting a directory outside it is refused, and graph traversal is bounded there too, so a filter following dependencies or dependents cannot bring back a unit from a tree the server was never pointed at. That bounds what the server acts on, not what a configuration can read: an HCL function such as file() reads the real disk wherever it points.
You can grant multiple capabilities at once:
// .mcp.json
{
"mcpServers": {
"terragrunt": {
"command": "terragrunt",
"args": ["mcp", "--allow=exec", "--allow=http"],
"env": {
"TG_EXPERIMENT": "mcp-command"
}
}
}
}A separate flag, --dangerously-allow-apply, adds apply and destroy tools on top of --allow=exec. Without it neither tool is registered, so a client is never told they exist, and the server won't ever run apply or destroy on behalf of a client.
With it, the tool calls return an elicitation (the protocol's way for a server to ask the client's user a question) naming the units that would be run, and the run starts only once the person operating the client accepts it. A decline ends the tool call, and a client with no way to ask anyone is refused. The approval names the units, not the changes: nothing is planned to build that list, since a plan costs a full run that the acceptance then repeats. Call the plan tool first if you want the changes in front of you before accepting.
Only grant this capability on infrastructure you are willing to lose.
Passing it without --allow=exec is refused at startup, since applying means running OpenTofu/Terraform. Launch the server in the environment directory you are willing to have changed rather than at the repository root, because that directory is as far as any tool call can reach:
// .mcp.json
{
"mcpServers": {
"terragrunt-throwaway": {
"command": "terragrunt",
"args": [
"mcp",
"--working-dir", "/path/to/dev",
"--allow=exec",
"--dangerously-allow-apply"
],
"env": {
"TG_EXPERIMENT": "mcp-command"
}
}
}
}Warning
Agents managing infrastructure
An agent reading your estate is still an agent acting on it. A model can be confidently wrong about what a tool does, and it can be steered by things you don't expect, including the comments in configurations, module READMEs, and command output it was pointed at. Read what an agent responds with as a proposal. Keep a person between it and anything that changes real resources, and give it credentials scoped to what you are willing to have it reach.
The restrictions this server places on itself are not a sandbox. They bound what the tools on this server do, and nothing else. An agent that can run shell commands can run terragrunt run --all apply itself, with your ambient credentials, whether or not the server was started with --allow=exec.
You remain responsible for how your agents manage infrastructure, and for what they do with the access you give them. An agent with these tools and your credentials can change or destroy real resources, and accepting that risk is your decision.
The tools that run OpenTofu/Terraform use the binary named by --tf-path, and ignore a unit's terraform_binary and engine block, since Terragrunt starts both without an --allow-cmd pattern or an approval. plan, apply, and destroy take parallelism to cap how many units run at once.
The tools the server offers, the arguments they take, and what each capability grants are documented with the mcp command.
This will not stabilize before v1.3, so treat the tool set, the flag names, and the shape of every result as subject to change until then. The experiment documentation lists the criteria that have to be met first.
🧪 Experiments Updated
iac-engine — Engines no longer need a local OpenTofu/Terraform binary
Before every run, Terragrunt probes the OpenTofu/Terraform binary installed on the host with tofu -version to learn the version and implementation of the tool it is going to orchestrate. With an engine configured, that probe still ran, so run and run --all failed unless tofu or terraform was also installed on PATH.
Units with engines now skip the probe. Their version and implementation stay unknown, which does not affect the run, because the engine decides which binary executes the commands. A terraform_version_constraint on such a unit cannot be checked and is skipped with a warning; terragrunt_version_constraint is checked as before. Disabling the engine with --no-engine restores the probe.
See the IaC engines docs for details.
Eleven experiments completed
The following experiments graduated to general availability in this release, and the features they gated are now enabled by default:
azure-backendblock-iterationbounded-discoverycatalog-formatdependency-fetch-output-from-statemutable-generateocioptional-dependency-outputsoptional-hooksprofilingversion-attribute
Each feature is described in the New Features section above.
The corresponding --experiment flags (and TG_EXPERIMENT values) are no longer needed. Passing one still works, but emits a warning about the completed experiment, so you can drop it at your convenience.
Thank you to everyone who ran these experiments early and filed the feedback that got them here.
tg-login — Signing in to the Gruntwork Developer Portal
The tg-login experiment now enables terragrunt login, which signs you in to the Gruntwork Developer Portal from the CLI:
terragrunt --experiment tg-login loginOnce you are signed in, terragrunt catalog discovers the repositories your organization selected in the portal and adds them to your catalog. See Gruntwork Developer Portal for how to select those repositories.
See the experiment documentation for what still has to land before it stabilizes.
⚙️ Process Updates
Dropped the hashicorp/terraform v0.15.3 dependency
Terragrunt drew its built-in functions from github.com/hashicorp/terraform, pinned to v0.15.3 by a replace directive. Those functions now come from Terragrunt's own copy of the OpenTofu implementations, and go.mod has no replace directives.
Projects that import Terragrunt as a Go module no longer resolve github.com/hashicorp/terraform, and their dependency graph drops by roughly 175 modules, most of them cloud provider SDKs that Terraform used for its backends.
Installing and running the Terragrunt binary is unchanged.
Pull Requests
✨ Features
- feat(profiling): collect runtime profiles by default by @denis256 in #6945
- feat(dependency): read dependency outputs from state by default by @denis256 in #6932
- feat(hcl): switch base64gzip to the current encoder by @denis256 in #6947
- feat: Adding
logincommand by @yhakbar in #6804 - feat: Adding portal Catalog API client by @yhakbar in #6805
- feat: Adding portal repositories to
terragrunt catalogby @yhakbar in #6937 - feat: Allow creation of S3 buckets in the account regional namespace by @yhakbar in #6870
- feat: Replace bucket ACL with policy for access logging by @yhakbar in #6868
- feat(azurerm): adapt minimum_tls_version in azure state for v1.2.0 by @denis256 in #6968
- feat: Adding
mcpcommand by @yhakbar in #6851
🐛 Bug Fixes
- fix: Ensure that worktrees get cleaned up even when cancelled by @yhakbar in #6900
- fix: reuse assumed role session and fix dependency init check with absolute TF_DATA_DIR by @denis256 in #6902
- fix: Adding
--cas-offlinesupport forcatalogby @yhakbar in #6904 - fix: Fixing integration of
expansionwithautoincludeby @yhakbar in #6936 - fix: Patching bug with discovery not respecting
enabledattribute by @yhakbar in #6939 - fix(docs): serve the vendor tag proxies the shared GTM container needs by @ZachGoldberg in #6974
- fix(docs): run the Google tag on the main thread and serve the container first-party by @ZachGoldberg in #6975
- fix(docs): resolve the container's extensionless tag paths past the trailing-slash redirect by @ZachGoldberg in #6976
- fix(docs): resolve GA4's collect endpoints past the trailing-slash redirect by @ZachGoldberg in #6978
- fix: More URL redaction by @yhakbar in #6961
- fix: Remove root policy in buckets by @yhakbar in #6674
- fix: Fixing worktree optimization with Git filter expressions on Windows by @yhakbar in #6970
- fix: correct typo in base64gzip changelog note by @denis256 in #6983
- fix: Addressing feedback in #6851 by @yhakbar in #6991
- fix: respect discovery boundary in stack generate and stack run by @denis256 in #6989
- fix: guard MustWalkTerraformOutput against negative index, nil by @denis256 in #6995
- fix: Fixing race in
TestStackExpansionGitFilterSelectsRemovedInstanceby @yhakbar in #7004 - fix: Adjusts how we ensure that the CAS only gets a Git-compatible venv by @yhakbar in #7003
- fix(strict): name base64gzip-compat experiment in legacy-base64gzip control by @denis256 in #7014
- fix: Resolving
iam_roleper unit directory and erroring on malformedexcludeblocks by @yhakbar in #7012 - fix: Fixing signin display name by @yhakbar in #7015
- fix: respect discovery boundary when parsing dependents and in Git worktrees by @denis256 in #6998
- fix: Resolving CAS refs the way
git fetchdoes, so a tag shadowed by arelease/<tag>branch clones by @yhakbar in #7021 - fix: Address feedback from #7021 by @yhakbar in #7024
- fix: Addressing OpenTofu/Terraform language globally by @yhakbar in #7028
- fix: Ensuring that negative patterns prevent error matching wherever they sit by @yhakbar in #7031
- fix: Updating Provider Cache Server handling of the legacy shorthand by @yhakbar in #7032
- fix: Fixing bare include warning by @yhakbar in #7027
- fix: Fixing summary padding visible width for non-ascii characters by @yhakbar in #7034
- fix: Dropping
.gitsuffix for catalog clones by @yhakbar in #7033 - fix: Fixing excludes logic for
--queue-construct-asby @yhakbar in #7036 - fix: Preventing GC auto maintenance from causing races in CAS by @yhakbar in #7040
- fix: Remove hidden dependency on ability to run
tofu -versionfor engines by @yhakbar in #7041 - fix: Fixing worktree log prefix by @yhakbar in #7043
- fix: Handling null, unknown and sensitive strings in exclude blocks without crashing by @denis256 in #7023
- fix: Fixing
-no-colorargument drop by @yhakbar in #7035 - fix: Evaluating constant update_source_with_cas expressions in CAS source rewrite by @denis256 in #7061
- fix: Adding Provider Cache Server checksum requirement for registries by @yhakbar in #7063
- fix: Display
readingwith consistent relative sources by @yhakbar in #7048 - fix(stacks): generate nested stacks inside git filter worktrees by @denis256 in #7057
- fix: Confine CAS git tree entries to the download directory by @denis256 in #7062
- fix: stop read_terragrunt_config panics and dependency read cycles by @denis256 in #7072
- fix: Fixing
urlshowing up as invalid URL when usingjsonlformat with CAS by @yhakbar in #7082 - fix: handling deleted dependencies in filter by @denis256 in #6546
🏎️ Performance
📖 Documentation
- docs: clean up released version gates for v1.1.5 by @github-actions[bot] in #6905
- docs: Bumping Bun to 1.4.2 by @yhakbar in #6951
- docs: More stacks docs clean-up by @yhakbar in #6960
- docs: Dropping v1 banner by @yhakbar in #6965
- docs: Adding GW Developer Portal Private Beta callouts by @yhakbar in #6980
- docs: clean up released version gates for v1.1.6 by @github-actions[bot] in #6985
- docs: Adding
mcp.docs.terragrunt.cominstall instructions by @yhakbar in #6984 - docs: Adding docs for the
logincommand by @yhakbar in #6996 - docs: Adding portal catalog setup docs by @yhakbar in #6997
- docs: Cleaning up changelog for
v1.2.0-rc1by @yhakbar in #7019 - docs: Updating catalog TUI demo by @yhakbar in #7090
🤖 CI
- ci(coverage): suppress TestGitStoreEnsureCommit_PinnedSHAFetchesOneCommit from timing report by @denis256 in #7017
- ci(coverage): azurehelper package coverage increase by @denis256 in #7054
- ci(coverage): raise test coverage by @denis256 in #7060
🧹 Chores
- chore: Enforce usage of testify by @yhakbar in #6901
- chore: PR comments by @denis256 in #6911
- chore: Completing
block-iterationexperiment by @yhakbar in #6925 - chore: Completing oci experiment by @denis256 in #6926
- chore: Completing
bounded-discoveryexperiment by @yhakbar in #6928 - chore: Completing the
catalog-formatexperiment by @yhakbar in #6931 - chore: Completing the
mutable-generateexperiment by @yhakbar in #6933 - chore: Completing the
optional-dependency-outputsexperiment by @yhakbar in #6935 - chore: Completing azure-backend experiment by @denis256 in #6929
- chore: Cleaning up stacks docs by @yhakbar in #6950
- chore: Completing the
version-attributeexperiment by @yhakbar in #6949 - chore: Fixing
TestAwsDependencyOutputOptimizationtests by @yhakbar in #6953 - chore: Vendoring OpenTofu packages by @yhakbar in #6948
- chore: Use RustFS where AWS usage isn't strictly necessary by @yhakbar in #6954
- chore: Fixing these test names by @yhakbar in #6955
- chore: Parallelizing more cloud tests by @yhakbar in #6957
- chore: Completing the
optional-hooksexperiment by @yhakbar in #6938 - chore: Consolidating URL redaction by @yhakbar in #6903
- chore: More stack parser parity tests by @yhakbar in #6941
- chore: Adding Windows unit tests by @yhakbar in #6907
- chore: Fix-ups from recent merge flood by @yhakbar in #6963
- chore: fail unstaged login stub requests by @denis256 in #6964
- chore: Fixing Windows tests by @yhakbar in #6966
- chore: Adding better login failure messages by @yhakbar in #6962
- chore: Renaming
TestAwstoTestAWSby @yhakbar in #6979 - chore(deps): bump cloud, azure, aws, golang dependencies by @denis256 in #6973
- chore: Fixing race in CAS tree test race by @yhakbar in #6982
- chore: Cache in CI better by @yhakbar in #6972
- chore: Adding full CLI fuzz by @yhakbar in #6987
- chore: Bumping JS deps by @yhakbar in #6992
- chore: Getting rid of gopls workflow by @yhakbar in #6994
- chore: Streamlining CI jobs by @yhakbar in #6840
- chore: Setting
GITHUB_STEP_SUMMARYin bats tests to avoid actually writing to summaries by @yhakbar in #7000 - chore: Bumping Go tools by @yhakbar in #6986
- chore: Setting up per-run env and version to increase test parallelization by @yhakbar in #6827
- chore: Adding e2e login and catalog test by @yhakbar in #6999
- chore: Using
gotestsumbetter by @yhakbar in #7001 - chore: Fuzz fixes by @yhakbar in #7002
- chore: lint fix by @yhakbar in #7011
- chore: Drop ContextWithEnv by @yhakbar in #7010
- chore: Using a Dev Drive in Windows CI by @yhakbar in #7013
- chore: Adding explanations for serial tests and parallelizing some tests by @yhakbar in #6824
- chore: Getting rid of
multierroruse by @yhakbar in #7016 - chore: Making venv isolation explicit instead of cloning it with ParsingContext by @yhakbar in #7025
- chore(deps): bump aws-sdk-go-v2, hcl/v2, go-shellwords, mcp go-sdk, google api by @denis256 in #7030
- chore: Passing the venv to pkg/config functions as a parameter by @yhakbar in #7029
- chore: Serializing Windows console tests by @yhakbar in #7038
- chore: Assert non-nil
ShellOptionsinRunCommandWithOutputby @yhakbar in #7037 - chore: Exporting and splitting pkg/config decode helpers by @yhakbar in #7044
- chore: Drop
echofrom Provider Cache Server by @yhakbar in #7046 - chore: scaffold updates by @denis256 in #7022
- chore: add support for OpenTofu 1.13 by @denis256 in #7058
- chore: Warn on local state usage at run time instead of discovery by @yhakbar in #7050
- chore: Addressing feedback from #7063 by @yhakbar in #7065
- chore(deps): bump go dependencies by @denis256 in #7070
- chore: Addressing feedback from #7048 by @yhakbar in #7084
- chore: Adding hijack checkmark in TODOs by @yhakbar in #7087
- chore: Trimming down small dependencies by @yhakbar in #7059