From 9ec276281c8d24707814805c7cc48b60843803cd Mon Sep 17 00:00:00 2001 From: Andre Brisco Date: Sat, 2 Nov 2024 10:24:02 -0700 Subject: [PATCH] Added support for helm plugins --- MODULE.bazel.lock | 48 ++++++++- README.md | 34 +++++- helm/defs.bzl | 10 +- helm/extensions.bzl | 10 +- helm/private/BUILD.bazel | 1 - helm/private/current_toolchain.bzl | 2 +- helm/private/helm.bzl | 4 + helm/private/helm_cmd/BUILD.bazel | 8 ++ helm/private/helm_cmd/helm_cmd.go | 43 ++++++++ helm/private/helm_lint.bzl | 18 ++-- helm/private/helm_package.bzl | 9 +- helm/private/helm_registry.bzl | 2 + helm/private/helm_toolchain.bzl | 132 +++++++++++++++++++++++ helm/private/linter/BUILD.bazel | 8 +- helm/private/linter/linter.go | 28 +++-- helm/private/packager/BUILD.bazel | 10 +- helm/private/packager/packager.go | 38 ++++--- helm/private/plugin/BUILD.bazel | 7 ++ helm/private/plugin/plugin_builder.go | 116 ++++++++++++++++++++ helm/private/registrar/BUILD.bazel | 1 + helm/private/registrar/registrar.go | 98 +++++++++++++---- helm/repositories.bzl | 10 +- helm/toolchain.bzl | 36 ++----- tests/test_deps.bzl | 47 +++++++- tests/toolchain_with_plugins/BUILD.bazel | 31 ++++++ 25 files changed, 638 insertions(+), 113 deletions(-) create mode 100644 helm/private/helm_cmd/BUILD.bazel create mode 100644 helm/private/helm_cmd/helm_cmd.go create mode 100644 helm/private/helm_toolchain.bzl create mode 100644 helm/private/plugin/BUILD.bazel create mode 100644 helm/private/plugin/plugin_builder.go create mode 100644 tests/toolchain_with_plugins/BUILD.bazel diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 5633fca..d4b9d13 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -1640,7 +1640,7 @@ "moduleExtensions": { "//helm:extensions.bzl%helm": { "general": { - "bzlTransitiveDigest": "PqkrsL6mq4yXE0K9v1lAssGpS7uXvdgG9Spj1LIRmIk=", + "bzlTransitiveDigest": "Ek7mwRyjG12YfPwFfZysums7crTKL9/PrkF1LLhFb0k=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { @@ -1676,6 +1676,7 @@ "attributes": { "name": "_main~helm~helm_linux_arm_toolchain", "platform": "linux-arm", + "plugins": [], "exec_compatible_with": [ "@platforms//os:linux", "@platforms//cpu:arm" @@ -1688,6 +1689,7 @@ "attributes": { "name": "_main~helm~helm_darwin_amd64_toolchain", "platform": "darwin-amd64", + "plugins": [], "exec_compatible_with": [ "@platforms//os:macos", "@platforms//cpu:x86_64" @@ -1700,6 +1702,7 @@ "attributes": { "name": "_main~helm~helm_linux_arm64_toolchain", "platform": "linux-arm64", + "plugins": [], "exec_compatible_with": [ "@platforms//os:linux", "@platforms//cpu:aarch64" @@ -1712,6 +1715,7 @@ "attributes": { "name": "_main~helm~helm_linux_i386_toolchain", "platform": "linux-i386", + "plugins": [], "exec_compatible_with": [ "@platforms//os:linux", "@platforms//cpu:i386" @@ -1737,6 +1741,7 @@ "attributes": { "name": "_main~helm~helm_windows_amd64_toolchain", "platform": "windows-amd64", + "plugins": [], "exec_compatible_with": [ "@platforms//os:windows" ] @@ -1781,6 +1786,7 @@ "attributes": { "name": "_main~helm~helm_linux_ppc64le_toolchain", "platform": "linux-ppc64le", + "plugins": [], "exec_compatible_with": [ "@platforms//os:linux", "@platforms//cpu:ppc" @@ -1832,6 +1838,7 @@ "attributes": { "name": "_main~helm~helm_linux_amd64_toolchain", "platform": "linux-amd64", + "plugins": [], "exec_compatible_with": [ "@platforms//os:linux", "@platforms//cpu:x86_64" @@ -1857,6 +1864,7 @@ "attributes": { "name": "_main~helm~helm_darwin_arm64_toolchain", "platform": "darwin-arm64", + "plugins": [], "exec_compatible_with": [ "@platforms//os:macos", "@platforms//cpu:aarch64" @@ -1875,10 +1883,22 @@ }, "//tests:test_extensions.bzl%helm_test": { "general": { - "bzlTransitiveDigest": "/daJAFxR7sMlWp6zD7AgTn+TxYSzJhDCWX13TZSPZdo=", + "bzlTransitiveDigest": "1tWldiuOjZhyVE4Y1b297tdf7pIsHDgKx7xTTCrHt2g=", "accumulatedFileDigests": {}, "envVariables": {}, "generatedRepoSpecs": { + "helm_cm_push_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "_main~helm_test~helm_cm_push_linux", + "urls": [ + "https://github.com/chartmuseum/helm-push/releases/download/v0.10.4/helm-push_0.10.4_linux_amd64.tar.gz" + ], + "integrity": "sha256-KfH3E2mbR+PJwY1gtQVffA6LzBIh1mOcBX54fgi2Vqg=", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"plugin.yaml\"])\n\nfilegroup(\n name = \"data\",\n srcs = glob([\"bin/**\"]),\n)\n" + } + }, "helm_test_deps__with_chart_deps_postgresql": { "bzlFile": "@@//helm/private:helm_import.bzl", "ruleClassName": "helm_import_repository", @@ -1919,6 +1939,30 @@ "reproducible": true } }, + "helm_cm_push_macos": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "_main~helm_test~helm_cm_push_macos", + "urls": [ + "https://github.com/chartmuseum/helm-push/releases/download/v0.10.4/helm-push_0.10.4_darwin_arm64.tar.gz" + ], + "integrity": "sha256-oKyCvUYCHt/LPcIj99ZaVP6PlpGPy7dgwTl/yo43SqI=", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"plugin.yaml\"])\n\nfilegroup(\n name = \"data\",\n srcs = glob([\"bin/**\"]),\n)\n" + } + }, + "helm_cm_push_windows": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "name": "_main~helm_test~helm_cm_push_windows", + "urls": [ + "https://github.com/chartmuseum/helm-push/releases/download/v0.10.4/helm-push_0.10.4_windows_amd64.tar.gz" + ], + "integrity": "sha256-aFkN3IJXd8TVlJ/NY3v2sZ4Rerp644e02u0HNqboELw=", + "build_file_content": "package(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"plugin.yaml\"])\n\nfilegroup(\n name = \"data\",\n srcs = glob([\"bin/**\"]),\n)\n" + } + }, "rules_helm_test_container_base_single": { "bzlFile": "@@rules_oci~2.0.0//oci/private:pull.bzl", "ruleClassName": "oci_pull", diff --git a/README.md b/README.md index ffece1f..aa5ae82 100755 --- a/README.md +++ b/README.md @@ -186,6 +186,27 @@ Rules for creating Helm chart packages. | values_json | The `values.yaml` file for the current package as a json object. | String | optional | `""` | + + +## helm_plugin + +
+helm_plugin(name, data, plugin_name, yaml)
+
+ +Define a [helm plugin](https://helm.sh/docs/topics/plugins/). + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| data | Additional files associated with the plugin. | List of labels | optional | `[]` | +| plugin_name | An explicit name for the plugin. If unset, `name` will be used. | String | optional | `""` | +| yaml | The yaml file representing the plugin | Label | required | | + + ## helm_push @@ -267,10 +288,10 @@ if the following environment variables are defined: ## helm_toolchain
-helm_toolchain(name, helm)
+helm_toolchain(name, helm, plugins)
 
-A helm toolchain +A helm toolchain. **ATTRIBUTES** @@ -279,6 +300,7 @@ A helm toolchain | :------------- | :------------- | :------------- | :------------- | :------------- | | name | A unique name for this target. | Name | required | | | helm | A helm binary | Label | required | | +| plugins | Additional plugins to make available to helm. | List of labels | optional | `[]` | @@ -379,8 +401,8 @@ str: A json encoded string which represents `Chart.yaml` contents.
 helm_chart(name, chart, chart_json, crds, values, values_json, substitutions, templates, images,
-           deps, install_name, registry_url, helm_opts, install_opts, upgrade_opts, uninstall_opts,
-           data, stamp, kwargs)
+           deps, install_name, registry_url, login_url, helm_opts, install_opts, upgrade_opts,
+           uninstall_opts, data, stamp, kwargs)
 
Rules for producing a helm package and some convenience targets. @@ -413,6 +435,7 @@ Rules for producing a helm package and some convenience targets. | deps | A list of helm package dependencies. | `None` | | install_name | The `helm install` name to use. `name` will be used if unset. | `None` | | registry_url | The registry url for the helm chart. `{name}.push_registry` is only defined when a value is passed here. | `None` | +| login_url | The registry url to log into for publishing helm charts. | `None` | | helm_opts | Additional options to pass to helm. | `[]` | | install_opts | Additional options to pass to `helm install`. | `[]` | | upgrade_opts | Additional options to pass to `helm upgrade`. | `[]` | @@ -427,7 +450,7 @@ Rules for producing a helm package and some convenience targets. ## helm_register_toolchains
-helm_register_toolchains(version, helm_url_templates)
+helm_register_toolchains(version, helm_url_templates, plugins)
 
Register helm toolchains. @@ -439,6 +462,7 @@ Register helm toolchains. | :------------- | :------------- | :------------- | | version | The version of Helm to use | `"3.16.1"` | | helm_url_templates | A list of url templates where helm can be downloaded. | `["https://get.helm.sh/helm-v{version}-{platform}.{compression}"]` | +| plugins | Labels to `helm_plugin` targets to add to generated toolchains. | `[]` | diff --git a/helm/defs.bzl b/helm/defs.bzl index 9352292..e404b78 100644 --- a/helm/defs.bzl +++ b/helm/defs.bzl @@ -105,6 +105,11 @@ load( _helm_push = "helm_push", _helm_push_images = "helm_push_images", ) +load( + "//helm/private:helm_toolchain.bzl", + _helm_plugin = "helm_plugin", + _helm_toolchain = "helm_toolchain", +) load( ":providers.bzl", _HelmPackageInfo = "HelmPackageInfo", @@ -114,10 +119,6 @@ load( _helm_register_toolchains = "helm_register_toolchains", _rules_helm_dependencies = "rules_helm_dependencies", ) -load( - ":toolchain.bzl", - _helm_toolchain = "helm_toolchain", -) helm_chart = _helm_chart helm_import = _helm_import @@ -126,6 +127,7 @@ helm_install = _helm_install helm_lint_aspect = _helm_lint_aspect helm_lint_test = _helm_lint_test helm_package = _helm_package +helm_plugin = _helm_plugin helm_push = _helm_push helm_push_images = _helm_push_images helm_push_registry = _helm_push diff --git a/helm/extensions.bzl b/helm/extensions.bzl index 8213ca7..00f2a91 100644 --- a/helm/extensions.bzl +++ b/helm/extensions.bzl @@ -25,11 +25,12 @@ def _helm_impl(ctx): options = module.tags.options version = options[0].version helm_url_templates = options[0].helm_url_templates + plugins = options[0].plugins - _register_toolchains(version, helm_url_templates) + _register_toolchains(version, helm_url_templates, plugins) _register_go_yaml() -def _register_toolchains(version, helm_url_templates): +def _register_toolchains(version, helm_url_templates, plugins): if not version in HELM_VERSIONS: fail("{} is not a supported version ({})".format(version, HELM_VERSIONS.keys())) @@ -73,6 +74,7 @@ def _register_toolchains(version, helm_url_templates): helm_toolchain_repository, name = name + "_toolchain", platform = platform, + plugins = plugins, exec_compatible_with = CONSTRAINTS[platform], ) @@ -100,6 +102,10 @@ options = tag_class(attrs = { ), default = DEFAULT_HELM_URL_TEMPLATES, ), + "plugins": attr.string_list( + doc = "A list of plugins to add to the generated toolchain.", + default = [], + ), "version": attr.string( doc = "The version of helm to download for the toolchain.", default = DEFAULT_HELM_VERSION, diff --git a/helm/private/BUILD.bazel b/helm/private/BUILD.bazel index 6e1c71f..f5f4bff 100644 --- a/helm/private/BUILD.bazel +++ b/helm/private/BUILD.bazel @@ -5,7 +5,6 @@ stamp_build_setting(name = "stamp") filegroup( name = "bzl_srcs", srcs = glob(["**/*.bzl"]) + [ - "//helm/private/packager:bzl_srcs", "//helm/private/pusher:bzl_srcs", "//helm/private/runner:bzl_srcs", "//helm/private/stamp:bzl_srcs", diff --git a/helm/private/current_toolchain.bzl b/helm/private/current_toolchain.bzl index 21000bb..bd51fae 100644 --- a/helm/private/current_toolchain.bzl +++ b/helm/private/current_toolchain.bzl @@ -12,7 +12,7 @@ def _current_helm_toolchain_impl(ctx): toolchain_info = ctx.toolchains["@rules_helm//helm:toolchain_type"] return [ toolchain_info, - toolchain_info.default, + toolchain_info.default_info, toolchain_info.template_variables, ] diff --git a/helm/private/helm.bzl b/helm/private/helm.bzl index 47e07f7..926d072 100644 --- a/helm/private/helm.bzl +++ b/helm/private/helm.bzl @@ -17,6 +17,7 @@ def helm_chart( deps = None, install_name = None, registry_url = None, + login_url = None, helm_opts = [], install_opts = [], upgrade_opts = [], @@ -50,6 +51,7 @@ def helm_chart( install_name (str, optional): The `helm install` name to use. `name` will be used if unset. registry_url (str, Optional): The registry url for the helm chart. `{name}.push_registry` is only defined when a value is passed here. + login_url (str, optional): The registry url to log into for publishing helm charts. helm_opts (list, optional): Additional options to pass to helm. install_opts (list, optional): Additional options to pass to `helm install`. uninstall_opts (list, optional): Additional options to pass to `helm uninstall`. @@ -91,6 +93,7 @@ def helm_chart( package = name, include_images = False, registry_url = registry_url, + login_url = login_url, **kwargs ) @@ -99,6 +102,7 @@ def helm_chart( include_images = True, package = name, registry_url = registry_url, + login_url = login_url, **kwargs ) diff --git a/helm/private/helm_cmd/BUILD.bazel b/helm/private/helm_cmd/BUILD.bazel new file mode 100644 index 0000000..fd33916 --- /dev/null +++ b/helm/private/helm_cmd/BUILD.bazel @@ -0,0 +1,8 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "helm_cmd", + srcs = ["helm_cmd.go"], + importpath = "github.com/abrisco/rules_helm/helm/private/helm_cmd", + visibility = ["//helm:__subpackages__"], +) diff --git a/helm/private/helm_cmd/helm_cmd.go b/helm/private/helm_cmd/helm_cmd.go new file mode 100644 index 0000000..b9f23e5 --- /dev/null +++ b/helm/private/helm_cmd/helm_cmd.go @@ -0,0 +1,43 @@ +package helm_cmd + +import ( + "fmt" + "log" + "os" + "os/exec" + "path/filepath" +) + +func BuildHelmCommand(helmPath string, args []string, pluginsDir string) (exec.Cmd, error) { + // Create a temporary directory with a specified prefix. + tempDir, err := os.MkdirTemp(os.Getenv("TEST_TMPDIR"), "helm_cmd-") + if err != nil { + log.Fatal(err) + } + + // Generate a fake kubeconfig for more consistent results when building packages + kubeconfig := filepath.Join(tempDir, ".kubeconfig") + file, err := os.Create(kubeconfig) + if err != nil { + log.Fatal(err) + } + if err := file.Chmod(0700); err != nil { + log.Fatal(err) + } + file.Close() + + // Set the HELM_PLUGINS environment variable for plugins directory + cmd := exec.Command(helmPath, args...) + + env := os.Environ() + env = append(env, fmt.Sprintf("HELM_PLUGINS=%s", pluginsDir)) + env = append(env, fmt.Sprintf("HELM_CACHE_HOME=%s", filepath.Join(tempDir, "cache"))) + env = append(env, fmt.Sprintf("HELM_CONFIG_HOME=%s", filepath.Join(tempDir, "config"))) + env = append(env, fmt.Sprintf("HELM_DATA_HOME=%s", filepath.Join(tempDir, "data"))) + env = append(env, fmt.Sprintf("HELM_REPOSITORY_CACHE=%s", filepath.Join(tempDir, "repository_cache"))) + env = append(env, fmt.Sprintf("HELM_REPOSITORY_CONFIG=%s", filepath.Join(tempDir, "repositories.yaml"))) + env = append(env, fmt.Sprintf("HELM_REGISTRY_CONFIG=%s", filepath.Join(tempDir, "config.json"))) + env = append(env, fmt.Sprintf("KUBECONFIG=%s", kubeconfig)) + + return *cmd, nil +} diff --git a/helm/private/helm_lint.bzl b/helm/private/helm_lint.bzl index da1928c..1b0fdd7 100644 --- a/helm/private/helm_lint.bzl +++ b/helm/private/helm_lint.bzl @@ -14,6 +14,7 @@ def _helm_lint_aspect_impl(target, ctx): args = ctx.actions.args() args.add("-helm", toolchain.helm) + args.add("-helm_plugins", toolchain.helm_plugins.path) args.add("-package", helm_pkg_info.chart) args.add("-output", output) @@ -22,7 +23,7 @@ def _helm_lint_aspect_impl(target, ctx): executable = ctx.executable._linter, mnemonic = "HelmLintCheck", inputs = [helm_pkg_info.chart], - tools = [toolchain.helm], + tools = [toolchain.helm, toolchain.helm_plugins], arguments = [args], ) @@ -54,14 +55,15 @@ def _helm_lint_test_impl(ctx): helm_pkg_info = ctx.attr.chart[HelmPackageInfo] toolchain = ctx.toolchains[Label("//helm:toolchain_type")] + args = ctx.actions.args() + args.set_param_file_format("multiline") + args.add("-helm", rlocationpath(toolchain.helm, ctx.workspace_name)) + args.add("-helm_plugins", rlocationpath(toolchain.helm_plugins, ctx.workspace_name)) + args.add("-package", rlocationpath(helm_pkg_info.chart, ctx.workspace_name)) + ctx.actions.write( output = args_file, - content = "\n".join([ - "-helm", - rlocationpath(toolchain.helm, ctx.workspace_name), - "-package", - rlocationpath(helm_pkg_info.chart, ctx.workspace_name), - ]), + content = args, ) if toolchain.helm.basename.endswith(".exe"): @@ -79,7 +81,7 @@ def _helm_lint_test_impl(ctx): DefaultInfo( files = depset([test_runner]), runfiles = ctx.runfiles( - files = [toolchain.helm, helm_pkg_info.chart, args_file], + files = [toolchain.helm, toolchain.helm_plugins, helm_pkg_info.chart, args_file], ).merge(ctx.attr._linter[DefaultInfo].default_runfiles), executable = test_runner, ), diff --git a/helm/private/helm_package.bzl b/helm/private/helm_package.bzl index dec1f96..6b3995a 100644 --- a/helm/private/helm_package.bzl +++ b/helm/private/helm_package.bzl @@ -90,6 +90,7 @@ def _helm_package_impl(ctx): toolchain = ctx.toolchains[Label("//helm:toolchain_type")] args.add("-helm", toolchain.helm) + args.add("-helm_plugins", toolchain.helm_plugins.path) args.add("-chart", chart_yaml) args.add("-values", values_yaml) @@ -179,7 +180,13 @@ def _helm_package_impl(ctx): executable = ctx.executable._packager, outputs = [output, metadata_output], inputs = depset( - ctx.files.templates + ctx.files.crds + stamps + image_inputs + deps + [chart_yaml, values_yaml, templates_manifest, crds_manifest, substitutions_file], + ctx.files.templates + ctx.files.crds + stamps + image_inputs + deps + [ + chart_yaml, + values_yaml, + templates_manifest, + crds_manifest, + substitutions_file, + ], ), tools = depset([toolchain.helm]), mnemonic = "HelmPackage", diff --git a/helm/private/helm_registry.bzl b/helm/private/helm_registry.bzl index f313909..02c92e3 100644 --- a/helm/private/helm_registry.bzl +++ b/helm/private/helm_registry.bzl @@ -34,6 +34,7 @@ def _helm_push_impl(ctx): args = ctx.actions.args() args.set_param_file_format("multiline") args.add("-helm", rlocationpath(toolchain.helm, ctx.workspace_name)) + args.add("-helm_plugins", rlocationpath(toolchain.helm_plugins, ctx.workspace_name)) args.add("-chart", rlocationpath(pkg_info.chart, ctx.workspace_name)) args.add("-registry_url", ctx.attr.registry_url) @@ -60,6 +61,7 @@ def _helm_push_impl(ctx): registrar, args_file, toolchain.helm, + toolchain.helm_plugins, pkg_info.chart, ]).merge(image_runfiles) diff --git a/helm/private/helm_toolchain.bzl b/helm/private/helm_toolchain.bzl new file mode 100644 index 0000000..935c928 --- /dev/null +++ b/helm/private/helm_toolchain.bzl @@ -0,0 +1,132 @@ +"""rules_helm toolchain implementation""" + +HelmPluginInfo = provider( + doc = "Info about a Helm plugin.", + fields = { + "data": "Depset[File]: Files associated with the plugin.", + "name": "String: The name of the plugin.", + "yaml": "File: The yaml file representing the plugin.", + }, +) + +def _helm_plugin_impl(ctx): + name = ctx.attr.plugin_name + if not name: + name = ctx.label.name + + return [ + HelmPluginInfo( + yaml = ctx.file.yaml, + name = name, + data = depset(ctx.files.data), + ), + ] + +helm_plugin = rule( + doc = "Define a [helm plugin](https://helm.sh/docs/topics/plugins/).", + implementation = _helm_plugin_impl, + attrs = { + "data": attr.label_list( + doc = "Additional files associated with the plugin.", + allow_files = True, + ), + "plugin_name": attr.string( + doc = "An explicit name for the plugin. If unset, `name` will be used.", + ), + "yaml": attr.label( + doc = "The yaml file representing the plugin", + allow_single_file = True, + mandatory = True, + ), + }, +) + +def _create_plugins_dir(*, ctx, plugins, output): + manifest = ctx.actions.declare_file("{}.manifest".format(output.basename), sibling = output) + manifest_data = {} + inputs = [depset([manifest])] + for plugin in plugins: + info = plugin[HelmPluginInfo] + inputs.append(depset([info.yaml], transitive = [info.data])) + + if info.name in manifest_data: + fail("Two plugins sharing the name {} were provided. Please update {}".format( + info.name, + ctx.label, + )) + + manifest_data[info.name] = { + "data": [f.path for f in info.data.to_list()], + "yaml": info.yaml.path, + } + + ctx.actions.write( + output = manifest, + content = json.encode_indent(manifest_data, indent = " " * 4) + "\n", + ) + + args = ctx.actions.args() + args.add("-manifest", manifest) + args.add("-output", output.path) + + ctx.actions.run( + mnemonic = "HelmPluginsDir", + progress_message = "HelmPluginsDir %{label}", + executable = ctx.executable._plugins_builder, + arguments = [args], + inputs = depset(transitive = inputs), + outputs = [output], + ) + + return output + +def _helm_toolchain_impl(ctx): + binary = ctx.file.helm + + plugins_dir = _create_plugins_dir( + ctx = ctx, + plugins = ctx.attr.plugins, + output = ctx.actions.declare_directory("{}.plugins".format(ctx.label.name)), + ) + + template_variables = platform_common.TemplateVariableInfo({ + "HELM_BIN": binary.path, + "HELM_PLUGINS": plugins_dir.path, + }) + + default_info = DefaultInfo( + files = depset([binary]), + runfiles = ctx.runfiles(files = [binary, plugins_dir]), + ) + + toolchain_info = platform_common.ToolchainInfo( + default_info = default_info, + helm = binary, + helm_plugins = plugins_dir, + template_variables = template_variables, + ) + + return [default_info, toolchain_info, template_variables] + +helm_toolchain = rule( + implementation = _helm_toolchain_impl, + doc = "A helm toolchain.", + attrs = { + "helm": attr.label( + doc = "A helm binary", + allow_single_file = True, + mandatory = True, + cfg = "exec", + ), + "plugins": attr.label_list( + doc = "Additional plugins to make available to helm.", + cfg = "exec", + providers = [HelmPluginInfo], + ), + "_plugins_builder": attr.label( + default = Label("//helm/private/plugin:plugin_builder"), + cfg = "exec", + executable = True, + ), + }, +) diff --git a/helm/private/linter/BUILD.bazel b/helm/private/linter/BUILD.bazel index 900f253..55661f9 100644 --- a/helm/private/linter/BUILD.bazel +++ b/helm/private/linter/BUILD.bazel @@ -1,9 +1,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary") -package(default_visibility = ["//visibility:public"]) - go_binary( name = "linter", srcs = ["linter.go"], - deps = ["@io_bazel_rules_go//go/runfiles"], + deps = [ + "@io_bazel_rules_go//go/runfiles", + "//helm/private/helm_cmd", + ], + visibility = ["//visibility:public"] ) diff --git a/helm/private/linter/linter.go b/helm/private/linter/linter.go index c48d9e2..713c869 100644 --- a/helm/private/linter/linter.go +++ b/helm/private/linter/linter.go @@ -10,17 +10,18 @@ import ( "io" "log" "os" - "os/exec" "path/filepath" "strings" + "github.com/abrisco/rules_helm/helm/private/helm_cmd" "github.com/bazelbuild/rules_go/go/runfiles" ) type Arguments struct { - helm string - pkg string - output string + helm string + helmPlugins string + pkg string + output string } func get_runfile(runfile_path string) string { @@ -59,6 +60,7 @@ func parse_args() Arguments { var args Arguments flag.StringVar(&args.helm, "helm", "", "The path to a helm executable") + flag.StringVar(&args.helmPlugins, "helm_plugins", "", "The path to a helm plugins directory") flag.StringVar(&args.output, "output", "", "The path to the Bazel `HelmPackage` action output") flag.StringVar(&args.pkg, "package", "", "The path to the helm package to lint.") @@ -165,10 +167,15 @@ func find_package_root(extract_dir string) string { return file_info[0].Name() } -func lint(directory string, package_name string, helm string, output string) { - command := exec.Command(helm, "lint", package_name) - command.Dir = directory - out, err := command.Output() +func lint(directory string, package_name string, helm string, helmPluginsDir string, output string) { + cmd, err := helm_cmd.BuildHelmCommand(helm, []string{"lint", package_name}, helmPluginsDir) + if err != nil { + log.Fatal(err) + } + + cmd.Dir = directory + + out, err := cmd.Output() if err != nil { os.Stderr.WriteString(string(out)) log.Fatal(err) @@ -239,12 +246,15 @@ func main() { var pkg = args.pkg var helm = args.helm + var helmPlugins = args.helmPlugins if is_test { pkg = get_runfile(pkg) helm = get_runfile(helm) + helmPlugins = get_runfile(helmPlugins) } else { pkg = makeAbsolutePath(pkg) helm = makeAbsolutePath(helm) + helmPlugins = makeAbsolutePath(helmPlugins) } if err := extractPackage(pkg, dir); err != nil { @@ -253,5 +263,5 @@ func main() { lint_dir := find_package_root(dir) - lint(dir, lint_dir, helm, args.output) + lint(dir, lint_dir, helm, helmPlugins, args.output) } diff --git a/helm/private/packager/BUILD.bazel b/helm/private/packager/BUILD.bazel index ddf31dd..1f409f0 100644 --- a/helm/private/packager/BUILD.bazel +++ b/helm/private/packager/BUILD.bazel @@ -6,14 +6,6 @@ go_binary( visibility = ["//visibility:public"], deps = [ "@go_yaml_yaml", + "//helm/private/helm_cmd", ], ) - -filegroup( - name = "bzl_srcs", - srcs = glob( - ["**/*.bzl"], - allow_empty = True, - ), - visibility = ["//:__subpackages__"], -) diff --git a/helm/private/packager/packager.go b/helm/private/packager/packager.go index 1c0c5d3..a6c373a 100644 --- a/helm/private/packager/packager.go +++ b/helm/private/packager/packager.go @@ -10,11 +10,11 @@ import ( "io" "log" "os" - "os/exec" "path/filepath" "regexp" "strings" + "github.com/abrisco/rules_helm/helm/private/helm_cmd" "gopkg.in/yaml.v3" ) @@ -95,6 +95,7 @@ type Arguments struct { Substitutions string DepsManifest string Helm string + HelmPlugins string Output string MetadataOutput string ImageManifest string @@ -106,19 +107,20 @@ type Arguments struct { func parseArgs() Arguments { var args Arguments - flag.StringVar(&args.TemplatesManifest, "templates_manifest", "", "A helm file containing a list of all helm template files") - flag.StringVar(&args.CrdsManifest, "crds_manifest", "", "A helm file containing a list of all helm crd files") - flag.StringVar(&args.Chart, "chart", "", "The helm `chart.yaml` file") + flag.StringVar(&args.TemplatesManifest, "templates_manifest", "", "A helm file containing a list of all helm template files.") + flag.StringVar(&args.CrdsManifest, "crds_manifest", "", "A helm file containing a list of all helm crd files.") + flag.StringVar(&args.Chart, "chart", "", "The helm `chart.yaml` file.") flag.StringVar(&args.Values, "values", "", "The helm `values.yaml` file.") - flag.StringVar(&args.Substitutions, "substitutions", "", "A json file containing key value pairs to substitute into the values file") - flag.StringVar(&args.DepsManifest, "deps_manifest", "", "A file containing a list of all helm dependency (`charts/*.tgz`) files") - flag.StringVar(&args.Helm, "helm", "", "The path to a helm executable") + flag.StringVar(&args.Substitutions, "substitutions", "", "A json file containing key value pairs to substitute into the values file.") + flag.StringVar(&args.DepsManifest, "deps_manifest", "", "A file containing a list of all helm dependency (`charts/*.tgz`) files.") + flag.StringVar(&args.Helm, "helm", "", "The path to a helm executable.") + flag.StringVar(&args.HelmPlugins, "helm_plugins", "", "The path to a helm plugins directory.") flag.StringVar(&args.Output, "output", "", "The path to the Bazel `HelmPackage` action output") - flag.StringVar(&args.MetadataOutput, "metadata_output", "", "The path to the Bazel `HelmPackage` action metadata output") - flag.StringVar(&args.ImageManifest, "image_manifest", "", "Information about Bazel produced container oci images used by the helm chart") - flag.StringVar(&args.StableStatusFile, "stable_status_file", "", "The stable status file (`ctx.info_file`)") - flag.StringVar(&args.VolatileStatusFile, "volatile_status_file", "", "The stable status file (`ctx.version_file`)") - flag.StringVar(&args.WorkspaceName, "workspace_name", "", "The name of the current Bazel workspace") + flag.StringVar(&args.MetadataOutput, "metadata_output", "", "The path to the Bazel `HelmPackage` action metadata output.") + flag.StringVar(&args.ImageManifest, "image_manifest", "", "Information about Bazel produced container oci images used by the helm chart.") + flag.StringVar(&args.StableStatusFile, "stable_status_file", "", "The stable status file (`ctx.info_file`).") + flag.StringVar(&args.VolatileStatusFile, "volatile_status_file", "", "The stable status file (`ctx.version_file`).") + flag.StringVar(&args.WorkspaceName, "workspace_name", "", "The name of the current Bazel workspace.") flag.Parse() return args @@ -805,10 +807,14 @@ func main() { } // Build the helm package - command := exec.Command(filepath.Join(cwd, args.Helm), "package", ".") - command.Dir = tmpPath - command.Env = append(os.Environ(), fmt.Sprintf("KUBECONFIG=%s", kubeconfig)) - out, err := command.CombinedOutput() + cmd, err := helm_cmd.BuildHelmCommand(filepath.Join(cwd, args.Helm), []string{"package", "."}, filepath.Join(cwd, args.HelmPlugins)) + if err != nil { + log.Fatal(err) + } + + cmd.Dir = tmpPath + + out, err := cmd.CombinedOutput() if err != nil { os.Stderr.WriteString(string(out)) log.Fatal(err) diff --git a/helm/private/plugin/BUILD.bazel b/helm/private/plugin/BUILD.bazel new file mode 100644 index 0000000..61f6f88 --- /dev/null +++ b/helm/private/plugin/BUILD.bazel @@ -0,0 +1,7 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary") + +go_binary( + name = "plugin_builder", + srcs = ["plugin_builder.go"], + visibility = ["//visibility:public"], +) diff --git a/helm/private/plugin/plugin_builder.go b/helm/private/plugin/plugin_builder.go new file mode 100644 index 0000000..44d0a65 --- /dev/null +++ b/helm/private/plugin/plugin_builder.go @@ -0,0 +1,116 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" +) + +// ManifestEntry represents the structure of each entry in the manifest +type ManifestEntry struct { + YAML string `json:"yaml"` + Data []string `json:"data"` +} + +// Manifest represents the entire manifest map +type Manifest map[string]ManifestEntry + +func main() { + // Parse command-line flags + outputDir := flag.String("output", "", "Path to the output directory") + manifestPath := flag.String("manifest", "", "Path to the manifest JSON file") + flag.Parse() + + // Validate the flags + if *outputDir == "" || *manifestPath == "" { + fmt.Println("Both -output and -manifest are required") + flag.Usage() + os.Exit(1) + } + + // Create an empty .rules_helm file in the output directory + rulesHelmPath := filepath.Join(*outputDir, ".rules_helm") + if _, err := os.Create(rulesHelmPath); err != nil { + log.Fatalf("Failed to create .rules_helm file in %s: %v\n", *outputDir, err) + } + + // Read and parse the manifest JSON + manifestFile, err := os.Open(*manifestPath) + if err != nil { + log.Fatalf("Failed to open manifest file: %v\n", err) + } + defer manifestFile.Close() + + var manifest Manifest + decoder := json.NewDecoder(manifestFile) + if err := decoder.Decode(&manifest); err != nil { + log.Fatalf("Failed to parse manifest JSON: %v\n", err) + } + + // Process each entry in the manifest + for key, entry := range manifest { + // Validate that all data files have the same parent directory as the YAML file + yamlDir := filepath.Dir(entry.YAML) + + for _, dataFile := range entry.Data { + if !strings.HasPrefix(filepath.Dir(dataFile), yamlDir) { + log.Fatalf("Error: Data file %s does not have the same parent directory as YAML file %s\n", dataFile, entry.YAML) + } + } + + // Create the directory for this key in the output path + targetDir := filepath.Join(*outputDir, key) + if err := os.MkdirAll(targetDir, os.ModePerm); err != nil { + log.Fatalf("Failed to create directory %s: %v\n", targetDir, err) + } + + // Copy the YAML file + if err := copyFile(entry.YAML, filepath.Join(targetDir, filepath.Base(entry.YAML))); err != nil { + log.Fatalf("Failed to copy YAML file for %s: %v\n", key, err) + } + + // Copy each data file and preserve the relative directory structure + for _, dataFile := range entry.Data { + relativePath, err := filepath.Rel(yamlDir, dataFile) + if err != nil { + log.Fatalf("Failed to determine relative path for %s: %v\n", dataFile, err) + } + + dataTargetPath := filepath.Join(targetDir, relativePath) + if err := os.MkdirAll(filepath.Dir(dataTargetPath), os.ModePerm); err != nil { + log.Fatalf("Failed to create directory %s: %v\n", filepath.Dir(dataTargetPath), err) + } + + if err := copyFile(dataFile, dataTargetPath); err != nil { + log.Fatalf("Failed to copy data file %s for %s: %v\n", dataFile, key, err) + } + } + } +} + +// copyFile copies a file from src to dst +func copyFile(src, dst string) error { + sourceFile, err := os.Open(src) + if err != nil { + return fmt.Errorf("unable to open source file: %w", err) + } + defer sourceFile.Close() + + destinationFile, err := os.Create(dst) + if err != nil { + return fmt.Errorf("unable to create destination file: %w", err) + } + defer destinationFile.Close() + + _, err = io.Copy(destinationFile, sourceFile) + if err != nil { + return fmt.Errorf("failed to copy data: %w", err) + } + + return nil +} diff --git a/helm/private/registrar/BUILD.bazel b/helm/private/registrar/BUILD.bazel index a52c668..c87ec72 100644 --- a/helm/private/registrar/BUILD.bazel +++ b/helm/private/registrar/BUILD.bazel @@ -6,5 +6,6 @@ go_binary( visibility = ["//visibility:public"], deps = [ "@io_bazel_rules_go//go/runfiles", + "//helm/private/helm_cmd", ], ) diff --git a/helm/private/registrar/registrar.go b/helm/private/registrar/registrar.go index dc49b92..56a1109 100644 --- a/helm/private/registrar/registrar.go +++ b/helm/private/registrar/registrar.go @@ -10,6 +10,7 @@ import ( "os/exec" "strings" + "github.com/abrisco/rules_helm/helm/private/helm_cmd" "github.com/bazelbuild/rules_go/go/runfiles" ) @@ -67,6 +68,80 @@ func getHostFromURL(inputURL string) (string, error) { return parsedURL.Host, nil } +// Because the registrar runs outside of Bazel, there are environment variables +// we want to explicitly allow from the host machine. +func updateEnv(current []string) []string { + + keysToRemove := []string{ + "HELM_CACHE_HOME", + "HELM_CONFIG_HOME", + "HELM_DATA_HOME", + "HELM_REPOSITORY_CACHE", + "HELM_REPOSITORY_CONFIG", + "HELM_REGISTRY_CONFIG", + "KUBECONFIG", + } + + // Convert envVars to a map for easier lookup + currentEnv := make(map[string]string) + for _, envVar := range current { + parts := strings.SplitN(envVar, "=", 2) + if len(parts) == 2 { + key := parts[0] + value := parts[1] + currentEnv[key] = value + } + } + + // Convert replacements to a map for easier lookup + globalEnv := make(map[string]string) + for _, replacement := range os.Environ() { + parts := strings.SplitN(replacement, "=", 2) + if len(parts) == 2 { + key := parts[0] + value := parts[1] + globalEnv[key] = value + } + } + + // Process the removal and replacement + for _, key := range keysToRemove { + if value, exists := globalEnv[key]; exists { + // If the key exists in the replacements map, update the original map + currentEnv[key] = value + } else { + // If the key does not exist in the replacements map, delete it + delete(currentEnv, key) + } + } + + newEnv := make([]string, 0, len(currentEnv)) + for key, value := range currentEnv { + newEnv = append(newEnv, fmt.Sprintf("%s=%s", key, value)) + } + + return newEnv +} + +func runHelm(helmPath string, args []string, pluginsDir string, stdin *string) { + cmd, err := helm_cmd.BuildHelmCommand(helmPath, args, pluginsDir) + if err != nil { + log.Fatal(err) + } + + cmd.Env = updateEnv(cmd.Env) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if stdin != nil { + cmd.Stdin = strings.NewReader(*stdin) + } + + if err := cmd.Run(); err != nil { + log.Fatalf("Failed to run helm command: %w", err) + } +} + func main() { // Get the file path for args argsRlocation := os.Getenv("HELM_PUSH_ARGS_FILE") @@ -84,6 +159,7 @@ func main() { // Setup flags for helm, chart, registry_url, and image_pushers rawHelmPath := flag.String("helm", "", "Path to helm binary") + rawHelmPluginsPath := flag.String("helm_plugins", "", "The path to helm plugins.") rawChartPath := flag.String("chart", "", "Path to Helm .tgz file") registryURL := flag.String("registry_url", "", "URL of registry to upload helm chart") rawLoginURL := flag.String("login_url", "", "URL of registry to login to.") @@ -98,6 +174,7 @@ func main() { } helmPath := getRunfile(*rawHelmPath) + helmPluginsPath := getRunfile(*rawHelmPluginsPath) chartPath := getRunfile(*rawChartPath) var imagePushers []string @@ -144,17 +221,8 @@ func main() { } } - loginCmd := exec.Command(helmPath, "registry", "login", "--username", helmUser, "--password-stdin", loginUrl) - loginCmd.Stdout = os.Stdout - loginCmd.Stderr = os.Stderr - - // Provide the password to stdin of the login command - loginCmd.Stdin = strings.NewReader(helmPassword) - log.Printf("Logging into Helm registry `%s`...\n", loginUrl) - if err := loginCmd.Run(); err != nil { - log.Fatalf("Failed to login to Helm registry: %v", err) - } + runHelm(helmPath, []string{"registry", "login", "--username", helmUser, "--password-stdin", loginUrl}, helmPluginsPath, &helmPassword) } else if helmUser != "" { log.Printf("WARNING: A Helm registry username was set but no associated `HELM_REGISTRY_PASSWORD`/`HELM_REGISTRY_PASSWORD_FILE` var was found. Skipping `helm registry login`.") } else if helmPassword != "" { @@ -174,12 +242,6 @@ func main() { } // Subprocess helm push - pushCmd := exec.Command(helmPath, "push", chartPath, *registryURL) - pushCmd.Stdout = os.Stdout - pushCmd.Stderr = os.Stderr - - log.Printf("Running helm push: %s", pushCmd.String()) - if err := pushCmd.Run(); err != nil { - log.Fatalf("Failed to push helm chart: %v", err) - } + log.Println("Running helm push...") + runHelm(helmPath, []string{"push", chartPath, *registryURL}, helmPluginsPath, nil) } diff --git a/helm/repositories.bzl b/helm/repositories.bzl index 43f3cfc..602f0de 100644 --- a/helm/repositories.bzl +++ b/helm/repositories.bzl @@ -61,6 +61,7 @@ package(default_visibility = ["//visibility:public"]) helm_toolchain( name = "toolchain_impl", helm = "@helm_{platform}//:{bin}", + plugins = {plugins}, ) toolchain( @@ -89,6 +90,7 @@ def _helm_toolchain_repository_impl(repository_ctx): bin = bin, exec_compatible_with = json.encode(repository_ctx.attr.exec_compatible_with), target_compatible_with = json.encode(repository_ctx.attr.target_compatible_with), + plugins = json.encode(repository_ctx.attr.plugins), )) repository_ctx.file("WORKSPACE.bazel", _HELM_WORKSPACE_CONTENT.format( @@ -107,6 +109,10 @@ helm_toolchain_repository = repository_rule( doc = "Platform the Helm executable was built for.", mandatory = True, ), + "plugins": attr.string_list( + doc = "A list of plugins to add to the generated toolchain.", + default = [], + ), "target_compatible_with": attr.string_list( doc = "A list of constraints for the target platform for this toolchain.", default = [], @@ -139,12 +145,13 @@ helm_host_alias_repository = repository_rule( ) # buildifier: disable=unnamed-macro -def helm_register_toolchains(version = DEFAULT_HELM_VERSION, helm_url_templates = DEFAULT_HELM_URL_TEMPLATES): +def helm_register_toolchains(version = DEFAULT_HELM_VERSION, helm_url_templates = DEFAULT_HELM_URL_TEMPLATES, plugins = []): """Register helm toolchains. Args: version (str, optional): The version of Helm to use helm_url_templates (list, optional): A list of url templates where helm can be downloaded. + plugins (list, optional): Labels to `helm_plugin` targets to add to generated toolchains. """ if not version in HELM_VERSIONS: fail("{} is not a supported version ({})".format(version, HELM_VERSIONS.keys())) @@ -184,6 +191,7 @@ def helm_register_toolchains(version = DEFAULT_HELM_VERSION, helm_url_templates helm_toolchain_repository, name = name + "_toolchain", platform = platform, + plugins = plugins, exec_compatible_with = CONSTRAINTS[platform], ) diff --git a/helm/toolchain.bzl b/helm/toolchain.bzl index 0bb5e07..f7c3adb 100644 --- a/helm/toolchain.bzl +++ b/helm/toolchain.bzl @@ -1,32 +1,8 @@ -"""rules_helm toolchain implementation""" +"""Helm toolchain rules""" -def _helm_toolchain_impl(ctx): - binary = ctx.file.helm - template_variables = platform_common.TemplateVariableInfo({ - "HELM_BIN": binary.path, - }) - - default_info = DefaultInfo( - files = depset([binary]), - runfiles = ctx.runfiles(files = [binary]), - ) - - toolchain_info = platform_common.ToolchainInfo( - helm = binary, - default = default_info, - template_variables = template_variables, - ) - - return [default_info, toolchain_info, template_variables] - -helm_toolchain = rule( - implementation = _helm_toolchain_impl, - doc = "A helm toolchain", - attrs = { - "helm": attr.label( - doc = "A helm binary", - allow_single_file = True, - mandatory = True, - ), - }, +load( + "//helm/private:helm_toolchain.bzl", + _helm_toolchain = "helm_toolchain", ) + +helm_toolchain = _helm_toolchain diff --git a/tests/test_deps.bzl b/tests/test_deps.bzl index 9b75a1f..550157f 100644 --- a/tests/test_deps.bzl +++ b/tests/test_deps.bzl @@ -1,10 +1,25 @@ """Dependencies for helm test/example targets""" +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") load("@rules_oci//oci:pull.bzl", "oci_pull") load("//helm:defs.bzl", "helm_import_repository") +_CM_HELM_PUSH_BUILD_CONTENT = """\ +package(default_visibility = ["//visibility:public"]) + +exports_files(["plugin.yaml"]) + +filegroup( + name = "data", + srcs = glob(["bin/**"]), +) +""" + def helm_test_deps(): - helm_import_repository( + """Helm test dependencies""" + maybe( + helm_import_repository, name = "helm_test_deps__with_chart_deps_redis", repository = "https://charts.bitnami.com/bitnami", url = "https://charts.bitnami.com/bitnami/redis-14.4.0.tgz", @@ -13,7 +28,8 @@ def helm_test_deps(): chart_name = "redis", ) - helm_import_repository( + maybe( + helm_import_repository, name = "helm_test_deps__with_chart_deps_postgresql", repository = "https://charts.bitnami.com/bitnami", url = "https://charts.bitnami.com/bitnami/postgresql-14.0.5.tgz", @@ -22,8 +38,33 @@ def helm_test_deps(): chart_name = "postgresql", ) - oci_pull( + maybe( + oci_pull, name = "rules_helm_test_container_base", digest = "sha256:2042a492bcdd847a01cd7f119cd48caa180da696ed2aedd085001a78664407d6", image = "alpine", ) + + maybe( + http_archive, + name = "helm_cm_push_linux", + urls = ["https://github.com/chartmuseum/helm-push/releases/download/v0.10.4/helm-push_0.10.4_linux_amd64.tar.gz"], + integrity = "sha256-KfH3E2mbR+PJwY1gtQVffA6LzBIh1mOcBX54fgi2Vqg=", + build_file_content = _CM_HELM_PUSH_BUILD_CONTENT, + ) + + maybe( + http_archive, + name = "helm_cm_push_macos", + urls = ["https://github.com/chartmuseum/helm-push/releases/download/v0.10.4/helm-push_0.10.4_darwin_arm64.tar.gz"], + integrity = "sha256-oKyCvUYCHt/LPcIj99ZaVP6PlpGPy7dgwTl/yo43SqI=", + build_file_content = _CM_HELM_PUSH_BUILD_CONTENT, + ) + + maybe( + http_archive, + name = "helm_cm_push_windows", + urls = ["https://github.com/chartmuseum/helm-push/releases/download/v0.10.4/helm-push_0.10.4_windows_amd64.tar.gz"], + integrity = "sha256-aFkN3IJXd8TVlJ/NY3v2sZ4Rerp644e02u0HNqboELw=", + build_file_content = _CM_HELM_PUSH_BUILD_CONTENT, + ) diff --git a/tests/toolchain_with_plugins/BUILD.bazel b/tests/toolchain_with_plugins/BUILD.bazel new file mode 100644 index 0000000..ca2e0d8 --- /dev/null +++ b/tests/toolchain_with_plugins/BUILD.bazel @@ -0,0 +1,31 @@ +load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("//helm:defs.bzl", "helm_plugin", "helm_toolchain") + +helm_plugin( + name = "helm_cm_push", + data = select({ + "@platforms//os:linux": ["@helm_cm_push_linux//:data"], + "@platforms//os:macos": ["@helm_cm_push_macos//:data"], + "@platforms//os:windows": ["@helm_cm_push_windows//:data"], + }), + plugin_name = "helm-cm-push", + yaml = select({ + "@platforms//os:linux": "@helm_cm_push_linux//:plugin.yaml", + "@platforms//os:macos": "@helm_cm_push_macos//:plugin.yaml", + "@platforms//os:windows": "@helm_cm_push_windows//:plugin.yaml", + }), +) + +write_file( + name = "helm_bin", + out = "helm.sh", + content = [], +) + +helm_toolchain( + name = "toolchain", + helm = ":helm_bin", + plugins = [ + ":helm_cm_push", + ], +)