All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
When a new release is proposed:
- Create a new branch
bump/x.x.x(this isn't a long-lived branch!!!); - The Unreleased section on
CHANGELOG.mdgets a version number and date; - Open a Pull Request with the bump version changes targeting the
mainbranch; - When the Pull Request is merged, a new Git tag must be created using GitHub environment.
Releases to productive environments should run from a tagged version. Exceptions are acceptable depending on the circumstances (critical bug fixes that can be cherry-picked, etc.).
- changed the Go module dependencies to their latest versions
- changed the Go module dependencies to their latest versions
- hardened test-fixture directory permissions from
0o755to owner-only0o700(and the executable fake-CLIChmodlikewise), resolving the Semgrepincorrect-default-permissionCI failures (a directory needs the owner execute bit, so the rule's0o600file threshold is documented as inapplicable and suppressed per line)
- changed the Go module dependencies to their latest versions
- changed the Go module dependencies to their latest versions
- added per-module log prefixing for terra-managed parallel execution (
--parallel=N): each worker's Terragrunt/Terraform stdout and stderr is now streamed through terra and prefixed with the module's directory name (e.g.[module-a]), so interleaved output from concurrent modules stays attributable. The label is colorized per module when terra's stdout is an interactive terminal (honoring theNO_COLORconvention) and printed plain when redirected to a file or pipe. Lines from different modules are serialized through a shared lock so they never interleave mid-line. Implemented with a new thread-safeLinePrefixWriterand a focusedParallelShellRepositoryport implemented byStdShellRepository; the previous behavior forwarded every worker's output to the shared console unlabeled. The feature is covered end-to-end by an integration test that runs the built terra binary against faketerragrunt/terraformstubs in a fully sandboxedHOME/PATH, asserting the[module]prefixes and that the developer's real installed binaries are left byte-for-byte unchanged (verified by SHA-256)
- changed the Go module dependencies to their latest versions
- changed the Go version to
1.26.5and updated all module dependencies
- fixed terra emitting a
The following experiment(s) are already completed: caswarning on every Terragrunt invocation (once per module under--parallel). Terragrunt graduated the Content Addressable Store (CAS) from an experiment to a stable, default-on feature in1.1, soconfigureCacheEnvironmentno longer sets the completedTG_EXPERIMENT=casopt-in. CAS is left at its default (enabled), andTERRA_NO_CAS=truenow opts out through Terragrunt's stableTG_NO_CAS=trueflag instead of unsettingTG_EXPERIMENT; any stray inheritedTG_NO_CASis cleared when CAS stays enabled. Additionally, acasentry inherited from an exportedTG_EXPERIMENTis stripped (preserving any other experiments) so the warning is fully silenced even for users who set it in older workflows
- changed the Go module dependencies to their latest versions
- fixed
terra installhanging on the dependency-update prompt (Do you want to update? [y/N]) in non-interactive sessions such as CI, where stdin is not a terminal and the prompt would block the job forever.promptForUpdatenow detects a non-interactive stdin and never blocks: setTERRA_ASSUME_YES=trueto auto-confirm updates (e.g. in a pipeline), otherwise the update is skipped with a warning; interactive sessions keep prompting as before
- scoped the
Claude CodeandClaude Code Reviewreusable-workflow calls in.github/workflows/to pass only the requiredCLAUDE_CODE_OAUTH_TOKENsecret instead ofsecrets: inherit, following the principle of least privilege
- changed the Go module dependencies to their latest versions
- changed the Go module dependencies to their latest versions
- changed the Go version to
1.26.4and updated all module dependencies
- changed the Go module dependencies to their latest versions
- refreshed
.github/copilot-instructions.mdto remove stale.golangci.ymlreference and add missingTERRA_NO_WORKSPACEandTERRA_DOWNLOAD_TIMEOUTenvironment variables to the reference section
- changed the Go module dependencies to their latest versions
- changed the Go module dependencies to their latest versions
- changed the Go module dependencies to their latest versions
- refreshed
.github/copilot-instructions.mdto fix stale SAST tool names, documentTERRA_CLOUDauto-detection from credential variables, and add queue-scoping flag forwarding to the auto-upgrade section
- changed the Go version to
1.26.3
- fixed the auto
init --upgradedetection insideUpgradeAwareShellRepositoryso it catches Terraform'sModule source has changeddiagnostic. Previously, when a module'ssourceaddress changed (for example bumping its?ref=Git tag),terra applyfailed withError: Module source has changedand the reactive retry never fired -- Terraform's hintRun "terraform init" to install all modules required by this configuration.is split across two stderr lines by Terragrunt's per-line<timestamp> STDERR <cmd>: │prefix, so the existingrun "terraform init"substring pattern ingetUpgradePatternscould not match. Added two single-line patterns (Module source has changedandsource address was changed since this module was installed) that survive the prefix wrap, plus BDD-style cases ininternal/infrastructure/repositories/upgrade_shell_repository_test.gocovering both a synthesized multi-line Terragrunt-prefixed sample and the standalone description sentence.
- fixed the auto
init --upgraderetry insideUpgradeAwareShellRepositoryso it propagates--all,--filter,--queue-include-dir,--queue-exclude-dir,--queue-include-units-reading,--queue-strict-include,--queue-include-external, and--queue-exclude-externalfrom the original command (both space and=forms). Previously a queued run such asterragrunt apply --all --queue-exclude-dir <unit>that failed with a "Module not installed" diagnostic would trigger the reactive retry asterragrunt init --upgradein the parent directory, and Terragrunt aborted withYou attempted to run terragrunt in a folder that does not contain a terragrunt.hcl filebecause--allwas missing. The retry now runs asterragrunt init --upgrade --all --queue-exclude-dir <unit>(and equivalents) so init walks the same queue and the original command is retried successfully. Added BDD-style cases ininternal/infrastructure/repositories/upgrade_shell_repository_test.gocovering the helper extraction (bare--all, space-form valued flags,--flag=valueform, end-of-list valued flags, order preservation) and an end-to-end retry that asserts the propagated flags reach the wrapped script.
- added
TERRA_DOWNLOAD_TIMEOUTenvironment variable to override the per-download deadlineterra installapplies to the Terraform / Terragrunt fetch (default10m). Accepts anytime.ParseDurationvalue (30m,1h,20m30s); malformed or non-positive values fall back to the default and log a warning. Resolves the silentfailed to write downloaded content to file: context deadline exceededfailure on slow transports -- corporate proxies, low-bandwidth links, and especially QEMU-emulated multi-arch container builds where syscall overhead pushes the Terragrunt body read past the previously hardcoded 10-minute ceiling.
- changed
NewCLI()ininternal/domain/entities/cli.goto auto-detect the cloud-CLI adapter from the cloud-specific credential variables whenTERRA_CLOUDis not set. A non-emptyTERRA_AZURE_SUBSCRIPTION_IDselects the Azure adapter; a non-emptyTERRA_AWS_ROLE_ARNselects the AWS adapter. PreviouslyNewCLI()returnednilwheneverTERRA_CLOUDwas empty, so consumers had to set BOTHTERRA_CLOUD=azureANDTERRA_AZURE_SUBSCRIPTION_ID=<id>for terra to runaz account set --subscription <id>before each command -- redundant, and easy to miss (consumer pipelines kept hitting cross-stack subscription leaks because settingTERRA_AZURE_SUBSCRIPTION_IDalone did nothing). Selection precedence: explicitTERRA_CLOUDwins (backwards-compatible); credential-variable auto-detection on fallthrough; if BOTH credential variables are populated ANDTERRA_CLOUDis empty, log a warning and returnnilrather than guess (operator must disambiguate). Added BDD-style cases in a newinternal/domain/entities/cli_test.gocovering each selection branch + the precedence + ambiguity scenarios; subsumed the prior 3-caseTestNewCLIblock insettings_test.go.
- changed the Go module dependencies to their latest versions
- changed the Go module dependencies to their latest versions
- refreshed
CLAUDE.mdand.github/copilot-instructions.mdto document the--yes/--noconfirmation flags and the--replydeprecation from v1.15.0, and removed references to non-existent test directories
- added
--yes/-yand--no/-nconfirmation flags that translate to native Terraform and Terragrunt flags.--yesinjects Terragrunt's--non-interactiveplus Terraform's-auto-approveforapply/destroy;--noinjects only--non-interactive, so Terraform's apply prompt aborts instead of proceeding. This aligns terra with theapt/npm/azconvention and replaces the previous PTY-based approach with reliable flag injection.
- changed the Go module dependencies to their latest versions
- changed the validation error for
--parallelwithapply/destroyfrom "--replyis required" to "--yesis required", matching the new flag names in error messages and copy-pasteable suggestions.
- deprecated the
--reply/-rflags. They still work and are translated to the new--yes/--noflag injection (--reply=yand bare--replymap to--yes;--reply=nmaps to--no), but now emit a one-time migration warning.--replywill be removed in a future release.
- fixed
terra apply --reply=ysilently waiting forever on Terraform's "Do you want to perform these actions? Enter a value:" prompt. The previous PTY-based auto-responder only matched[y/n]and "external dependency" prompts, so Terraform's apply confirmation (which requires the literal wordyes) was never answered. Users had to fall back to-auto-approve. The new flag-injection path invokes-auto-approvenatively, soterra apply --yes(and the deprecated--reply=y) now work reliably.
- fixed a flaky
TestUpgradeAwareShellRepository_ExecuteCommandWithUpgradesubtest that intermittently failed in CI withfork/exec ...: text file busy. The test writes an executable shell script and immediately runs it; undert.Parallel()another goroutine could inherit to write fd during a fork, soexecvesaw the inode as still open-for-write and returnedETXTBSY(seegolang/go#22315). Script creation now pipes through a subprocess, so no write fd for the script ever lives in the test process. - fixed the centralized provider cache being silently bypassed: terra now sets
TG_NO_AUTO_PROVIDER_CACHE_DIR=truewhenever the Provider Cache Server is enabled, so Terragrunt0.99+'s CAS-auto-enabledauto-provider-cache-direxperiment stops overridingTG_PROVIDER_CACHE_DIR. Before this fix, providers were duplicated intoTG_DOWNLOAD_DIR/<hash>/.../.terraform/providers/per go-getter source,~/.cache/terra/providers/stayed empty, and every new stack or concurrent terminal paid a full provider download. After the fix, providers download once and are shared across every stack, repo, and terminal until the version changes. Opt out withTERRA_NO_PROVIDER_CACHE=true.
- changed the Go module dependencies to their latest versions
- changed the Go module dependencies to their latest versions
- added a non-fatal warning when Terragrunt-only flags (
--filter,--queue-exclude-dir,--queue-include-dir) are combined with terra's--parallel=N, since they are silently ignored by terra's worker pool; the warning nudges users toward--only/--skipor toward switching to--all
- changed the
--only/--skipvalidation error to echo the user's command and show both valid forms (--parallel=N --skip=mod1and--all --filter='!mod1'), teaching the--filteralternative for the--allpath instead of leaving users to discover terragrunt's native flags on their own - changed the
--parallel+--allconflict error to echo the user's command and offer both alternative forms as copy-pasteable examples - changed the
terra --helptext to include a "Parallel execution strategies" block that summarizes when to use--parallel=Nversus--all, making the split discoverable without reading the docs - changed the Go version to
1.26.2and updated all module dependencies
- added automatic version check on CLI startup using
CheckForUpdates()
- changed the Go module dependencies to their latest versions
- added
--replyrequirement when using--parallelwithapplyordestroyto prevent workers from hanging on interactive prompts; for terra-managed parallel, just--reply(no value) is sufficient since terra always injects--non-interactivewhen--replyis present and adds-auto-approveautomatically for interactive commands likeapplyanddestroy - added documentation for the Git
refs/files-backend.crace condition that occurs during parallel execution with shared dependencies, including root cause analysis and workarounds - added validation requiring
--reply=<value>(with explicit value) when used with--all, since the PTY auto-answering needs to know whether to respond "y" or "n" - added warning when
--reply=<value>is used with--parallel, informing the user the value is ignored and only meaningful with--all(Terragrunt-managed parallelism)
- changed
--allflag to always forward to Terragrunt (no longer intercepted by terra for state commands); use--parallel=5instead for terra-managed parallel state operations - changed
--includeflag to--onlyand--excludeflag to--skipfor terra's parallel module selection, eliminating name collisions with terragrunt's own--include/--excludeflags - changed
cliforgeimport paths to reflect upstream package restructuring - changed self-update command to delegate to
cliforge/selfupdateshared library, removing ~300 lines of duplicated GitHub API, archive extraction, and binary replacement logic
- fixed parallel
apply/destroywith--replynot injecting-auto-approve, causing terraform to prompt for confirmation and hang workers - fixed parallel module discovery descending into
.terragrunt-cacheand other hidden directories, which caused hundreds of cached dependency modules to be processed as actual targets
- removed
--auto-answer/-aflags; replaced with--reply/-rto avoid collision with Terragrunt's-ashorthand for--all - removed
--no-parallel-bypassflag (--allnow always forwards to Terragrunt; use Terragrunt's--parallelism=Ndirectly for Terragrunt-managed parallelism) - removed legacy
--allsupport for state commands (import,state rm, etc.); use--parallel=Ninstead
- added validation for
--include/--excludeflag combinations with--parallel,--no-parallel-bypass, and conflict detection
- changed
--filterflag to separate--includeand--excludeflags for parallel execution, eliminating Bash shell escaping issues with the!exclusion prefix
- changed the Go module dependencies to their latest versions
- added
TERRA_NO_PROVIDER_CACHEenvironment variable to disable the Terragrunt Provider Cache Server (opt-out toggle)
- changed provider caching strategy: replaced
TF_PLUGIN_CACHE_DIRwith Terragrunt Provider Cache Server (TG_PROVIDER_CACHE=1+TG_PROVIDER_CACHE_DIR) to fix "text file busy" errors during parallel execution (--parallel=N)
- fixed upgrade-aware retry triggering
init --upgradeafter user-canceled apply/plan/destroy operations
- added
TERRA_NO_WORKSPACEenvironment variable to disable automatic workspace selection fromTERRA_WORKSPACE - added unit tests for DIG container registration, self-update command, run-from-root command, upgrade-aware shell repository, version command, and OS operations
- changed
clearcommand to also removeterragrunt-cache(without leading dot) and.terraform.lock.hcllock files
- fixed
clearcommand not resetting found paths between iterations, causing already-deleted entries to be re-processed - fixed
RunAdditionalBeforeCommandtests using hard-coded/test/pathinstead oft.TempDir(), making them environment-dependent - fixed auto-init running
terragrunt initon every command even when.terraformdirectory already exists - fixed auto-upgrade detection logging which pattern triggered the retry, aiding future debugging
- fixed overly broad auto-upgrade detection that triggered unnecessary
init --upgradeon runtime provider errors (e.g., TLS failures) - fixed proactive init not detecting
.terragrunt-cacheand legacyterragrunt-cachedirectories, causing unnecessaryterragrunt initon every command when using Terragrunt - fixed proactive init running unnecessarily when centralized caching (
TG_DOWNLOAD_DIR) is active and cache already has content
- added execution timing to command logs showing how long each Terragrunt invocation took
- changed provider caching strategy: removed
TG_PROVIDER_CACHE(Provider Cache Server) in favor ofTF_PLUGIN_CACHE_DIRonly, which benchmarks showed is faster (8.9s vs 10.6s warm) with identical disk savings via symlinks - changed version management to use build-time
ldflagsinjection instead of hardcoded constant
- fixed
terra self-updatefailing due to incorrect asset name matching (expectedterra_os_archbut releases useterra-version-os-arch.tar.gz) and missing archive extraction - fixed Terragrunt deprecation warning by replacing
TERRAGRUNT_USE_PARTIAL_PARSE_CONFIG_CACHEwithTG_USE_PARTIAL_PARSE_CONFIG_CACHE
- removed
TERRA_NO_PROVIDER_CACHEenvironment variable (Provider Cache Server replaced byTF_PLUGIN_CACHE_DIR)
- changed the Go module dependencies to their latest versions
- added Terragrunt Partial Parse Config Cache enabled by default (
TG_USE_PARTIAL_PARSE_CONFIG_CACHE=true) for faster HCL config parsing across modules sharing the same root include; disabled withTERRA_NO_PARTIAL_PARSE_CACHE=true - added Terragrunt Provider Cache Server enabled by default (
TG_PROVIDER_CACHE=1) for localhost proxy-based provider deduplication via symlinks; disabled withTERRA_NO_PROVIDER_CACHE=true
- changed the Go version to
1.26.1and updated all module dependencies - replaced raw struct literals in tests with
testkitbuilders for consistent test data construction
- fixed opt-out toggles (
TERRA_NO_CAS,TERRA_NO_PROVIDER_CACHE,TERRA_NO_PARTIAL_PARSE_CACHE) to explicitly unset the corresponding environment variables, ensuring deterministic behavior when the parent environment has pre-existing values
- removed cross-platform file locking mechanism (
gofrs/flock) that prevented running multiple terra instances simultaneously from the same repository; CAS and centralized caching make it unnecessary
- fixed cross-compilation failure for
darwin(macOS) targets by replacing platform-specificos_linux.gowithos_unix.gousing//go:build !windowsconstraint, and renamedOSLinuxtoOSUnix
- added auto-initialization with upgrade detection: when Terragrunt commands fail due to uninitialized modules, backend changes, or provider version conflicts, terra automatically runs
init --upgradeand retries the original command
- migrated test builders to use the
testkitlibrary (github.com/rios0rios0/testkit) for standardized builder patterns - renamed
cmd/terra/wire.gotocmd/terra/dig.goto reflect the actual DI tool in use (Uber DIG, not Google Wire)
- added
--auto-answerflag (-a) to automatically handle Terragrunt prompts, with configurable responses (--auto-answer=yor-a=n; defaults to "n" for backward compatibility) - added
--globalflag to theclearcommand to also remove centralized cache directories - added
updatecommand as an alias forinstall - added
versioncommand to display Terra, Terraform, and Terragrunt versions - added centralized Terragrunt module and provider caching (
TG_DOWNLOAD_DIR,TF_PLUGIN_CACHE_DIR) configured automatically before every invocation, with override viaTERRA_MODULE_CACHE_DIRandTERRA_PROVIDER_CACHE_DIR - added cross-platform file locking via
gofrs/flockto prevent race conditions when multiple terra processes run concurrently from the same repository - added dependency injection (first Wire, then DIG) and inverted all dependencies
- added installation shell script (
install.sh) for automated terra installation from GitHub releases with platform detection,--version,--force,--dry-run, and--install-diroptions - added parallel execution for any command via
--parallel=N, including state manipulation commands (import,state rm,state mv,state pull,state push,state show) with--all, automatic module discovery, configurable concurrency (default: 5 jobs), and error aggregation - added self-update feature to update the CLI without any additional step
- added Terragrunt CAS (Content Addressable Store) enabled by default for Git clone deduplication via hard links; disabled with
TERRA_NO_CAS=true - added validation on the
settingsentity
- changed the documentation with pipelines and minor change to template files
- corrected controllers responsibilities mapping the external to internal entities
- corrected the structure to follow best practices using DDD
- decoupled responsibilities from just one command to other layers
- moved all business logic to the domain structure
- replaced deprecated
run-allcommand syntax with--allflag to align with Terragrunt's new syntax - replaced Wire with DIG for dependency injection to support Go
1.25.1and active maintenance - updated
.editorconfigto enforce LF line endings across all file types instead of just Go files - updated Copilot instructions and contributing guide to enforce
LF(Unix) line endings for all new and edited files - updated Copilot instructions to use
rios0rios0/pipelinesproject for linting and CI tools instead of direct tool installation - updated documentation to require
CHANGELOG.mdupdates for new features and bug fixes (not required for documentation-only changes) - upgraded the project to Go
1.26and all the dependencies
- fixed optional environment variables validation for
TERRA_CLOUDto allow empty values while still enforcing valid values when provided - fixed permission denied errors when normal users try to download dependencies via
terra installorterra updateby using unique temporary file creation instead of predictable file names - fixed slice bounds error in
ArgumentsHelperwhen no arguments are provided - fixed the issue where version checks for Terraform and Terragrunt were triggered on every command execution, causing unnecessary network calls and slowdowns
- fixed version checks to only occur when explicitly running "install" or "update" commands
- added a new environment variable to handle Azure subscriptions
- fixed the required workspace flag to be optional
- added the
godotenvto handle the environment variables
- changed the main command to accept input from the user and wait for the
stdinto be closed - changed to forward unknown flags to Terraform and Terragrunt - #3
- added the
clearcommand to remove the cache and temporary files - added the
fmtcommand to format all Terraform and Terragrunt files
- created the first version working properly and installing all dependencies