Skip to content

JIT: Option to always re-pull JIT-vendored sources via force_pull #2135

@zack-is-cool

Description

@zack-is-cool

Describe the Feature

Add a configuration option — at either the global atmos.yaml level or per-component source: block — that instructs Atmos to always re-pull from the remote source when a JIT-vendored component is used, rather than reusing an existing .workdir directory.

Expected Behavior

When the option is enabled, every atmos terraform $command would fetch the latest content from the configured source.uri + source.version, even if a .workdir for that component already exists on disk. The stale .workdir would be replaced rather than reused.

When the option is disabled (current default), behavior is unchanged — existing .workdir contents are reused as a cache.

Use Case

During active development of a JIT-vendored Terraform module, the inner loop looks like:

  1. Make a change to the upstream module and push it to the remote.
  2. Run atmos terraform plan <component> -s <stack> locally to validate.

With the current behavior, Atmos detects that a .workdir already exists and skips the pull entirely. The developer is silently running against stale code. The only workarounds today are:

  • Manually delete .workdir before every plan (rm -rf .workdir/terraform/<component>).
  • Run atmos terraform source pull --force as a separate step before every plan.
  • Remember to do this — which they won't, and which breaks CI parity.

This is especially painful when:

  • The source.version is a branch or floating ref (e.g., main, develop) rather than a pinned tag, where "latest" genuinely changes.
  • Multiple engineers are iterating on the same upstream module and expect their colleagues' pushes to be picked up automatically.
  • CI pipelines run in ephemeral environments where .workdir doesn't persist, but local developer environments do — creating a behavioral gap.

Describe Ideal Solution

components:
  terraform:
    source:
      force_pull: true   # re-pull on every command invocation; default: false

Option 2 — Per-component source: block override

components:
  terraform:
    my-module-iac:
      source:
        uri: "git::https://github.com/org/repo.git"
        version: "main"
        force_pull: true   # overrides global default for this component only
      provision:
        workdir:
          enabled: true
      metadata:
        component: "my-module"

Both options should be composable: a global force_pull: false with a per-component force_pull: true lets teams pin stable components while keeping actively-developed ones fresh.

A CLI flag override would also be welcome for one-off use without changing config:

atmos terraform plan my-module-iac -s demo-dev --jit-pull-strategy=always

Alternatives Considered

1. Always run atmos terraform source pull --force manually before plan

Problem: Requires discipline and adds a manual step. Breaks when forgotten. Not enforceable in a shared workflow. Also currently affected by the workdir path mismatch bug (see related issue).

2. Delete .workdir before running

Problem: Blunt instrument — nukes all cached components, not just the one being developed. Slow in monorepos with many JIT components. Error-prone.

3. Use a pinned version/tag instead of a branch ref

Problem: Doesn't apply during the development cycle itself, where by definition the "version" is work-in-progress and not yet tagged. Forces an artificial tagging overhead for every iteration.

4. Content-hash or git-sha staleness check

A more sophisticated alternative to a hard force_pull would be a staleness check: compare the commit SHA of the remote ref against what was last pulled, and only re-pull on mismatch. This would be ideal for floating refs like branch names and avoids unnecessary network traffic for pinned tags. This could be a separate, complementary option:

source:
  uri: "git::https://github.com/org/repo.git"
  version: "main"
  pull_strategy: "if-changed"   # options: "cached" (default) | "always" | "if-changed"

Additional Context

Demo

The following script shows what the proposed behavior would look like with force_pull: true enabled. It uses a local git repo as the upstream source so no credentials or network access are required.

Note (macOS): /tmp and /var are symlinks to /private/tmp and /private/var. The script uses pwd -P to resolve the real path so that file:// URIs are absolute and go-getter can clone them correctly.

#!/usr/bin/env bash
# ============================================================
# ATMOS FEATURE DEMO: force_pull — always re-pull JIT source
# ============================================================
set -euo pipefail

WORKDIR="$(mktemp -d -t atmos-repro-XXXXXX)"
# Resolve macOS symlinks (/var -> /private/var) so file:// URIs are absolute
WORKDIR="$(cd "${WORKDIR}" && pwd -P)"
echo "Working in: ${WORKDIR}"
cd "${WORKDIR}"

# --- 1) Create a local git repo to act as the upstream module ---
UPSTREAM="${WORKDIR}/upstream"
mkdir -p "${UPSTREAM}"
cd "${UPSTREAM}"
git init -b main
git config user.email "demo@example.com"
git config user.name "Demo"
cat <<'TFEOF' > main.tf
# version: 1
output "version" { value = "v1" }
TFEOF
git add .
git commit -m "v1"
cd "${WORKDIR}"

# --- 2) atmos.yaml with proposed force_pull setting ---
cat <<EOF > atmos.yaml
base_path: "."

components:
  terraform:
    base_path: "components/terraform"
    command: "tofu"
    deploy_run_init: true
    auto_generate_backend_file: true
    source:
      force_pull: true        # <-- proposed global setting: always re-pull on invocation

stacks:
  name_template: "{{ .vars.name }}"
  base_path: "stacks"
  included_paths:
    - "**/*"
EOF

# --- 3) Stack with a JIT-sourced component on a floating branch ref ---
mkdir -p stacks
cat <<EOF > stacks/demo.yaml
vars:
  name: demo

terraform:
  backend_type: local

components:
  terraform:
    my-module:
      source:
        uri: "git::file://${UPSTREAM}"
        version: "main"
      provision:
        workdir:
          enabled: true
EOF

# --- 4) First init — pulls v1 from upstream ---
echo
echo "=== init (v1 in upstream) ==="
atmos terraform init my-module -s demo
echo "--- .workdir contents ---"
grep -r 'value' .workdir/ --include="*.tf" || true

# --- 5) Commit v2 to upstream (simulates a teammate pushing a change) ---
echo
echo "=== committing v2 to upstream ==="
cd "${UPSTREAM}"
cat <<'TFEOF' > main.tf
# version: 2
output "version" { value = "v2" }
TFEOF
git add .
git commit -m "v2"
cd "${WORKDIR}"

# --- 6) Second init — with force_pull: true, should detect and pull v2 ---
echo
echo "=== init (v2 pushed upstream, force_pull: true should re-pull) ==="
atmos terraform init my-module -s demo
echo "--- .workdir contents ---"
grep -r 'value' .workdir/ --include="*.tf" || true

echo
echo "Done. Workspace preserved at: ${WORKDIR}"

Expected output with force_pull: true: the second init block shows v2, confirming the upstream change was picked up without manual intervention.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions