Skip to content

[Feature]: Helmfile deployer for declarative, bash-free deployments #632

Description

@lockwobr

Feature Summary

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:

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 graphneeds: 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:

  • helmfile.yaml — declarative release graph
  • Per-component values files (reuses existing bundler output)

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.
  • Kustomize / raw manifest components. Helmfile is Helm-first. If [Epic]: Generic Helm-based bundle format with vendored charts #516 ships local charts first, this concern disappears. If not, the chartify project (helmfile org) handles the conversion.
  • 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:

Component

Bundlers

Priority

Important (would improve my workflow)

Compatibility / Breaking Changes

Additive — existing deployers unchanged. New --deployer helmfile flag value; no migration impact for current users.

Operational Considerations

Are you willing to contribute?

Maybe, with guidance

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions