Repository navigation
Take a pinned version without resolving it - #446
Open
benlangfeld wants to merge 2 commits into
Open
benlangfeld wants to merge 2 commits into
benlangfeld wants to merge 2 commits into
Conversation
terraform-backend loads the whole root module in order to read one backend block. The cost scales with the size of the module rather than with the thing being looked up, and on a large root module that is most of a minute before terraform has been asked to do anything. A block cannot appear in a file that does not mention its name, so the files worth parsing can be picked out by a substring check first. Files that mention a word only in a comment or a string are still parsed, so the result is unchanged; the scan only decides what to skip, never what the answer is. A file that cannot be read is treated as a candidate so that the usual parsing and error handling still applies to it. On a generated 261 file module with one backend block, reading the backend type goes from 34.07s to 0.20s for the same answer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 23, 2026
determine_version fetches the release index as its first statement and loads the module a few lines later, both before anything has looked at what the version actually needs. When the version is pinned exactly there is nothing to resolve, and loading the module means parsing every file in it. Decide it from what can be read cheaply instead. Only files mentioning required_version, backend or cloud can say whether the version is pinned, so parsing those answers it - on a real 245 file module that is 1.7s rather than 32s, and the release index is not fetched at all. The order sources are considered in is unchanged, which is what the gating is for. A remote or cloud backend means the workspace version outranks the pin, so the shortcut is skipped and the usual resolution runs. An exact required_version otherwise wins, as it already would. Only when the module requires nothing does the environment get a say, and then only if no version file is present, since those outrank it too. Constraints are untouched - a range still needs the available versions, so it still fetches them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
benlangfeld
force-pushed
the
exact-version-fast-path
branch
from
September 23, 2026 15:03
e7ffcf9 to
ff0c07a
Compare
benlangfeld
marked this pull request as ready for review
September 23, 2026 15:10
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the suggestion in #443 (comment), and then some — it now reads the pin from the module rather than needing anything set in the workflow.
The problem
determine_versiondoes two expensive things unconditionally, before anything has looked at what the version actually needs:When the version is pinned exactly there is nothing to resolve, and
load_modulemeans parsing every.tffile in the root module.The change
Work out whether the version is pinned using only what can be read cheaply. Just three block names can settle it, so only files mentioning one of them are parsed:
An exact
required_version— one=constraint naming major, minor and patch — needs no resolving, so it is used directly and the release index is never fetched.TERRAFORM_VERSION/OPENTOFU_VERSIONnaming a version exactly serves as a fallback when the module requires nothing.Precedence is unchanged
This was the open question on the previous revision of this PR, which read the environment variable only and so jumped it above five sources that outrank it. Reading the pin from the module removes the problem, because the gating can now be decided from the same cheap parse:
remoteorcloudrequired_version.tfswitchrc.terraform-version/.opentofu-version.tool-versionstry_read_asdfdoesTERRAFORM_VERSIONSo the shortcut is only taken where it agrees with what the usual resolution would have chosen. No workflow that works today changes behaviour, and the
TERRAFORM_VERSION_EXACTvariable I offered as an alternative is no longer needed.Effect
End to end on the real 245 file root module from #443, with
required_version = "1.16.3"and nothing set in the workflow:Against 32.07s for the full parse it replaces, plus the index fetch. Constraints are untouched: a range still needs the available versions and still fetches them.
Correctness notes
Version()reads a prefix, soVersion('1.16.3, <2.0.0')would silently yield1.16.3; there is a test for exactly that.1.16is not an exact pin —Constraint.patchisNone— so it still resolves normally.Tests
Eighteen in
tests/terraform_version/test_exact.py: exact pins including pre-releases, six constraint shapes left alone, the product following the action, and one per precedence rule in the table above — remote backend, cloud block, each of the three version files, pin beating the environment, environment as fallback, and nothing named at all.pytest tests --ignore=tests/github_pr_commentgives 171 passed against 147 onmain, with the same 2 failures and 7 errors before and after — those need S3 and environment I do not have.tests/github_pr_commentfails to collect identically before and after, on a missingTERRAFORM_ACTIONS_*variable.🤖 Generated with Claude Code