Motivation
snapshot-review renders the full deployment tree twice (baseline + PR). On a consumer repo with ~115 deployments the workflow takes ~4:20 today; opened up to application developers adding their own deployments, both the tree size and the PR rate grow — full-twice rendering scales as O(repo) per PR, so total CI load grows roughly quadratically. Target: feedback time proportional to the change, not the repo.
This is a design issue for later implementation. Three building blocks, ordered by risk (the first two are correctness-free and should land first).
1. Baseline snapshot cache (flat ~2× win, zero risk)
Baseline renders are deterministic per (target-branch sha, toolchain/action version). Cache the rendered baseline as a workflow artifact keyed on those and replay it in PR runs instead of re-rendering.
- Store snapshots redacted: markers are deterministic (
REDACTED:sha256:<12hex> of the value), so diffs against a redacted PR-side render align exactly — and the cached artifact contains no secret material at rest.
- First run after a target-branch push (or a toolchain change) renders and populates; subsequent PRs replay.
- Invalidation key must include the atlas-render image identity, not just the ref.
2. Prebuilt action images (flat 30–60s+ win, zero risk)
atlas-render / atlas-diff are Dockerfile actions built on every job. Publish them per release/main-sha and reference the image directly (docker://), removing the per-run build.
3. Changed-file render subsetting (the big win, needs care)
The directory convention is the dependency graph, so changed paths classify mechanically:
| Changed path |
Affected render set |
deployments/<scope>/apps/<name>/** |
deployment <name> at that scope's fan-out (cluster → 1, group → group's clusters, global → all clusters) |
deployments/<...>/cluster.values.* |
all deployments of that cluster |
deployments/<group>/group.values.* |
all deployments in that group |
deployments/global.values.* |
full render |
templates/<t>/** |
all deployments instantiating template <t> (deployment→template map = cheap yq pass over all deployment.yaml files, no render needed) |
| entry helmfile / atlas version ref |
full render |
| anything unclassifiable |
full render (default-deny) |
Classifier requirements:
- Evaluate against both revisions (deletions/moves invalidate their old scope; a cluster-level deployment.yaml added/removed flips most-specific-wins shadowing — mapping by deployment name on both sides catches this). Input is
git diff --name-status base...head.
- Union of per-file sets; any single "full" wins.
- Both sides render the same subset.
Execution: ATLAS_FILTER_CLUSTER / ATLAS_FILTER_DEPLOYMENT_NAME are the stage-1 short-circuit this needs, but they are single-valued (eq) today → prerequisite: multi-value filter support (comma-separated list) so one invocation renders a set of (cluster, deployment) pairs without repeated state builds.
Known failure mode: false negatives
A change affecting a render the classifier did not select produces a silent green PR. The only conventional-layout escape is readFile/tpl reaching outside the owning directory (shared snippets). Mitigations, in trust order:
- Shadow mode first: compute the subset, still render full, assert subset-diff ≡ full-diff on every PR for an extended period; log discrepancies. Switch over only on empirical evidence.
- Escape hatches, permanent: a PR label forcing full render + a scheduled full render of the target branch (bounds any classifier bug to one day).
- Optional lint: constrain
readFile targets to the template's own directory, turning the convention the classifier depends on into an enforced contract.
The review comment should state the subset transparently ("rendered N of M deployments; rules: ...") so a surprising green sheet is auditable.
Long-term: atlas emits the dependency map
The path→(cluster, deployment) mapping is the same knowledge consumer-side render-optimization annotations (Argo CD manifest-generate-paths-style) encode by hand. Atlas should emit this map itself (it already knows deployment→template and hierarchy scopes at discovery time) as a machine-readable artifact, consumed by both the review subsetter and consumer annotations — one source of truth instead of two drifting approximations.
Expected outcome (measured baseline: ~4:20 full-twice)
- Blocks 1+2 alone: well under 2 minutes, no correctness risk.
- With subsetting — typical single-deployment PR (the dominant case once developers self-serve): ~15–30s; template-touching PR: ~1–2 min; framework/global PR: unchanged full render (rare, intentionally).
Rollout order
- Prebuilt images → 2. baseline cache → 3. multi-value ATLAS_FILTER_* → 4. classifier in shadow mode → 5. cutover with escape hatches → 6. atlas-emitted dependency map.
Motivation
snapshot-reviewrenders the full deployment tree twice (baseline + PR). On a consumer repo with ~115 deployments the workflow takes ~4:20 today; opened up to application developers adding their own deployments, both the tree size and the PR rate grow — full-twice rendering scales as O(repo) per PR, so total CI load grows roughly quadratically. Target: feedback time proportional to the change, not the repo.This is a design issue for later implementation. Three building blocks, ordered by risk (the first two are correctness-free and should land first).
1. Baseline snapshot cache (flat ~2× win, zero risk)
Baseline renders are deterministic per (target-branch sha, toolchain/action version). Cache the rendered baseline as a workflow artifact keyed on those and replay it in PR runs instead of re-rendering.
REDACTED:sha256:<12hex>of the value), so diffs against a redacted PR-side render align exactly — and the cached artifact contains no secret material at rest.2. Prebuilt action images (flat 30–60s+ win, zero risk)
atlas-render/atlas-diffare Dockerfile actions built on every job. Publish them per release/main-sha and reference the image directly (docker://), removing the per-run build.3. Changed-file render subsetting (the big win, needs care)
The directory convention is the dependency graph, so changed paths classify mechanically:
deployments/<scope>/apps/<name>/**<name>at that scope's fan-out (cluster → 1, group → group's clusters, global → all clusters)deployments/<...>/cluster.values.*deployments/<group>/group.values.*deployments/global.values.*templates/<t>/**<t>(deployment→template map = cheap yq pass over all deployment.yaml files, no render needed)Classifier requirements:
git diff --name-status base...head.Execution:
ATLAS_FILTER_CLUSTER/ATLAS_FILTER_DEPLOYMENT_NAMEare the stage-1 short-circuit this needs, but they are single-valued (eq) today → prerequisite: multi-value filter support (comma-separated list) so one invocation renders a set of (cluster, deployment) pairs without repeated state builds.Known failure mode: false negatives
A change affecting a render the classifier did not select produces a silent green PR. The only conventional-layout escape is
readFile/tplreaching outside the owning directory (shared snippets). Mitigations, in trust order:readFiletargets to the template's own directory, turning the convention the classifier depends on into an enforced contract.The review comment should state the subset transparently ("rendered N of M deployments; rules: ...") so a surprising green sheet is auditable.
Long-term: atlas emits the dependency map
The path→(cluster, deployment) mapping is the same knowledge consumer-side render-optimization annotations (Argo CD manifest-generate-paths-style) encode by hand. Atlas should emit this map itself (it already knows deployment→template and hierarchy scopes at discovery time) as a machine-readable artifact, consumed by both the review subsetter and consumer annotations — one source of truth instead of two drifting approximations.
Expected outcome (measured baseline: ~4:20 full-twice)
Rollout order