Skip to content

feat(kustomize): edit images/replicas overrides through to kustomization.yaml#198

Merged
sunib merged 2 commits into
mainfrom
feat/gitops-api-f1
Jul 6, 2026
Merged

feat(kustomize): edit images/replicas overrides through to kustomization.yaml#198
sunib merged 2 commits into
mainfrom
feat/gitops-api-f1

Conversation

@sunib

@sunib sunib commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Description

First rung of the gitops-api workstream (docs/design/gitops-api/): a live change produced by a kustomization's images:/replicas: override entry is now written back to that entry, never through into the source manifest — the edit lands where the value lives.

Previously these fields were "benign-accepted" but not understood: with an overlay pinning newTag, every event/resync wrote the rendered tag into the source file, turning the override into dead text and landing deliberate tag bumps in the wrong place. See docs/design/gitops-api/f1-images-replicas-edit-through.md for the full design.

How it works:

  • Analyzer (internal/manifestanalyzer/overrides.go): parses images:/replicas: entries; walks true render roots (a referenced base composes with its parent, innermost-first; every distinct path within a root is recorded, so diamonds trip the ambiguity refusal); attaches an unambiguous override chain per document.
  • Projection (overrides_projection.go): inverts the chain per component (image name/tag/digest, replica count), routes each divergence to its supplier (entry vs source file), verifies by re-simulating the render; anything unsafe falls back to today's write-through — never a guess.
  • Editor (manifestedit/kustomization.go): bounded, comment-preserving; only updates existing fields on existing entries; all-or-nothing.
  • Writer (plan_flush.go): patchExisting runs the projection (covers live events and resync); /scale field patches route governed spec.replicas to the entry; store-build diagnostics are logged once per write batch so silent fallbacks leave a trace.

Behavior changes:

  • Live == overlay render is now a true no-op (previously overwrote the source file every time).
  • Governed tag/image/replica changes edit kustomization.yaml; the source manifest keeps its bytes.
  • Malformed images:/replicas: values now refuse the folder (they'd fail kustomize build).
  • Known limitation documented: one entry is a shared knob; divergent live tags among its consumers are unrepresentable and the entry follows the last writer (design doc, "Known limitation").

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Documentation update

Testing

  • Unit tests pass locally (task test; coverage 73.9% → 74.5%, .coverage-baseline raised)
  • Integration tests pass locally (task test-e2e: 53 passed, 0 failed, 8 skipped)
  • New tests added for new functionality (corpus fixtures incl. diamond/ambiguous layouts; projection, editor, writer, and resync tests)

Checklist

  • My code follows the style guidelines of this project (task lint: 0 issues)
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (docs/design/gitops-api/, docs/architecture.md, docs/configuration.md "Kustomize support" section)
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules (none)

Related Issues

None — tracked in docs/design/gitops-api/README.md (F1 of the feature ladder).

Additional Notes

One images: entry is a shared knob (kustomize semantics): products built on this should present a governed version change as affecting all consumers of the entry. A cross-consumer consistency refusal needs sibling live state and is noted as F2 follow-up work.

🤖 Generated with Claude Code

…ion.yaml

A live change produced by a kustomization's images:/replicas: entry is now
written back to that entry instead of into the source manifest ("the edit
lands where the value lives"):

- the analyzer parses images:/replicas: entries and attaches an unambiguous
  override chain per document, walking true render roots (a referenced base
  composes with its parent instead of counting as its own root); distinct
  chains from multiple roots attach nothing and emit an
  ambiguous-kustomize-overrides diagnostic
- a projection inverts the transformer chain per component (image name / tag
  / digest, replica count), routes each divergence to its supplier (entry or
  source file), and verifies by re-simulating the render; anything unsafe
  falls back to today's write-through
- manifestedit gains a bounded, comment-preserving kustomization editor that
  only updates existing fields on existing entries, all-or-nothing
- patchExisting runs the projection (covering live events and resync), and
  /scale field patches route a governed spec.replicas to the entry
- live state equal to the overlay's render is now a true no-op: the source
  file's shadowed tag is no longer overwritten on every event/resync
- malformed images:/replicas: values now refuse the folder (they would fail
  kustomize build; we can no longer vouch for the render)

Adds docs/design/gitops-api/ (workstream tracker + F1 design, including the
shared-entry divergent-consumers limitation), corpus fixtures pinning the
supported/ambiguous boundary, and tests across all three layers.

Validation: task lint (0 issues), task test (coverage 73.9% -> 74.4%,
baseline raised), task test-e2e (53 passed, 0 failed, 8 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sunib, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 031c989b-3762-4011-a492-876ddb26e48c

📥 Commits

Reviewing files that changed from the base of the PR and between 5cfa934 and d81a073.

📒 Files selected for processing (15)
  • .coverage-baseline
  • docs/configuration.md
  • docs/design/gitops-api/README.md
  • docs/design/gitops-api/f1-images-replicas-edit-through.md
  • internal/git/inplace_overrides_test.go
  • internal/git/manifestedit/kustomization_test.go
  • internal/git/plan_flush.go
  • internal/manifestanalyzer/overrides.go
  • internal/manifestanalyzer/overrides_projection_test.go
  • internal/manifestanalyzer/overrides_test.go
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/diamond-images/a/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/diamond-images/b/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/diamond-images/base/deployment.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/diamond-images/base/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/diamond-images/kustomization.yaml
📝 Walkthrough

Walkthrough

This PR implements Kustomize images:/replicas: edit-through (F1): analysis-time override parsing and chain attribution, live-state projection/inversion, and a comment-preserving kustomization writer, wired into the git plan/flush pipeline so governed drift routes to override entries instead of source manifests. Includes design docs, fixtures, and tests.

Changes

F1 Override Edit-Through

Layer / File(s) Summary
Design docs and coverage baseline
docs/design/gitops-api/*, docs/architecture.md, internal/manifestanalyzer/acceptance.go, .coverage-baseline
Adds GitOps API and F1 design docs, updates architecture writer/field-patch text, tweaks refusal message, bumps coverage threshold.
Kustomization scalar edit writer
internal/git/manifestedit/kustomization.go, internal/git/manifestedit/kustomization_test.go, internal/git/manifestedit/split.go
Adds PatchKustomization/KustomizationEdit for comment-preserving scalar edits to existing entries, plus DocumentBody helper and tests.
Override parsing and chain attribution
internal/manifestanalyzer/overrides.go, internal/manifestanalyzer/overrides_test.go, internal/manifestanalyzer/testdata/...
Parses images:/replicas: fields, traverses kustomization graph to attribute an unambiguous override chain per file, flags ambiguity, with corpus tests and fixtures.
ManifestStore override wiring
internal/manifestanalyzer/store.go
Adds DocumentModel.Overrides, computes/resolves override assignments during store build and materialization, extends kustomization parsing for override validity.
Desired-state override projection
internal/manifestanalyzer/overrides_projection.go, internal/manifestanalyzer/overrides_projection_test.go
Implements SplitDesiredForOverrides/ReplicaCountEdit to invert live image/replica state into source form plus routed kustomization edits, with extensive unit tests.
Plan/flush routing and end-to-end tests
internal/git/plan_flush.go, internal/git/inplace_overrides_test.go
Routes governed spec.replicas field patches and desired-manifest divergence into kustomization override edits during patch application; end-to-end tests validate byte-level file behavior.

Estimated code review effort: 4 (Complex) | ~75 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Event
  participant PlanFlush as plan_flush.go
  participant Projection as SplitDesiredForOverrides
  participant Kustomization as PatchKustomization
  participant SourceFile as Source Manifest

  Event->>PlanFlush: live drift (image tag / replicas)
  PlanFlush->>Projection: gitRaw, desired, override chain
  Projection->>Projection: invert image/replica overrides
  Projection-->>PlanFlush: projected desired + override edits
  alt override edits present
    PlanFlush->>Kustomization: apply scalar edits to entry
    Kustomization-->>PlanFlush: patched kustomization bytes
  else no override match
    PlanFlush->>SourceFile: write-through patch
  end
  PlanFlush-->>Event: flush result (source and/or kustomization updated)
Loading

Estimated code review effort: 4 (Complex) | ~75 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: routing kustomize images/replicas edits back into kustomization.yaml.
Description check ✅ Passed The description covers the required sections and includes change type, testing, checklist, related issues, and notes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/gitops-api-f1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/design/gitops-api/README.md`:
- Around line 45-48: The status for F1 in the design index is out of sync with
the linked `f1-images-replicas-edit-through.md` doc. Update the README table
entry for the F1 feature so its status matches the design doc’s implemented
state, keeping the existing feature name and link intact. Use the F1 row in the
GitOps API README to locate and correct the status text.

In `@internal/manifestanalyzer/overrides.go`:
- Around line 216-261: The shared-base traversal in kustomizeOverrideAssignments
is using a per-root visited set that incorrectly suppresses alternate paths in a
diamond-shaped kustomization graph, so only the first override chain is
recorded. Update the walk logic so recordOverrideChain can see every distinct
path to a resource within the same render root, rather than pruning by directory
alone; use the existing kustomizeOverrideAssignments, walk, and
recordOverrideChain flow to locate the fix. If supporting multiple paths is
intentionally out of scope, add a clear comment or documentation stating that
same-root shared subtrees are not checked for ambiguous overrides.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 66a8a832-f358-405b-9fcd-e5127be28b3c

📥 Commits

Reviewing files that changed from the base of the PR and between 05955b2 and 5cfa934.

📒 Files selected for processing (24)
  • .coverage-baseline
  • docs/architecture.md
  • docs/design/gitops-api/README.md
  • docs/design/gitops-api/f1-images-replicas-edit-through.md
  • internal/git/inplace_overrides_test.go
  • internal/git/manifestedit/kustomization.go
  • internal/git/manifestedit/kustomization_test.go
  • internal/git/manifestedit/split.go
  • internal/git/plan_flush.go
  • internal/manifestanalyzer/acceptance.go
  • internal/manifestanalyzer/overrides.go
  • internal/manifestanalyzer/overrides_projection.go
  • internal/manifestanalyzer/overrides_projection_test.go
  • internal/manifestanalyzer/overrides_test.go
  • internal/manifestanalyzer/store.go
  • internal/manifestanalyzer/testdata/contextual-namespace/README.md
  • internal/manifestanalyzer/testdata/contextual-namespace/supported/images-overlay/deployment.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/supported/images-overlay/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/supported/replicas-overlay/base/deployment.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/supported/replicas-overlay/base/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/supported/replicas-overlay/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/ambiguous-images/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/ambiguous-images/other/kustomization.yaml
  • internal/manifestanalyzer/testdata/contextual-namespace/unsupported/ambiguous-images/shared.yaml

Comment thread docs/design/gitops-api/README.md
Comment thread internal/manifestanalyzer/overrides.go
…backs

Review follow-ups for #198:

- override-chain cycle protection moves from a per-walk visited set to
  on-path tracking, so a diamond (one render root reaching a shared base
  through two overlays) records both chains and trips the
  ambiguous-kustomize-overrides refusal instead of silently attributing
  the first path (CodeRabbit major); adds the diamond-images corpus fixture
- the write batch logs store-build diagnostics (warning+) once per batch as
  "manifest store diagnostic", so ambiguity/scope fallbacks the writer takes
  silently leave a trace in the live path, not just the analyzer CLI
- targeted tests for behavior that was only implicitly covered: DocumentBody,
  digest routing, applyOverrideEdits refusal paths, and two resync
  integration tests (governed folder in sync -> zero churn; governed drift
  -> routed to the entry)
- docs: gitops-api README marks F1 implemented (CodeRabbit minor), the F1
  doc describes the diamond-safe walk and corrects the diagnostic-visibility
  phrasing, and configuration.md gains a user-facing "Kustomize support in
  the target path" section (supported subset / loud refusals / silent
  fallbacks and where to see them)

Validation: task lint (0 issues), task test (coverage 74.4% -> 74.5%,
baseline raised), task test-e2e (53 passed, 0 failed, 8 skipped).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sunib sunib merged commit a8ce917 into main Jul 6, 2026
30 of 31 checks passed
@sunib sunib deleted the feat/gitops-api-f1 branch July 6, 2026 09:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant