Skip to content

Wire atmos terraform generate planfile into the atmos ci hook lifecycle #2497

@thejrose1984

Description

@thejrose1984

Describe the Feature

atmos terraform generate planfile currently bypasses the atmos ci integration entirely. The command writes a JSON/YAML representation of a Terraform plan to disk, but unlike atmos terraform plan/apply/deploy, it does not fire any CI hooks. As a result, none of the CI features that already exist for plan/apply — job summary, CI outputs, planfile upload to storage, GitHub commit status / check run, or PR comments — are available when a user uses generate planfile in CI.

This issue tracks wiring generate planfile into the same hook lifecycle that plan already uses, so that running it in CI produces the same artifacts and surface area.

Expected Behavior

When atmos terraform generate planfile -s <stack> <component> runs inside CI (or with --ci / ATMOS_CI=true):

  1. A before.terraform.generate.planfile hook fires before the command runs.
  2. The generated JSON/YAML planfile (and, optionally, the underlying binary *.tfplan produced internally) is uploaded to the configured planfile store (S3 / GitHub Artifacts / local) via the same Components.Terraform.Planfiles configuration used by plan.
  3. A job summary is written using a generate-planfile.md template (or aliased to plan.md) under pkg/ci/plugins/terraform/templates/.
  4. CI outputs (stack, component, has_changes, resource counts, etc.) are emitted via the platform's output writer.
  5. A commit status / check run is created and updated with the result.
  6. Optionally, a PR comment is posted using the same template.
  7. An after.terraform.generate.planfile hook fires on success or failure (matching the runHooks / runHooksOnErrorWithOutput pattern from cmd/terraform/plan.go).

Use Case

Today, teams that want a JSON-shaped plan artifact in CI (for audit, OPA/Conftest policy evaluation, manual review, etc.) have to either:

  • Run atmos terraform plan and post-process the canonical binary planfile, or
  • Run atmos terraform generate planfile and then separately invoke atmos terraform planfile upload and re-create their own summary/status logic.

The first path forces a binary-then-JSON two-step; the second path duplicates the CI plumbing that already exists for plan. Wiring generate planfile into the hook lifecycle gives users a single, declarative CI integration that works regardless of which terraform subcommand is being driven.

Describe Ideal Solution

Minimal changes:

  • Add BeforeTerraformGeneratePlanfile / AfterTerraformGeneratePlanfile to pkg/hooks/event.go.
  • Bind the new events in pkg/ci/plugins/terraform/plugin.go GetHookBindings(), reusing onBeforePlan / onAfterPlan semantics. Adjust uploadPlanfile to either:
    • upload the JSON/YAML output as an additional FileEntry alongside the binary plan.tfplan, or
    • upload it under a distinct key dimension (e.g. kind=plan-json) so it does not collide with the binary planfile produced by terraform plan.
  • Stop deleting the binary -out file when --ci is active in internal/exec/terraform_generate_planfile.go (currently removed by defer os.RemoveAll(tmpDir)), so the upload step has something to ship.
  • Wrap RunE in cmd/terraform/generate/planfile.go with the same PreRunE / PostRunE runHooks / runHooksWithOutput pattern used by cmd/terraform/plan.go.
  • Add a --ci flag (mirroring cmd/terraform/plan.go:129-132) with pkg/ci.IsCI() autodetection, and tee terraform stdout/stderr through ansi.Strip for templates.
  • Add a generate-planfile.md template (or alias to plan.md) under pkg/ci/plugins/terraform/templates/.
  • Cover the new events in pkg/ci/executor_test.go and cmd/terraform/utils_hooks_test.go (parallel to the existing runCIHooksForPlanComponent coverage).

Alternatives Considered

  1. Document a two-command workflow — keep generate planfile as-is and tell users to run atmos terraform planfile upload after. Rejected because it duplicates CI plumbing (summary/checks/comments are not produced) and forces users to re-implement what already exists for plan.
  2. Promote generate planfile JSON to a sub-resource of the plan artifact — emit it only as a side effect of atmos terraform plan with a --json / --also-emit-json flag. Rejected because it conflates two commands and breaks workflows that intentionally call generate planfile standalone (e.g., policy gates that do not need to run a full plan).
  3. Add an atmos ci generate-planfile orchestrator command — a new subcommand that wraps generate planfile and runs the upload/summary/checks itself. Rejected because the rest of the terraform commands wire CI through hook events; introducing a parallel orchestrator would split the model.

Additional Context

Relevant code references:

Related but distinct:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions