Skip to content

feat(gitops-api): treat higher-level KRM objects (HelmRelease, Argo Application, KRO) as first-class documents#203

Merged
sunib merged 5 commits into
mainfrom
feat/gitops-api-f7
Jul 7, 2026
Merged

feat(gitops-api): treat higher-level KRM objects (HelmRelease, Argo Application, KRO) as first-class documents#203
sunib merged 5 commits into
mainfrom
feat/gitops-api-f7

Conversation

@sunib

@sunib sunib commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What this delivers

Two independent changes:

1. Higher-level KRM objects as first-class documents

Control-plane custom resources — Flux HelmRelease, Argo CD Application, KRO
resources — already mirror and edit in place through the operator's
kind-agnostic pipeline, exactly like core resources (no per-kind allowlist; only
a small deny list of noisy built-ins). This change makes that guarantee
load-bearing so a future special-case can't silently regress it, and documents
the "install an app = commit a KRM document" workflow for users.

No operator code changes — it's a test corpus, one e2e pin, and docs:

  • Unit corpushelmrelease.yaml, argocd-application.yaml,
    kro-podinfoapp.yaml under internal/git/manifestedit/testdata/corpus/. They
    flow through the two existing globbed gates with no new test code:
    TestCorpusRoundTrip_ByteIdentical (re-encode byte-for-byte) and
    TestConvergence_Corpus (perturb-then-settle).
  • e2etest/e2e/helmrelease_mirror_edit_e2e_test.go: a live Flux
    HelmRelease is mirrored to its canonical path
    <ns>/helm.toolkit.fluxcd.io/helmreleases/<name>.yaml, then a live
    spec.chart.spec.version bump round-trips in place, preserving a
    hand-authored comment. spec.suspend: true keeps the helm-controller inert so
    the object behaves like any other inert document.
  • User docsdocs/installing-apps-as-krm.md: installing an app is
    committing a KRM document; the chart-inflation boundary (helmCharts:)
    stays refused.

2. Fix the recurring "release everything" release-please PR

After each release, release-please kept opening a spurious PR proposing a giant,
full-history changelog (it proposed 0.31.0 re-listing work shipped releases
ago). Root cause: it cut the GitHub release as a draft and opened the next
PR in the same run — and a draft is invisible to release-please's own "latest
release" lookup, so that PR was computed against the entire history.

Fixed by splitting the action into two passes:

  • release-please job → skip-github-pull-request: cut the (draft) release
    only, never open a PR.
  • release-please-pr job → skip-github-release: open/refresh the next PR
    after publish-release makes the release public and visible.

Keeps immutable releases + the draft/asset-attach flow intact. Release docs
updated (docs/ci-overview.md, .github/RELEASES.md).

Validation

  • task lint (incl. actionlint) — clean.
  • task test — pass; unit coverage steady at baseline (no production code
    changed).
  • task test-e2e — the new HelmRelease mirror + version-bump +
    comment-preservation spec passes live. The only failure is a pre-existing,
    unrelated flake in the "Unsupported Folder Refusal" spec (a known racy
    GitPathAccepted/refusal status projection) — independent of this PR, which
    changes no operator Go, so the deployed binary is behaviourally identical to
    main.

🤖 Generated with Claude Code

…cuments

Flux HelmRelease, Argo CD Application, and KRO resources already mirror and edit
through the kind-agnostic pipeline exactly like core resources (no per-kind
allowlist; only a small deny list of noisy built-ins). F7 makes that guarantee
load-bearing rather than assumed. No product code changes.

- manifestedit corpus: helmrelease.yaml, argocd-application.yaml,
  kro-podinfoapp.yaml flow through the existing globbed round-trip
  (byte-identical) and convergence (perturb-then-settle) gates.
- e2e (manager,f7-higher-level-krm): a Flux HelmRelease is mirrored to its
  canonical path and a live chart-version bump round-trips in place, preserving
  a hand-authored comment. Uses the Flux CRDs already in the base e2e cluster;
  spec.suspend keeps the helm-controller inert.
- docs: f7 design doc + user guide installing-apps-as-krm.md (install an app =
  add a KRM document; the chart-inflation boundary stays refused); README ladder
  marks F7 implemented.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds F7 design and user documentation, new manifestedit corpus fixtures, a HelmRelease mirror/edit e2e test with templates, and a release workflow job that closes stale release-please PRs after publishing.

Changes

F7 Higher-Level KRM Documents

Layer / File(s) Summary
F7 design and README updates
docs/design/gitops-api/f7-higher-level-krm-documents.md, docs/design/gitops-api/README.md
Adds the F7 design doc, marks F7 implemented in the design README, and updates the baseline section with higher-level KRM mirror/edit behavior.
Installing apps as KRM documentation
docs/installing-apps-as-krm.md, docs/README.md
Adds the user guide for installing apps as KRM documents and links it from the docs start page.
Manifestedit corpus fixtures
internal/git/manifestedit/testdata/corpus/argocd-application.yaml, .../helmrelease.yaml, .../kro-podinfoapp.yaml
Adds new Argo CD, HelmRelease, and KRO manifest fixtures for corpus-based round-trip and convergence coverage.
HelmRelease mirror/edit e2e test and templates
test/e2e/helmrelease_mirror_edit_e2e_test.go, test/e2e/templates/manager/helmrelease.tmpl, test/e2e/templates/manager/watchrule-helmrelease.tmpl
Adds an e2e HelmRelease mirroring/edit test, a helper for seeding and pushing repo edits, and supporting HelmRelease/WatchRule templates.

Release Workflow Cleanup

Layer / File(s) Summary
Close stale release-please PRs
.github/workflows/release.yml
Adds a post-release job that checks whether main is ahead of the published tag and closes matching stale release-please PRs when it is not.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is informative, but it doesn't follow the required template sections for type, testing, checklist, related issues, screenshots, or additional notes. Reformat it to the repository template and add the missing sections, including type of change, testing details, checklist, related issues, screenshots, and additional notes.
✅ Passed checks (4 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 accurately summarizes the main change: promoting higher-level KRM objects to first-class documents.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/gitops-api-f7

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 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@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: 1

🤖 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 `@test/e2e/helmrelease_mirror_edit_e2e_test.go`:
- Around line 155-167: The fallback path in this test helper destroys the
checkout and then immediately tries to read the committed file again, which
makes the later os.ReadFile in the seeding block fail misleadingly. Update the
orphan-branch handling in the same helper that uses gitRun/mustGit so it does
not assume relPath still exists after checkout --orphan and rm -rf ., or bail
out early with a clear error before attempting to seed the comment. Keep the
file-read and write step only for the successful origin/main checkout path, or
make the fallback create the target file explicitly before reading it.
🪄 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: ffb9bb2c-c77d-4b53-bc26-74abacfd24ea

📥 Commits

Reviewing files that changed from the base of the PR and between 97a9c87 and 2678e84.

📒 Files selected for processing (10)
  • docs/README.md
  • docs/design/gitops-api/README.md
  • docs/design/gitops-api/f7-higher-level-krm-documents.md
  • docs/installing-apps-as-krm.md
  • internal/git/manifestedit/testdata/corpus/argocd-application.yaml
  • internal/git/manifestedit/testdata/corpus/helmrelease.yaml
  • internal/git/manifestedit/testdata/corpus/kro-podinfoapp.yaml
  • test/e2e/helmrelease_mirror_edit_e2e_test.go
  • test/e2e/templates/manager/helmrelease.tmpl
  • test/e2e/templates/manager/watchrule-helmrelease.tmpl

Comment thread test/e2e/helmrelease_mirror_edit_e2e_test.go
sunib and others added 2 commits July 7, 2026 08:50
The install-apps guide explained that the operator won't *render* charts, but
not the bigger configuration discipline: users must deliberately scope capture
to authored intent (HelmRelease/Application/KRO documents) and keep the objects
those controllers render (Deployments, Services, ConfigMaps, ReplicaSets, Pods)
out of Git.

Add a "capture intent, not the rendered output" section that explains why
mirroring rendered output breaks round-trippability (controller-owned objects
have no single writable destination), that the operator cannot tell an authored
Deployment from a rendered one (only a few purely-runtime kinds are excluded by
default), and how to draw the line: select intent kinds not workload kinds,
separate intent from runtime by namespace, and mind overlaps. Frame it as the
same principle as the chart-inflation boundary, applied at runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
release-please builds the next release PR in the same invocation that cuts
the draft release for the just-merged one, before publish-release flips that
draft to published. A draft release is invisible to release-please's
"latest release" lookup, so the next PR is computed against the full history
and proposes a bogus "release everything" version (it cannot see the release
just cut) — e.g. #206 proposed 0.31.0 with a 168-line changelog re-listing
work shipped releases ago.

Add a close-stale-release-pr job after publish-release: once the release is
published, if main has no commits past its tag, close the open
'autorelease: pending' / 'release-please--' PR. release-please opens a fresh,
correct one on the next push with releasable commits. Deterministic cleanup;
leaves the draft/asset-attachment flow (required for immutable releases)
untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/installing-apps-as-krm.md (1)

149-152: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clarify the watchability requirement here.

This reads like every watched kind needs a CRD, but core kinds and aggregated APIs are also watchable. Rephrase this to require that the resource be served, and mention CRDs only for CRD-backed kinds.

Proposed fix
-The kind's CRD must be installed and served in the cluster — the operator
-resolves what to watch from the live API surface, so any served kind (core, CRD,
-or aggregated) is watchable.
+The resource must be served in the cluster — for CRD-backed kinds, that means
+the CRD must be installed. The operator resolves what to watch from the live
+API surface, so any served kind (core, CRD-backed, or aggregated) is watchable.
🤖 Prompt for 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.

In `@docs/installing-apps-as-krm.md` around lines 149 - 152, Clarify the
watchability wording in the installation docs so it does not imply every watched
kind needs a CRD; update the sentence in the section describing the operator’s
live API discovery to say the resource must be served in the cluster, and
mention CRDs only for CRD-backed kinds. Keep the references to the watch model
by preserving the link to configuration.md and the GitProvider / GitTarget /
WatchRule terminology.
🧹 Nitpick comments (2)
.github/workflows/release.yml (2)

385-392: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Consider resilience for the gh api compare call.

A single transient GitHub API error here fails the whole job with no retry, silently skipping the cleanup for this release (low impact, since it's non-blocking for the release itself, but requires manual intervention or waiting for the next release cycle to self-heal).

🤖 Prompt for 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.

In @.github/workflows/release.yml around lines 385 - 392, The release workflow’s
`gh api repos/${REPO}/compare/${TAG_NAME}...main` check in the cleanup step is
brittle because a transient API failure aborts the job and skips pruning. Update
the logic around the compare call to be resilient by adding a retry/backoff or a
safe fallback that preserves the job flow when GitHub API requests fail, and
keep the behavior localized to the cleanup block that computes `ahead` and
decides whether to exit early.

359-404: 📐 Maintainability & Code Quality | 🔵 Trivial

Solid, well-guarded cleanup job.

Compare direction (${TAG_NAME}...main), label/branch-prefix filtering, and the ahead_by != 0 fail-safe are all correct. Permissions are scoped to least privilege for this job's needs.

As per coding guidelines, remember to run task lint-actions locally before pushing this workflow change to catch any actionlint issues not visible in this review.

🤖 Prompt for 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.

In @.github/workflows/release.yml around lines 359 - 404, Add a local validation
step for this workflow change by running task lint-actions before pushing. This
comment is a reminder to verify the release-please cleanup job in the workflow,
especially the close-stale-release-pr step and its gh usage, with actionlint so
any workflow syntax or permissions issues are caught early.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In `@docs/installing-apps-as-krm.md`:
- Around line 149-152: Clarify the watchability wording in the installation docs
so it does not imply every watched kind needs a CRD; update the sentence in the
section describing the operator’s live API discovery to say the resource must be
served in the cluster, and mention CRDs only for CRD-backed kinds. Keep the
references to the watch model by preserving the link to configuration.md and the
GitProvider / GitTarget / WatchRule terminology.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 385-392: The release workflow’s `gh api
repos/${REPO}/compare/${TAG_NAME}...main` check in the cleanup step is brittle
because a transient API failure aborts the job and skips pruning. Update the
logic around the compare call to be resilient by adding a retry/backoff or a
safe fallback that preserves the job flow when GitHub API requests fail, and
keep the behavior localized to the cleanup block that computes `ahead` and
decides whether to exit early.
- Around line 359-404: Add a local validation step for this workflow change by
running task lint-actions before pushing. This comment is a reminder to verify
the release-please cleanup job in the workflow, especially the
close-stale-release-pr step and its gh usage, with actionlint so any workflow
syntax or permissions issues are caught early.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9fe8596b-218e-47a4-b4b0-8faecd9ed4a5

📥 Commits

Reviewing files that changed from the base of the PR and between 2678e84 and 50e41b1.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • docs/installing-apps-as-krm.md

Fix the spurious "release everything" PR at its root instead of janitoring it.
release-please cut the draft release AND built the next PR in one invocation;
the draft is invisible to its own "latest release" lookup, so the PR was
computed against full history (e.g. #206 -> 0.31.0, 168-line changelog).

Split the action into two invocations:
- release-please job: skip-github-pull-request=true — cut the (draft) release
  only, never open a PR, so no spurious PR is ever created.
- release-please-pr job: skip-github-release=true — open/refresh the next
  release PR *after* publish-release makes the release visible, so it anchors
  to the published release.

The PR job runs whenever CI + release-please succeeded and either no release
was created (publish-release skipped) or publish-release succeeded, so a failed
publish (release still a draft) never opens a PR against the draft. Keeps
immutable-releases + the draft/asset-attach flow untouched; replaces the
close-stale-release-pr janitor added in the previous commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sunib sunib force-pushed the feat/gitops-api-f7 branch from 64d563f to 0c52e71 Compare July 7, 2026 18:59
Reflect the release.yml change in the release docs: release-please now runs as
two passes — cut the (draft) release with skip-github-pull-request, then
open/refresh the next PR with skip-github-release after publish makes the release
visible. Explains why (a draft is invisible to release-please's latest-release
lookup, so a single-pass run proposed a full-history "release everything" PR).

- docs/ci-overview.md: third "worth calling out" property covering the split.
- .github/RELEASES.md: two-pass flow in "What Happens When You Push", draft →
  publish note, diagram note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sunib sunib changed the title feat(gitops-api): F7 — higher-level KRM objects as first-class documents feat(gitops-api): treat higher-level KRM objects (HelmRelease, Argo Application, KRO) as first-class documents Jul 7, 2026
@sunib sunib merged commit e5722a7 into main Jul 7, 2026
8 checks passed
@sunib sunib deleted the feat/gitops-api-f7 branch July 7, 2026 19:14
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