# rules_helm rules_helm provides Bazel rules for producing and managing [Helm](https://helm.sh/) charts. It integrates Helm into the Bazel build system, allowing developers to package, lint, template, install, upgrade, and push Helm charts using Bazel targets. The project supports external chart dependencies from HTTP URLs and OCI registries, container image integration with `rules_oci` and `rules_img`, and value substitutions for chart customization. The library provides a comprehensive set of rules including `helm_chart` (a convenience macro), `helm_package`, `helm_import`, `helm_import_repository`, `helm_install`, `helm_upgrade`, `helm_uninstall`, `helm_push`, `helm_template`, `helm_lint_test`, and `helm_template_test`. It also includes support for custom Helm plugins and toolchain configuration via Bzlmod extensions. ## Setup with MODULE.bazel Configure rules_helm in your Bazel project by adding the module dependency and setting up the Helm toolchain via the module extension. ```starlark # MODULE.bazel bazel_dep(name = "rules_helm", version = "0.22.0") helm = use_extension("@rules_helm//helm:extensions.bzl", "helm") helm.toolchain( name = "helm_toolchains", version = "3.14.4", ) helm.host_tools(name = "helm") use_repo(helm, "helm", "helm_toolchains") register_toolchains( "@helm_toolchains//:all", ) ``` ## helm_chart The primary macro for producing Helm chart packages with convenience targets for install, upgrade, uninstall, and push operations. This macro creates multiple targets automatically based on the configuration. ```starlark load("//helm:defs.bzl", "helm_chart") # Basic helm chart with automatic template detection helm_chart( name = "my_app", registry_url = "oci://my-registry.io/helm-charts", ) # Generated targets: # - :my_app - The packaged Helm chart (.tgz) # - :my_app.install - Install the chart via helm install # - :my_app.upgrade - Upgrade the chart via helm upgrade # - :my_app.uninstall - Uninstall the chart via helm uninstall # - :my_app.push - Push chart and images to registry # - :my_app.push_registry - Push only the chart to registry # - :my_app.push_images - Push only container images ``` ## helm_chart with Chart Dependencies Configure Helm charts with dependencies on other Helm charts, either from local packages or external repositories. ```starlark load("//helm:defs.bzl", "helm_chart") helm_chart( name = "with_chart_deps", deps = [ # Local chart dependencies "//charts/dep1", "//charts/dep2", # External chart dependencies "@helm_test_deps__redis//:redis", "@helm_test_deps__postgresql//:postgresql", ], ) ``` ## helm_chart with Container Images Integrate container images with Helm charts using `rules_oci` or `rules_img`. Image digests are automatically embedded into chart values. ```starlark load("@rules_oci//oci:defs.bzl", "oci_image", "oci_push") load("//helm:defs.bzl", "helm_chart") oci_image( name = "app_image", base = "@distroless_base", ) oci_push( name = "app_image_push", image = ":app_image", remote_tags = ["latest"], repository = "docker.io/myorg/myapp", ) helm_chart( name = "with_image_deps", images = [ ":app_image_push", ], ) ``` ## helm_chart with JSON Chart Definition Define Chart.yaml contents programmatically using the `chart_content` helper function instead of a static YAML file. ```starlark load("//helm:defs.bzl", "chart_content", "helm_chart") helm_chart( name = "chart_json", chart_json = chart_content( name = "my-chart", api_version = "v2", app_version = "1.0.0", description = "My application Helm chart", type = "application", version = "0.1.0", ), values = "values.yaml", registry_url = "oci://my-registry.io/charts", ) ``` ## helm_package The lower-level rule for creating Helm chart packages with full control over all inputs. Use this when you need more granular control than `helm_chart` provides. ```starlark load("//helm:defs.bzl", "chart_file", "helm_package") chart_file( name = "chart_file", chart_name = "inline-chart", version = "1.0.0", app_version = "2.0.0", ) helm_package( name = "my_package", chart = ":chart_file", templates = glob(["templates/**/*.yaml", "templates/**/*.tpl"]), values = "values.yaml", schema = "values.schema.json", crds = glob(["crds/**/*.yaml"]), files = ["files/config.json"], substitutions = { "{VERSION}": "1.0.0", "{ENVIRONMENT}": "production", }, stamp = -1, # Use --stamp flag to control ) ``` ## helm_chart with Files Include additional files accessible via the Helm `.Files` API in templates. ```starlark load("@bazel_skylib//rules:copy_file.bzl", "copy_file") load("@bazel_skylib//rules:write_file.bzl", "write_file") load("//helm:defs.bzl", "helm_chart") copy_file( name = "copied_config", src = "//configs:app_config.json", out = "copied/config.json", ) write_file( name = "generated_config", out = "generated/settings.txt", content = ["setting=value"], ) helm_chart( name = "with_files", files = [ "files/static_file.txt", ":copied_config", ":generated_config", ], registry_url = "oci://localhost/helm-registry", ) ``` ## helm_import_repository Fetch external Helm charts from HTTP URLs or OCI registries for use as dependencies. ```starlark load("//helm:defs.bzl", "helm_import_repository") # Import from a Helm repository index helm_import_repository( name = "redis_chart", repository = "https://charts.bitnami.com/bitnami", chart_name = "redis", version = "21.2.5", sha256 = "4f70fc4c8caac66b21450581e29e3437cad401895d5ac0191e9e91f74ed8dc10", ) # Direct HTTP URL download helm_import_repository( name = "postgresql_chart", url = "https://charts.bitnami.com/bitnami/postgresql-14.0.5.tgz", sha256 = "38d9b6657aa3b0cc16d190570dbaf96796e997d03a1665264dac9966343e4d1b", ) # OCI registry download helm_import_repository( name = "grafana_chart", url = "oci://registry-1.docker.io/bitnamicharts/grafana:12.1.4", sha256 = "015f66a231a809557ab368d903f6762ba31ba2f7b3d0f890445be6e8f213cff1", ) # Use in BUILD file helm_chart( name = "my_app", deps = [ "@redis_chart//:redis", "@postgresql_chart//:postgresql", "@grafana_chart//:grafana", ], ) ``` ## helm_import Import a pre-packaged Helm chart `.tgz` file that already exists locally. ```starlark load("//helm:defs.bzl", "helm_import") helm_import( name = "vendored_chart", chart = "third_party/charts/my-chart-1.0.0.tgz", version = "1.0.0", visibility = ["//visibility:public"], ) ``` ## helm_template Render Helm chart templates to a YAML file at build time. ```starlark load("//helm:defs.bzl", "helm_chart", "helm_template") helm_chart( name = "my_app", ) # Generate rendered templates as a build output helm_template( name = "my_app_rendered", out = "rendered.yaml", chart = ":my_app", values = [ ":production_values.yaml", ":secrets.yaml", ], opts = [ "--namespace=production", "--set=replicaCount=3", ], ) ``` ## helm_template_test Test that rendered Helm templates match expected patterns using regex assertions. ```starlark load("//helm:defs.bzl", "helm_chart", "helm_template_test") helm_chart( name = "my_app", ) helm_template_test( name = "my_app_template_test", chart = ":my_app", values = [ ":test_values.yaml", ], template_patterns = { "my-app/templates/deployment.yaml": [ r"replicas: 10\b", r"image: \"myorg/myapp:v1.0.0\"", ], "my-app/templates/service.yaml": [ r"type: LoadBalancer", r"port: 8080", ], }, opts = ["--set=replicaCount=10"], ) ``` ## helm_lint_test Run `helm lint` as a Bazel test to validate chart structure and best practices. ```starlark load("//helm:defs.bzl", "helm_chart", "helm_lint_test") helm_chart( name = "my_app", ) # Basic lint test helm_lint_test( name = "my_app_lint_test", chart = ":my_app", ) # Lint test with custom values and substitutions helm_lint_test( name = "my_app_lint_with_values_test", chart = ":my_app", values = [ ":lint_values.yaml", ], substitutions = { "image.tag": "latest", "replicaCount": "1", }, opts = ["--strict"], ) ``` ## helm_install, helm_upgrade, helm_uninstall Run Helm install, upgrade, and uninstall operations as executable Bazel targets. ```starlark load("//helm:defs.bzl", "helm_install", "helm_upgrade", "helm_uninstall", "helm_package") helm_package( name = "my_app", chart = "Chart.yaml", values = "values.yaml", templates = glob(["templates/**"]), ) helm_install( name = "my_app_install", package = ":my_app", install_name = "my-app-release", helm_opts = ["--namespace=default"], opts = [ "--create-namespace", "--wait", "--timeout=5m", ], ) helm_upgrade( name = "my_app_upgrade", package = ":my_app", install_name = "my-app-release", opts = [ "--install", "--wait", "--atomic", ], ) helm_uninstall( name = "my_app_uninstall", install_name = "my-app-release", opts = ["--wait"], ) # Run with: bazel run //:my_app_install # Run with: bazel run //:my_app_upgrade # Run with: bazel run //:my_app_uninstall ``` ## helm_push Push Helm charts to OCI registries with optional container image pushing. ```starlark load("//helm:defs.bzl", "helm_push", "helm_package") helm_package( name = "my_app", chart = "Chart.yaml", values = "values.yaml", templates = glob(["templates/**"]), ) # Push only the chart helm_push( name = "my_app_push_chart", package = ":my_app", registry_url = "oci://my-registry.io/charts/my-app", login_url = "my-registry.io", include_images = False, ) # Push chart and all associated images helm_push( name = "my_app_push_all", package = ":my_app", registry_url = "oci://my-registry.io/charts/my-app", login_url = "my-registry.io", include_images = True, env = { "HELM_REGISTRY_USERNAME": "$(REGISTRY_USER)", }, ) # Run with: HELM_REGISTRY_USERNAME=user HELM_REGISTRY_PASSWORD=pass bazel run //:my_app_push_all ``` ## helm_plugin Define custom Helm plugins to extend Helm functionality within the toolchain. ```starlark load("//helm:defs.bzl", "helm_plugin", "helm_toolchain") helm_plugin( name = "helm_cm_push", plugin_name = "cm-push", yaml = "@helm_cm_push//:plugin.yaml", data = ["@helm_cm_push//:data"], ) helm_toolchain( name = "custom_toolchain", helm = "@helm//:helm", plugins = [ ":helm_cm_push", ], ) ``` ## chart_file Generate a Chart.yaml file programmatically as a rule output. ```starlark load("//helm:defs.bzl", "chart_file", "helm_package") chart_file( name = "generated_chart_yaml", chart_name = "my-application", api_version = "v2", app_version = "3.0.0", description = "My awesome application", type = "application", version = "1.2.3", ) helm_package( name = "my_app", chart = ":generated_chart_yaml", templates = glob(["templates/**"]), values = "values.yaml", ) ``` ## Run Helm as a Tool Use the Helm binary directly via Bazel run or in genrules. ```bash # Run helm directly bazel run @helm//:helm -- version bazel run @helm//:helm -- template my-release ./chart bazel run @helm//:helm -- lint ./chart ``` ```starlark # Use helm in a genrule genrule( name = "render_chart", srcs = [":my_chart"], outs = ["rendered_templates.yaml"], cmd = "$(HELM_BIN) template my-release $(execpath :my_chart) > $@", toolchains = ["@rules_helm//helm:current_toolchain"], ) ``` ## Toolchain Extension Configuration Configure custom Helm versions and plugins via the Bzlmod module extension. ```starlark # MODULE.bazel helm = use_extension("@rules_helm//helm:extensions.bzl", "helm") # Use specific Helm version helm.toolchain( name = "helm_toolchains", version = "3.19.3", plugins = [ "@helm_cm_push//:cm-push", ], ) # Create host tools alias helm.host_tools(name = "helm") use_repo(helm, "helm", "helm_toolchains") register_toolchains("@helm_toolchains//:all") ``` ## Summary rules_helm enables seamless integration of Helm chart workflows into Bazel build pipelines. The primary use cases include building reproducible Helm chart packages with dependencies tracked by Bazel, running linting and template validation as part of CI/CD testing, deploying charts via install/upgrade operations with stamped version information, and pushing charts to OCI registries alongside container images. Integration patterns typically involve using `helm_chart` as the entry point macro for most charts, importing external dependencies via `helm_import_repository`, associating container images with charts via the `images` attribute for coordinated deployments, and using `helm_lint_test` and `helm_template_test` for validation. The toolchain supports custom Helm versions and plugins, making it adaptable to various enterprise requirements. Charts can be built with `bazel build`, tested with `bazel test`, and deployed with `bazel run` targets.