You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add --deployer helmfile alongside the existing helm, argocd, and argocdhelm deployers. The bundler emits a helmfile.yaml describing the component release graph; operators run helmfile apply / helmfile diff / helmfile destroy to drive rollouts.
Problem / Use Case
When the bundler needed a deploy/undeploy story, the project deliberately chose generated bash scripts over adding a deployment-tool dependency. The reasoning was that the scripts would stay simple enough that introducing another tool (helmfile, argo-rollouts, etc.) would be unnecessary overhead. That was a reasonable call at the time.
The evidence since suggests the bet didn't hold:
deploy.sh.tmpl is ~450 lines and undeploy.sh.tmpl is ~700 lines — together encoding orchestration logic (finalizer races, stale hook cleanup, webhook orphan detection, operator-specific post-install, pre/post-flight) that isn't really "config generation."
The bash keeps growing because orchestration is genuinely hard, and the template is where that complexity lands.
AICR's charter is that it generates validated configuration; it is not a deployment tool. The existing helm deployer has quietly drifted past that line. A helmfile deployer is the circle-back: emit the release graph as data (helmfile.yaml) and delegate orchestration to a tool purpose-built for it.
This is not a deprecation proposal. The helm deployer stays in place; this issue adds a parallel option — the two can coexist indefinitely. But if the helmfile deployer is well received, it opens a future conversation about simplifying or retiring the bash deployer. That's a separate decision for a later issue, not a commitment this one makes.
Bonus: what users get beyond generated bash
On top of the scope-alignment case, users pick up capabilities the bash deployer doesn't offer:
Readability — a helmfile.yaml is easier to scan, review, and reason about than a ~450-line templated bash script.
helmfile diff — preview changes before apply. No AICR deployer offers this today.
Idempotent apply and destroy — safe to re-run after partial failure; generated bash isn't.
Reviewable release graph — needs: dependencies and per-release flags in YAML, instead of ordering baked into bash template iteration.
Standard tool in the ecosystem — teams already running helmfile in their CI get a one-tool workflow instead of a bundle-plus-custom-bash hybrid.
Parallel installs where needs: allows; bash is sequential.
Proposed Solution
A new helmfile deployer implementing AICR's existing Deployer interface. The bundler emits:
The deployer lives entirely within helmfile's native lifecycle. No bash wrapper, no pre-flight logic, no extra scripting layered on top. Operators drive helmfile directly (helmfile apply / diff / destroy) using the tool's standard workflow — the same workflow the helmfile community already uses.
helmfile.yaml maps cleanly from AICR's current deploy logic:
Current deploy.sh behavior
Helmfile equivalent
Ordered install across components
needs: dependency graph
ASYNC_COMPONENTS skipping --wait
per-release wait: false
COMPONENT_HELM_TIMEOUT overrides
per-release timeout:
Component values.yaml + cluster-values.yaml
values: list
Success Criteria
aicr bundle --deployer helmfile -r recipe.yaml -o ./bundle produces a working helmfile.yaml.
helmfile apply and helmfile destroy succeed end-to-end against Kind/KWOK for at least one recipe.
helmfile diff shows expected changes for recipe edits.
Existing helm, argocd, argocdhelm deployers continue to work unchanged.
Docs updated in docs/user/cli-reference.md.
Sequencing Considerations
This issue is independently valuable, but sequencing with related bundler work pays off:
Neither is a hard block. This deployer could ship against today's bundle format and today's finalizer handling, then gain polish as #610/#516 land.
Hints for the Implementer
Design decisions intentionally left open — these are starting points, not prescriptions:
No pre-flight emitted. Helmfile has native lifecycle hooks (prepare:, presync:, postsync:, postuninstall:) that could carry pre-flight logic. Per Non-Goals, this deployer intentionally ships without any. The hooks remain available if a future need justifies them — start with nothing shipped.
helm-secrets plugin. Opens a future door for SOPS-encrypted values files. Not required for MVP, but worth keeping the deployer design compatible.
Concurrency. Helmfile installs in parallel where needs: allows. Declaring needs: correctly preserves current sequential behavior; concurrency becomes a later opt-in.
Tool install. Helmfile becomes a new deploy-time dependency for users of this deployer. Document in cli-reference; consider pinning via make tools-setup.
Wiring points. The deployer lives at pkg/bundler/deployer/helmfile/, implements Deployer from pkg/bundler/deployer/deployer.go, and is wired into buildDeployer() in pkg/bundler/bundler.go via a new config.DeployerHelmfile case in pkg/bundler/config/config.go:ParseDeployerType.
Alternatives Considered
Do nothing; users keep wrapping bash. Works but reinvents helmfile per team.
Push users toward ArgoCD. Forces a controller into every target cluster — wrong fit for CI / ephemeral / air-gapped use cases.
Replace the helm deployer with helmfile. Drops the "bash I can read" posture some users rely on. Additive is better than replacement.
Non-Goals
Guiding principle: the deployer lives entirely within helmfile's native lifecycle (apply, diff, destroy, its own hooks:). That lifecycle is what the helmfile community expects and what an operator picking up this deployer would assume. AICR doesn't extend or wrap it. If AICR developers ever need deeper operations (cluster scrubbing, debug workflows, scorched-earth cleanup), those belong in tools/ — separate from what the bundler emits.
Three concrete consequences:
No deploy.sh / undeploy.sh wrapper. Shipping one would reintroduce the shell-script accretion pattern this issue is trying to leave behind.
No pre-flight checks emitted. AICR's bash deploy.sh includes ~120 lines of pre-flight today (terminating namespaces, stale webhooks, orphaned API services, orphaned CRDs, stale node taints). Helmfile has prepare: hooks that could carry equivalent logic, but shipping any reintroduces the same accretion problem. Cluster pre-flight is the operator's concern.
No replicating today's undeploy.sh cleanup behavior. CRD deletion, finalizer force-clearing, orphaned webhook sweep, post-flight verification — this arguably doesn't belong in an AICR-emitted undeploy path at all. helmfile destroy stays the simple graceful-uninstall primitive helmfile already provides. The broader "leave no trace" cleanup is a separate problem for a follow-up ticket. Two plausible directions there:
A tools/nuke script operators opt into when they explicitly want a scorched-earth clean.
Feature Summary
Add
--deployer helmfilealongside the existinghelm,argocd, andargocdhelmdeployers. The bundler emits ahelmfile.yamldescribing the component release graph; operators runhelmfile apply/helmfile diff/helmfile destroyto drive rollouts.Problem / Use Case
When the bundler needed a deploy/undeploy story, the project deliberately chose generated bash scripts over adding a deployment-tool dependency. The reasoning was that the scripts would stay simple enough that introducing another tool (helmfile, argo-rollouts, etc.) would be unnecessary overhead. That was a reasonable call at the time.
The evidence since suggests the bet didn't hold:
deploy.sh.tmplis ~450 lines andundeploy.sh.tmplis ~700 lines — together encoding orchestration logic (finalizer races, stale hook cleanup, webhook orphan detection, operator-specific post-install, pre/post-flight) that isn't really "config generation."undeploy.sh.tmplalone have chased finalizer-handling edge cases (fix(bundler): improve deploy/undeploy script reliability #253, fix(bundler): fix undeploy PVC ordering, harden deploy scripts, add deployment docs #282, feat(bundler): add pre-flight checks to deploy.sh and post-flight to undeploy.sh #364, fix(bundler): clean up orphaned KAI and Kubeflow Trainer CRDs on undeploy #416, fix(bundler): scope cleanup to bundle components and remove stale skyhook taints #477, fix(bundler): add pre-flight finalizer check to undeploy.sh (#406) #561, fix(bundler): fix undeploy template pre/post-flight checks #602 — see Bridge Jobs for deploy/undeploy lifecycle flow control #610 for context).AICR's charter is that it generates validated configuration; it is not a deployment tool. The existing
helmdeployer has quietly drifted past that line. A helmfile deployer is the circle-back: emit the release graph as data (helmfile.yaml) and delegate orchestration to a tool purpose-built for it.This is not a deprecation proposal. The
helmdeployer stays in place; this issue adds a parallel option — the two can coexist indefinitely. But if the helmfile deployer is well received, it opens a future conversation about simplifying or retiring the bash deployer. That's a separate decision for a later issue, not a commitment this one makes.Bonus: what users get beyond generated bash
On top of the scope-alignment case, users pick up capabilities the bash deployer doesn't offer:
helmfile.yamlis easier to scan, review, and reason about than a ~450-line templated bash script.helmfile diff— preview changes before apply. No AICR deployer offers this today.needs:dependencies and per-release flags in YAML, instead of ordering baked into bash template iteration.needs:allows; bash is sequential.Proposed Solution
A new
helmfiledeployer implementing AICR's existingDeployerinterface. The bundler emits:helmfile.yaml— declarative release graphThe deployer lives entirely within helmfile's native lifecycle. No bash wrapper, no pre-flight logic, no extra scripting layered on top. Operators drive helmfile directly (
helmfile apply/diff/destroy) using the tool's standard workflow — the same workflow the helmfile community already uses.helmfile.yamlmaps cleanly from AICR's current deploy logic:needs:dependency graphASYNC_COMPONENTSskipping--waitwait: falseCOMPONENT_HELM_TIMEOUToverridestimeout:values:listSuccess Criteria
aicr bundle --deployer helmfile -r recipe.yaml -o ./bundleproduces a workinghelmfile.yaml.helmfile applyandhelmfile destroysucceed end-to-end against Kind/KWOK for at least one recipe.helmfile diffshows expected changes for recipe edits.helm,argocd,argocdhelmdeployers continue to work unchanged.docs/user/cli-reference.md.Sequencing Considerations
This issue is independently valuable, but sequencing with related bundler work pays off:
helmfile destroyhangs on the same finalizer race ashelm uninstall. Strongly prefer landing Bridge Jobs for deploy/undeploy lifecycle flow control #610 first so the deployer's uninstall path works from day one.helmfile.yamltrivially point at./001-component/paths. Without it,helmfile.yamlreferences remote repos (works, but misses [Epic]: Generic Helm-based bundle format with vendored charts #516's air-gap goal).Neither is a hard block. This deployer could ship against today's bundle format and today's finalizer handling, then gain polish as #610/#516 land.
Hints for the Implementer
Design decisions intentionally left open — these are starting points, not prescriptions:
prepare:,presync:,postsync:,postuninstall:) that could carry pre-flight logic. Per Non-Goals, this deployer intentionally ships without any. The hooks remain available if a future need justifies them — start with nothing shipped.chartifyproject (helmfile org) handles the conversion.needs:allows. Declaringneeds:correctly preserves current sequential behavior; concurrency becomes a later opt-in.make tools-setup.pkg/bundler/deployer/helmfile/, implementsDeployerfrompkg/bundler/deployer/deployer.go, and is wired intobuildDeployer()inpkg/bundler/bundler.govia a newconfig.DeployerHelmfilecase inpkg/bundler/config/config.go:ParseDeployerType.Alternatives Considered
Non-Goals
Guiding principle: the deployer lives entirely within helmfile's native lifecycle (
apply,diff,destroy, its ownhooks:). That lifecycle is what the helmfile community expects and what an operator picking up this deployer would assume. AICR doesn't extend or wrap it. If AICR developers ever need deeper operations (cluster scrubbing, debug workflows, scorched-earth cleanup), those belong intools/— separate from what the bundler emits.Three concrete consequences:
No
deploy.sh/undeploy.shwrapper. Shipping one would reintroduce the shell-script accretion pattern this issue is trying to leave behind.No pre-flight checks emitted. AICR's bash
deploy.shincludes ~120 lines of pre-flight today (terminating namespaces, stale webhooks, orphaned API services, orphaned CRDs, stale node taints). Helmfile hasprepare:hooks that could carry equivalent logic, but shipping any reintroduces the same accretion problem. Cluster pre-flight is the operator's concern.No replicating today's
undeploy.shcleanup behavior. CRD deletion, finalizer force-clearing, orphaned webhook sweep, post-flight verification — this arguably doesn't belong in an AICR-emitted undeploy path at all.helmfile destroystays the simple graceful-uninstall primitive helmfile already provides. The broader "leave no trace" cleanup is a separate problem for a follow-up ticket. Two plausible directions there:tools/nukescript operators opt into when they explicitly want a scorched-earth clean.cleanup.yamlbridge-Job pattern from Bridge Jobs for deploy/undeploy lifecycle flow control #610, which resolves the finalizer race declaratively.Component
Bundlers
Priority
Important (would improve my workflow)
Compatibility / Breaking Changes
Additive — existing deployers unchanged. New
--deployer helmfileflag value; no migration impact for current users.Operational Considerations
helmfilebinary for users of this deployer.Are you willing to contribute?
Maybe, with guidance