fix: only require a managed plan file for managed apply workflows - #6781
Merged
Conversation
Atlantis 0.46.0 (#6605) added apply-time plan validation that runs in doApply for every project, before any apply step executes. The validation targets the Atlantis convention plan artifact, <workspace>.tfplan. A workflow whose plan and apply are built entirely from custom run steps writes its plan wherever its commands choose, for example atlantis.tfplan. For those projects the convention file does not exist, so apply failed with: plan file is missing for dir "." workspace "default" project ""; run `atlantis plan` The same assumption broke a second path: when ExpectedPlanHash was empty, doApply hashed the convention plan file to pin it for the remainder of the command, which also fails when that file was never created. Derive whether Atlantis owns the plan artifact from the workflow shape. A workflow owns it when it uses the built-in plan step (Atlantis writes the file) or the built-in apply step (Atlantis reads it). Workflows built only from custom run steps manage their own plan file, so Atlantis no longer requires, hashes, or removes one for them. Durable plan state is still validated for every project. ValidateProjectPlan is split so custom-plan-path workflows go through ValidateProjectPlanStatus, which checks recorded plan status, apply eligibility, head and base identity, without touching an artifact on disk. Artifact removal on rejection is unchanged for managed workflows: the status failures that discarded the plan file before still do, and stale command head errors still leave it in place so a concurrent replica's plan is not deleted. Fixes #6642
Code Coverage OverviewLanguages: Go Go / code-coverage/goThe overall coverage in commit a25eb23 in the Show a code coverage summary of the most covered files.
|
rossstr-brainco
approved these changes
Aug 18, 2026
pseudomorph
approved these changes
Aug 18, 2026
This was referenced Sep 7, 2026
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.
Fixes #6642.
What broke
ApplyPlanValidatorwas introduced in #6605 and first released in v0.46.0, which matches the bisect reported in #6642 (0.43.0 fine, 0.46.0 broken).doApplyvalidates the plan for every project before any apply step executes:The path it validates is the Atlantis convention artifact, derived from
runtime.GetPlanFilename:A workflow whose
planandapplyare built entirely from customrunsteps writes its plan wherever its commands choose —atlantis.tfplanin the reported case. That project has no<workspace>.tfplan, so apply failed with:The same assumption broke a second path immediately below: when
ExpectedPlanHashwas empty,doApplycalledhashFileon that same nonexistent convention path to pin it for the rest of the command.Note the sibling call inside the built-in
applystep case is correct — it only runs for managed applies. The bug is that thedoApplycall fires for everyone.The fix
Derive whether Atlantis owns the plan artifact from the workflow shape. Atlantis owns it when the workflow uses the built-in
planstep (Atlantis writes the file) or the built-inapplystep (Atlantis reads it):Workflows built only from custom
runsteps manage their own plan file, so Atlantis no longer requires, hashes, or removes one for them.Durable plan state is still validated for every project.
ValidateProjectPlanis split so custom-plan-path workflows go through the newValidateProjectPlanStatus, which still enforces recorded plan status, apply eligibility, and head/base identity — it simply does not touch an artifact on disk.Artifact removal on rejection is unchanged for managed workflows. The status failures that discarded the plan file before still do; stale command head errors still leave it in place so a concurrent replica's plan is not deleted, and
errStaleCommandHeadremains unwrapped so theerrors.Isshort-circuit inapply_command_runnerkeeps working.The gate fails closed — the steps actually being executed are authoritative, so a
ProjectContextbuilt outside the builder (targeted apply, API paths, tests) still validates the plan file when a built-in apply step will read it:An earlier revision of this branch gated only on the context field; the existing
TestProjectCommandRunner_ApplyRejectsPlanDeletedAfterBuilderValidationcaught that a directly-constructed context defaulted tofalseand skipped validation. Hence the fail-closed form.Tests
TestDefaultProjectCommandRunner_ApplyCustomPlanPathWorkflow— reproduces Regression (v0.46.0): atlantis apply fails with "plan file is missing" for custom workflows #6642. Fails onmainwith the exact reported error; passes here.TestDefaultProjectCommandRunner_ApplyManagedPlanFileStillRequired— guards against over-permitting: a workflow using the built-inapplystep with a missing convention plan file is still rejected.Verification
The
-raceresult was compared against amainworktree run under identical scope.Review note
project_command_context_builder.goshows ~132 changed lines, but that is gofmt struct-literal realignment around a single added field. Please review it withgit diff -w, which reduces it to the one real edit plus the new predicates.Scope
Deliberately minimal and backportable to 0.46.x — no dependency on plan generations, publication claims, or S3 changes.
This extracts the #6642 regression fix from #6657, which bundles it with a much larger durable-plan-state effort (plan generations, a publication claim, S3 digest binding, validated snapshots, a recovery CLI). That work is worth pursuing separately; this PR is only the regression.
Two follow-ups intentionally left out:
rejectProjectPlandeletes the plan file when validation fails. That is aggressive independent of this bug and predates it, so it is not touched here.TestBuildProjectCmdCtxcompares wholeProjectContextvalues, so adding any field produces a multi-hundred-megabyte failure diff that reads as a hung test rather than a failure. Worth making that comparison field-scoped before more fields land.