Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Tests

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- name: Shellcheck
# uses the shellcheck version pinned in the Makefile, not the runner's
run: make lint

tests:
name: Tests (${{ matrix.name }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# Versions pinned in docker/Dockerfile (what the image ships).
- name: image versions
id: image
make_args: ""
# Oldest helmfile that supports Helm 4.
- name: minimum helm 4
id: helm4-min
make_args: HELM_VERSION=v4.0.0 HELMFILE_VERSION=1.2.0
# Helm 3 is still usable via HELM_BINARY until its EOL (2026-11-11).
- name: helm 3
id: helm3
make_args: HELM_VERSION=v3.19.4
- name: helm 3, minimum helmfile
id: helm3-helmfile-min
make_args: HELM_VERSION=v3.19.4 HELMFILE_VERSION=1.0.0
steps:
- uses: actions/checkout@v5

- name: Cache tools
uses: actions/cache@v5
with:
path: .tools
key: tools-${{ runner.os }}-${{ matrix.id }}-${{ hashFiles('docker/Dockerfile', 'Makefile') }}

- name: Show tool versions under test
run: make versions ${{ matrix.make_args }}

- name: Run tests
run: make test ${{ matrix.make_args }}

docker:
name: Docker image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- name: Build image and run smoke test
run: make test-docker
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.idea
.tools/
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,41 @@ The format is based on Keep a Changelog (https://keepachangelog.com/en/1.1.0/)
and this project adheres to Semantic Versioning (https://semver.org/).
---

## [1.3.2]
### Removed
- Dead code: `if [[ true ]]` wrappers, duplicate `PATH` expansion, unused `print_env_vars`, unused `/tmp/__<script>__/bin` directory, unreachable block after `exit 0` in `parameters`, commented-out `find` blocks.
- Remaining Helm 2 code: `helm init --client-only`, Helm 2 `--kube-version` handling and comments.
- `HELMFILE_HELM3` export (no-op since helmfile v1).

### Changed
- Helm version is detected with `helm version --template '{{.Version}}'` instead of parsing `--short` output.
- `init` and `generate` fail with a clear error for Helm < 3, helmfile < 1 or unparsable versions.
- `discover` and `parameters` no longer run `helm`/`helmfile`.
- Minimum versions: helm >= 3.6, helmfile >= 1 (>= 1.2 with Helm 4).
- Script runs with `set -Eeuo pipefail` and reports the failing phase and line on errors.
- `discover` writes "no match" and "forced response: disabled" messages to stderr; stdout is only used for a match.
- Options in `HELMFILE_GLOBAL_OPTIONS` / `HELMFILE_TEMPLATE_OPTIONS` are split into words without glob expansion; multi-line values are supported.
- `ARGOCD_ENV_*` / `PARAM_*` with names that are not valid shell variables are skipped with a warning instead of failing.
- Missing `helm` / `helmfile` binaries fail with a clear message.
- `KUBE_VERSION` is passed with helmfile's `--kube-version` flag; `KUBE_API_VERSIONS` as one comma-separated `--api-versions`. `--args` is only passed when needed.

### Deprecated
- `HELM_HOME`: use `PLUGIN_APP_HOME`. `HELM_HOME` is still accepted with a warning and is exported with the same value.

### Fixed
- `truthy_test` evaluated values as arithmetic expressions, which could run command substitutions from parameter values; it now compares strings only (`true`, `1`, `yes`, any case).
- Non-numeric `HELMFILE_REPO_CACHE_TIMEOUT` no longer causes errors; it disables the cache.
- Helm 4: `KUBE_VERSION` and `KUBE_API_VERSIONS` were ignored, so charts rendered with Helm's default capabilities instead of the destination cluster's.
- `KUBE_VERSION` with vendor suffixes was corrupted (`1.29.0+k3s1` became `1.29.031`); it is now normalized, invalid values are ignored with a warning.
- `HELMFILE_HELMFILE_STRATEGY=INCLUDE` aborted `init` silently when any helmfile source existed (`((count++))` under `set -e`).

### Added
- `PLUGIN_APP_HOME` environment variable.
- bats test suite (`test/`) covering the `discover`, `parameters`, `init` and `generate` phases, environment handling and Kubernetes capabilities.
- Docker image smoke test (`test/docker-smoke.sh`).
- `Makefile` with `tools`, `lint`, `test` and `test-docker` targets.
- GitHub Actions workflow `Test` running shellcheck, bats and the Docker smoke test on pushes to `main` and pull requests.

## [1.3.1] - 2026-05-27
### Fixed
- Fixed plugin installation and compatibility issues for Helm v4, ensuring proper support for CLI plugins including helm-secrets as described in the updated installation guide: https://github.com/jkroepke/helm-secrets/wiki/Installation
Expand Down
93 changes: 93 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Local developer entrypoints. CI calls the same targets.

SHELL := /bin/bash

TOOLS_DIR := $(CURDIR)/.tools
BATS_DIR := $(TOOLS_DIR)/bats
DOCKERFILE := docker/Dockerfile
IMAGE ?= argocd-helmfile-plugin:test

# Tool versions under test are taken from the Dockerfile so tests always
# run against what the image ships. Override on the command line if needed.
HELM_VERSION ?= $(shell sed -n 's/^ARG HELM_VERSION="\(.*\)"/\1/p' $(DOCKERFILE))
HELMFILE_VERSION ?= $(shell sed -n 's/^ARG HELMFILE_VERSION="\(.*\)"/\1/p' $(DOCKERFILE))

SHELLCHECK_VERSION := v0.11.0
BATS_CORE_VERSION := v1.14.0
BATS_SUPPORT_VERSION := v0.3.0
BATS_ASSERT_VERSION := v2.2.4

GO_ARCH := $(shell uname -m | sed -e 's/x86_64/amd64/' -e 's/aarch64/arm64/')

# Each version lives in its own directory so switching versions never mixes binaries.
HELM_DIR := $(TOOLS_DIR)/helm/$(HELM_VERSION)
HELMFILE_DIR := $(TOOLS_DIR)/helmfile/$(HELMFILE_VERSION)
SHELLCHECK_DIR := $(TOOLS_DIR)/shellcheck/$(SHELLCHECK_VERSION)
SHELLCHECK := $(SHELLCHECK_DIR)/shellcheck

export PATH := $(HELM_DIR):$(HELMFILE_DIR):$(PATH)

.PHONY: help tools lint test test-docker clean versions

help:
@echo "make tools - download helm, helmfile and bats into $(TOOLS_DIR)"
@echo "make lint - run shellcheck (pinned $(SHELLCHECK_VERSION), downloaded if needed)"
@echo "make test - run bats tests (downloads tools if needed)"
@echo "make test-docker - build the image and run a smoke test inside it"
@echo "make clean - remove $(TOOLS_DIR)"

versions:
@echo "helm: $(HELM_VERSION)"
@echo "helmfile: $(HELMFILE_VERSION)"

$(HELM_DIR)/helm:
@mkdir -p $(HELM_DIR)
wget -qO- "https://get.helm.sh/helm-$(HELM_VERSION)-linux-$(GO_ARCH).tar.gz" \
| tar zx --strip-components=1 -C $(HELM_DIR) linux-$(GO_ARCH)/helm

$(HELMFILE_DIR)/helmfile:
@mkdir -p $(HELMFILE_DIR)
wget -qO- "https://github.com/helmfile/helmfile/releases/download/v$(HELMFILE_VERSION)/helmfile_$(HELMFILE_VERSION)_linux_$(GO_ARCH).tar.gz" \
| tar zx -C $(HELMFILE_DIR) helmfile

# shellcheck is pinned: newer versions know more about bats and report different warnings
$(SHELLCHECK):
@mkdir -p $(SHELLCHECK_DIR)
wget -qO- "https://github.com/koalaman/shellcheck/releases/download/$(SHELLCHECK_VERSION)/shellcheck-$(SHELLCHECK_VERSION).linux.$(shell uname -m).tar.xz" \
| tar xJ --strip-components=1 -C $(SHELLCHECK_DIR) shellcheck-$(SHELLCHECK_VERSION)/shellcheck

$(BATS_DIR)/.installed:
@mkdir -p $(BATS_DIR)
git clone -q --depth 1 --branch $(BATS_CORE_VERSION) https://github.com/bats-core/bats-core $(BATS_DIR)/bats-core
git clone -q --depth 1 --branch $(BATS_SUPPORT_VERSION) https://github.com/bats-core/bats-support $(BATS_DIR)/bats-support
git clone -q --depth 1 --branch $(BATS_ASSERT_VERSION) https://github.com/bats-core/bats-assert $(BATS_DIR)/bats-assert
@touch $@

tools: $(HELM_DIR)/helm $(HELMFILE_DIR)/helmfile $(BATS_DIR)/.installed

# For .bats files:
# SC2030/SC2031 each @test runs in a subshell by design
# SC2154 $$output, $$stderr, $$status are set by bats "run"
# SC2016 single-quoted $${VAR} is intentional (tests variable expansion)
lint: $(SHELLCHECK)
$(SHELLCHECK) --version | sed -n 2p
$(SHELLCHECK) src/*.sh
$(SHELLCHECK) test/*.bash test/*.sh
$(SHELLCHECK) -s bash -e SC2030,SC2031,SC2016,SC2154 test/*.bats
@# Removed code must not come back: Helm 2 support and legacy constructs.
@if grep -nE 'init --client-only|HELMFILE_HELM3|helm_major_version\} -eq 2' src/*.sh; then \
echo "Helm 2 code found in src/"; exit 1; fi
@if grep -nE '\[\[ true \]\]|print_env_vars|\$$\(which |-eq 1 \]\]' src/*.sh; then \
echo "legacy construct found in src/"; exit 1; fi

test: tools
@helm version --short
@helmfile --version
BATS_LIB_PATH=$(BATS_DIR) $(BATS_DIR)/bats-core/bin/bats --print-output-on-failure test/

test-docker:
docker build -f $(DOCKERFILE) -t $(IMAGE) .
docker run --rm -v $(CURDIR)/test:/test:ro --entrypoint /bin/bash $(IMAGE) /test/docker-smoke.sh

clean:
rm -rf $(TOOLS_DIR)
60 changes: 59 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,35 @@ Consider these implications for your environment and act appropriately.
- https://github.com/helmfile/helmfile/pull/1 (can disable `exec` using env vars)
- the execution pod/context is the `argocd-repo-server`

# Requirements

- `helm` >= 3.6 (Helm 4 recommended; Helm 2 is not supported)
- `helmfile` >= 1, and >= 1.2 when used with Helm 4

The plugin checks both versions in the `init` and `generate` phases and fails
with a clear message if they are not supported.

## Kubernetes capabilities

Argo CD passes the destination cluster's version and APIs as `KUBE_VERSION` and
`KUBE_API_VERSIONS`. The plugin passes them to `helm template`, so charts can use
`.Capabilities.KubeVersion` and `.Capabilities.APIVersions.Has`:

- `KUBE_VERSION` is normalized first: a leading `v` and anything after the first
`+` or `-` are removed (`v1.29.0+k3s1` → `1.29.0`, `1.29.0-eks-5e0fdde` → `1.29.0`).
Values that are still not `<major>.<minor>[.<patch>]` are ignored with a warning.
- `KUBE_API_VERSIONS` is passed as `--api-versions`.

## Helm 4 notes

- Post-renderers are Helm plugins in Helm 4. `--post-renderer` in
`HELM_TEMPLATE_OPTIONS` or `postRenderer:` in helmfile must name an installed
plugin, not an executable path.
- `helm registry login` takes a domain name only (no path). Check
`HELMFILE_INIT_SCRIPT_FILE` scripts that log in to OCI registries.
- Plugins installed via `HELMFILE_INIT_SCRIPT_FILE` need `--verify=false` unless
they are signed and their key is available.

# Installation

- https://argo-cd.readthedocs.io/en/stable/operator-manual/config-management-plugins/
Expand Down Expand Up @@ -101,13 +130,21 @@ optional):
`HELMFILE_HELMFILE` should the same release name be declared in multiple
files
- `HELMFILE_CACHE_CLEANUP` - run helmfile cache cleanup on init
- `PLUGIN_APP_HOME` - per-application directory used as `HOME` while running
`helm`/`helmfile`, so applications do not share repositories, registry
logins or caches. Defaults to `/tmp/__argocd-helmfile-plugin.sh__/apps/${ARGOCD_APP_NAME}`
- `HELM_HOME` - **deprecated** alias for `PLUGIN_APP_HOME` (Helm itself ignores
it since v3). Still accepted with a warning; `PLUGIN_APP_HOME` wins if both are set

Of the above `ENV` variables, the following do variable expansion on the value:

- `HELMFILE_GLOBAL_OPTIONS`
- `HELMFILE_TEMPLATE_OPTIONS`
- `HELM_TEMPLATE_OPTIONS`
- `HELMFILE_INIT_SCRIPT_FILE`
- `PLUGIN_APP_HOME` (and deprecated `HELM_HOME`)
- `HELM_CACHE_HOME`
- `HELM_CONFIG_HOME`
- `HELM_DATA_HOME`

Meaning, you can do things like:
Expand Down Expand Up @@ -147,7 +184,7 @@ prevents the plugin(s) from being downloaded over and over each run.
- mountPath: /helm/data
name: helm-data-home

[[ ! -d "${HELM_DATA_HOME}/plugins/helm-secrets" ]] && /custom-tools/helm-v3 plugin install https://github.com/jkroepke/helm-secrets --version ${HELM_SECRETS_VERSION}
[[ ! -d "${HELM_DATA_HOME}/plugins/helm-secrets" ]] && /custom-tools/helm plugin install https://github.com/jkroepke/helm-secrets --version ${HELM_SECRETS_VERSION} --verify=false
chown -R 999:999 "${HELM_DATA_HOME}"

# lastly, in your app definition
Expand All @@ -171,6 +208,27 @@ etc. The value can be a relative or absolute path and the file itself can be
injected using an `initContainers` or stored in the application git repository.

## Development

### Tests

Tests use [bats-core](https://github.com/bats-core/bats-core) and run the plugin
against the real `helm` and `helmfile` binaries, using the versions pinned in
`docker/Dockerfile`. No cluster or network access to chart repositories is needed.

```bash
make test # downloads helm, helmfile and bats into .tools/, then runs test/*.bats
make lint # shellcheck
make test-docker # builds the image and runs test/docker-smoke.sh inside it
```

Requirements: `bash`, `git`, `wget`, `xz`, `jq`, `make` (and `docker` for `test-docker`).
`make` downloads pinned `shellcheck`, `helm`, `helmfile` and bats into `.tools/`.
Override tool versions with e.g. `make test HELM_VERSION=v3.19.4`.

Tests for known bugs are marked with `skip "known bug: ..."`. Remove the skip
together with the fix.

### Contributing
```declarative
# Create fork.
# Add the original repository as a new remote called "upstream" (only once, if not done before)
Expand Down
6 changes: 5 additions & 1 deletion docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ RUN groupadd -g $ARGOCD_USER_ID argocd && \
# Binary versions
# https://github.com/helm/helm/releases
# Supported Kubernetes Versions 1.35.x - 1.32.x (https://helm.sh/docs/topics/version_skew/)
# Plugin requires helm >= 3.6; with helm 4, helmfile >= 1.2
ARG HELM_VERSION="v4.2.3"
# https://github.com/helmfile/helmfile/releases
ARG HELMFILE_VERSION="1.5.2"
Expand Down Expand Up @@ -94,7 +95,10 @@ ARG HELM_GIT_VERSION="1.5.2"
# https://github.com/jkroepke/helm-secrets/releases
ARG HELM_SECRETS_VERSION="4.7.6"

# TODO... HELM 4+ WARNING: Skipping plugin signature verification. Use --verify=false to skip verification
# Helm 4 verifies plugin signatures by default:
# - helm-diff and helm-git are installed from git, which cannot be verified
# - helm-secrets tarballs are signed, but need the publisher key in a gpg keyring
# so verification is skipped explicitly; versions are pinned above.
RUN \
helm plugin install https://github.com/databus23/helm-diff --version ${HELM_DIFF_VERSION} --verify=false && \
helm plugin install https://github.com/aslafy-z/helm-git --version ${HELM_GIT_VERSION} --verify=false && \
Expand Down
Loading