README
¶
helm-dryer

An ArgoCD Config Management Plugin to compose value injection for Helm charts, by keeping the values files really DRY.
Installation
Binary
Download the latest release from GitHub Releases. Pre-built binaries are available for Linux and macOS (amd64/arm64):
# Download and extract (replace OS and ARCH as needed)
curl -sL https://github.com/lansweeper-oss/helm-dryer/releases/latest/download/dryer-linux-amd64.tar.gz | tar xz
sudo mv dryer /usr/local/bin/
Go install
go install github.com/lansweeper-oss/helm-dryer@latest
OCI image (for ArgoCD)
The plugin is published as an OCI image for use as an ArgoCD CMP sidecar (see ArgoCD integration):
ghcr.io/lansweeper-oss/helm-dryer/helm-dryer:<version>
Rationale
helm-dryer is an ArgoCD Config Management Plugin designed to help manage Helm charts
more efficiently by keeping the values files as simple and reusable as possible.
It helps in composing value injections for Helm charts, which means it can dynamically inject
values into Helm charts during the deployment process.
DRY Principle: By keeping the values files DRY, it avoids redundancy and repetition in the values files. This makes the configuration more maintainable and easier to manage.
For example, a values file like the following one cannot honor YAML anchors to fully reduce the
verbosity:
configuration:
clusterName: my-cluster
storage:
- name: s3
bucket: eu-west-1-foo-123456789
config:
kmsKeyId: alias/eu-west-1-foo-bar
region: eu-west-1
region: eu-west-1
accountId: 123456789
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789:role/eu-west-1-foo
name: my-cluster-sa
While this would be possible instead, given we inject the values referenced below as inline
arguments (similarly to helm --set key-value pairs):
{{- $storageName := "s3" }} # we can also define variables :-)
configuration:
clusterName: {{ .Values.clusterName }}
storage:
- name: {{ $storageName }}
bucket: {{ printf "%s-%s-%s" .Values.region .Values.domain .Values.accountId }}
config:
kmsKeyId: {{ printf "alias/%s-%s-%s" .Values.region .Values.domain .Values.key }}
region: {{ .Values.region }}
region: {{ .Values.region }}
accountId: {{ atoi .Values.accountId }}
serviceAccount:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::{{ .Values.accountId }}:role/{{ .Values.region }}-{{ .Values.domain }}
name: {{ .Values.clusterName }}-sa
High-level Implementation
The following diagram describes the behavior of the plugin, which first renders the values and finally templates the Helm chart with the rehydrated values for ArgoCD to deploy in the target Kubernetes cluster.
graph LR
A[ArgoCD Application] --> |values files| B[Dryer CMP]
A --> |values object| B
A --> |Helm path| B
B --> |template| D[Rendered Values]
B --> |Update dependencies| H[Helm]
H --> |Chart values| B
D -.-> |2nd pass| B
B --> |template| F[Helm Chart]
F --> |deploys| G[Kubernetes Cluster]
Under the hood, the plugin is fed from (and merged in that order, with later taking precedence):
- Helm dependencies (if any) merged one after the other (in an umbrella chart-like tree).
- For a dependency named
foo, that means the values will hang from a parent keyfoo.
- For a dependency named
- ArgoCD values files (read one by one).
- Initial values (
--initial-values, merged left to right, last file wins). - ArgoCD values object (passed as key-value pairs via
--set/-v).
graph RL
B[values Files] -->|overrides| A[Chart dependency values]
D[Initial Values] -->|overrides| B
C[values Object] -->|overrides| D
Usage
Create an ArgoCD application which uses this plugin like:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: test
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/lansweeper-oss/helm-dryer
targetRevision: HEAD
path: tests
plugin:
name: dryer-v1.0.0
parameters:
- name: settings
map:
ignoreMissing: "true"
twoPass: "true"
- name: valueFiles
array:
- values.yaml
- values.tpl.yaml
- values.stg.yaml
- values.missing.file.yaml
- name: valuesObject
map:
clusterName: eks-cluster-platform
partition: aws
accountId: "234796234"
namePrefixWithoutDomain: eks-cluster
foo: bar
destination:
server: https://kubernetes.default.svc
namespace: test
Besides that, for CI or troubleshooting purposes, this plugin can be run manually as a regular CLI that accepts these commands:
get: used to get the merged+templated values.template: used to template a given Helm chart with merged values.render: a wrapper oftemplatewhich assumes it's being called from ArgoCD (some parameters are injected as environment variables, see below).render-app: like template, but fed from an ArgoCD Application spec in YAML or JSON format. This command is meant to be used mainly for CI checks.
In order to check the usage of the tool, do:
go run . --help
Usage: helm-dryer <command> [flags]
An ArgoCD CMP to pre-template values files.
Flags:
-h, --help Show context-sensitive help.
-a, --api-versions=,... API versions (capabilities)
($KUBE_API_VERSIONS).
-f, --files=FILES Values files, relative to Path (or to
--repo-root if absolute).
--initial-values=INITIAL-VALUES
YAML files merged into the values object.
Paths follow valueFiles resolution.
-k, --kube-version="" Kubernetes version ($KUBE_VERSION).
-r, --release-name=STRING Release name ($ARGOCD_APP_NAME).
-n, --release-namespace=STRING
Release namespace ($ARGOCD_APP_NAMESPACE).
-v, --set=KEY=VALUE,... Injected key value pairs.
--credentials.file=STRING Path to OCI registry credentials file.
--credentials.namespace=STRING
Kubernetes namespace for ArgoCD secrets
($ARGOCD_NAMESPACE).
--credentials.password=STRING
OCI registry password ($OCI_PASSWORD).
--credentials.registry="ghcr.io"
OCI registry URL ($OCI_REGISTRY).
--credentials.secret Read repository credentials from ArgoCD
Kubernetes secrets.
--credentials.username=STRING
OCI registry username ($OCI_USERNAME).
-L, --delim-left="{{" Template left delimiter.
-R, --delim-right="}}" Template right delimiter.
-I, --ignore-empty Ignore empty/null values in templated value
files.
-m, --ignore-main-values When present, ignore the implicit load of
main values.yaml file.
-i, --ignore-missing Ignore missing values files.
--logging.debug Emit debug logs in addition to info logs.
--logging.format="json" Log format (json|console).
--on-the-fly Experimental. Merge resolved values on the
fly across files.
-o, --out="" Output file (default: stdout).
-p, --path="." Relative path to the chart.
--repo-root=STRING Repository root, base for absolute values
files.
--skip-crds Skip CRDs in the templated output.
--skip-schema-validation Disable JSON schema validation.
--skip-tests Skip tests from templated output.
--strip-null-values Strip null values before rendering. Restores
Helm v3 behavior.
-T, --timeout="90s" Operation timeout (e.g. 30s, 2m)
($DRYER_TIMEOUT).
-t, --ttl=STRING Time-to-live in time.Duration format
($CACHE_TIMEOUT).
-2, --two-pass Experimental. Perform a two-pass render.
-u, --update-dependencies Always update dependencies.
Commands:
get [flags]
Get the rendered values.
render [flags]
Render the template as a Configuration Management plugin.
render-app [flags]
Render from an ArgoCD Application file.
template [flags]
Render the template.
version [flags]
Show version and quit.
Run "helm-dryer <command> --help" for more information on a command.
For the full CLI reference of each command, see docs/cli.md.
Values files paths
Values files paths are resolved as ArgoCD does for spec.source.helm.valueFiles:
- A relative path is resolved from the chart path, that is, the
-pvalue or the Application folder when running as a CMP plugin. - An absolute path (starting with
/) is resolved from the root of the repository, and it may not escape it.
The repository root is obtained differently depending on the command:
renderderives it from the chart path andARGOCD_APP_SOURCE_PATH, both provided by ArgoCD.render-appuses the-pvalue, since the Applicationspec.source.pathis relative to it.getandtemplateuse the--repo-rootvalue, which defaults to the chart path.
For example, given a repository like:
env/
'--- prod.yaml
charts/
'--- app/
|--- Chart.yaml
'--- values.yaml
an Application with spec.source.path: charts/app can consume values from the root of the
repository:
- name: valueFiles
array:
- values.yaml # charts/app/values.yaml
- /env/prod.yaml # env/prod.yaml
and the very same set of values can be reproduced locally with:
go run . get -f values.yaml -f /env/prod.yaml -p charts/app --repo-root .
When the repository root cannot be determined, a warning is logged and the chart path is used instead, which makes absolute paths behave as relative ones.
A values file resolving outside the repository root is a configuration error, so it is reported even when the
ignoreMissingflag is enabled.
Render values
An example of (pre)rendering a set of values files follows:
go run . get -f tests/values.tpl.yaml -f tests/values.stg.tpl.yaml --set clusterName=eks-cluster-platform,partition=aws,accountId=234796234 --set namePrefixWithoutDomain=eks-cluster
Alternatively, the values object can be loaded from YAML files with --initial-values.
Later files override earlier ones, and --set always wins:
go run . get -f tests/values.tpl.yaml --initial-values common.yaml --initial-values env/staging.yaml --set domain=override
Please note that out of the box, go template and Sprig are supported as in a regular Helm template. Additionally,
fromYamlandtoYamlfunctions are available.At the moment, you can reuse a value already defined if the two-pass experimental feature is enabled, e.g. the following is supported:
foo: bar
url: example.com/{{ .Values.foo }} # would produce example.com/bar
Template a Helm chart
The template command additionally supports -A (application spec) and -H (disable hooks) flags.
For example:
go run . template -f tests/values.yaml -f tests/values.tpl.yaml -f tests/values.stg.tpl.yaml \
--set clusterName=eks-cluster-platform,partition=aws,accountId=234796234,namePrefixWithoutDomain=eks-cluster \
-p tests -r test -du
Render a Helm template
This is essentially a wrapper on template, but assumes some information is passed as environment variables.
The following keys are expected under ARGOCD_APP_PARAMETERS:
valueFiles, a list of files containing values. If a file is missing, it will produce an error if theignoreMissingflag is not enabled. No assumptions are made, and an explicit entry forvalues.yamlmay be required when using raw values. Entries are resolved from the Application folder, or from the root of the repository when the path is absolute (see Values files paths).initialValues, an optional list of YAML files merged into the values object. Later files override earlier ones, andvaluesObjectentries always win.valuesObject, an optional map of input values.ignoreEmpty[optional:false] a flag to ignore empty/null values in templated value files.stripNullValues[optional:false] strip null values (key: ~) from chart values before rendering. Restores Helm v3 behavior where explicit null entries are removed.ignoreMissing[optional:false] a flag indicating if missing values files are ignored.skipCRDs[optional:false] a flag to skip installation of CRDs by the Helm chart.skipSchemaValidation[optional:false] a flag to skip JSON schema validation.skipTests[optional:false] a flag to skip Helm test resources.onTheFly[optional:false] Experimental, merge resolved values across files on the fly (see below).twoPass[optional:false] Experimental, allow template values files over themselves.
For example:
apiVersion: argoproj.io/v1alpha1
kind: Application
spec:
source:
plugin:
name: dryer-v1.0.0 # name-version if we specified the version when setting up the CMP (see below)
parameters:
- name: valueFiles
array:
- values.tpl.yaml
- values.stg.yaml
- name: initialValues
array:
- common-values.yaml
- name: valuesObject
map:
image.tag: v1.2.3
foo: bar
replicas: "2"
- name: settings
map:
twoPass: "true"
[...]
Render will assume
--update-deps
Render from an ArgoCD Application spec
This command is meant to be used in CI, to validate what the plugin will produce for a given ArgoCD Application, directly from its spec. The file can be either in YAML or JSON format.
This command assumes the input comes from
-prather than parameters like-for-v.
See the Application spec example from render for a reference about the expected fields.
Environment variables
Besides the standard build environment variables, the plugin receives the ARGOCD_APP_PARAMETERS
variable, which is used to inject plugin-specific parameters.
Plugin settings map
This plugin accepts optional settings to control its behavior. The following settings are currently supported:
disableHooks- Do not render Helm hooks.ignoreEmpty- Do not raise an error when a specific value is empty / null in templated value files.stripNullValues- Strip null values (key: ~) from chart values before rendering, restoring Helm v3 behavior.ignoreMissing- Do not raise an error when a values file is missing.skipCRDs- Skip the CRDs if embedded in the Helm chart.skipSchemaValidation- Skip JSON schema validation.skipTests- Skip Helm test resources.releaseName- Override the release name, otherwise environment variableARGOCD_APP_NAMEor Application'smetadata.name(in that order of precedence) is used.releaseNamespace- Override the release namespace, otherwise environment variableARGOCD_APP_NAMESPACEor Application'sspec.destination.namespace(in that order of precedence) is used.ttl- Per-app control of the chart dependency archives TTL (Gotime.Durationformat, e.g."5m","1h").onTheFly- Experimental (see below) feature to merge resolved values across files on the fly.twoPass- Experimental (see below) feature to do a 2-pass render of the values.
These settings can be customized per-application and override the global (CLI argument) ones.
Known limitations and caveats
Null values in Helm v4
Helm v4 changed how explicit null values (key: ~) are handled in the values map.
In Helm v3, these entries were effectively ignored during template rendering.
Functions like dig would return the default value when encountering nil.
In Helm v4, nil values are preserved and passed through to template functions, which can cause errors like:
invalid value; expected string
This happens when a template function (e.g. regexMatch, printf) receives a nil value instead
of the expected type.
To restore the Helm v3 behavior, enable the --strip-null-values flag (or stripNullValues in
the ArgoCD plugin settings). This recursively removes all nil entries from the chart values before
passing them to Helm's template engine.
# ArgoCD Application example
parameters:
- name: settings
map:
stripNullValues: "true"
Explicit values.yaml declaration
Unlike Helm, which automatically loads values.yaml when present, this plugin requires explicit
inclusion of all value files in the configuration.
As per design, the values.yaml file must be explicitly listed in the valueFiles parameter to be
included in the merged values.
This behavior is by default not followed when templating the Helm chart, since this plugin leverages the Helm go SDK. For example, for the following example when specifying the files:
-f foo.tpl.yaml -f bar.tpl.yaml
in a folder like:
app/
|--- values.yaml
|--- foo.tpl.yaml
'--- bar.tpl.yaml
it will merge the values from the last 2 files (explicit), but still render the chart by reading
values.yaml (implicit).
This behavior can be switched off by setting the IgnoreMainValues flag.
Values file extension
This plugin leverages the Helm SDK to resolve dependencies and render charts with merged values.
Since Helm automatically loads values.yaml and parses it as YAML, we cannot apply templating to
this specific file.
To work around this limitation, use a different filename extension when creating templated files,
such as values.tpl.yaml.
This restriction only applies to the default
values.yamlfile. The plugin processes files based on their content, not their extension, so a file namedvalues.euw1.yamlwill undergo the same templating process as a file namedvalues.euw1.tpl.yaml.
Best practice: Use .tpl.yaml extension for templated value files and .yaml for raw/static value files.
Available functions and objects
Helm template specific functions are not supported. For instance, Kubernetes and Chart functions cannot be used (in the values files) as in a regular Helm chart.
Builtin objects support is limited to .KubeVersion, .Release.Name, and .Release.Namespace
keys.
This limitation only affects the values files, not the Helm templates which are unaffected. This plugin relies on the Helm SDK under the hood to template a Chart.
Upstream Implementation restrictions
One of the most important limitations is a consequence of the whole CMP implementation. The type of keys we inject as input parameters for a CMP can only be of types:
map[string]string(e.g.settings,valuesObject)[]string(e.g.valueFiles)string(not used in this plugin)
That is, for the sake of simplicity we'll only support arrays and maps of strings and it should be up to the templating code inside the values files to do type conversions accordingly, e.g. if the upstream chart expects a boolean we should cast it as such. This does not affect how the rest of the values (not templated ones) are treated by Helm.
Caching
This plugin makes no assumptions about the maximum size of the cache. As with any other CMP, it
mounts an emptyDir volume in /tmp, which at the end is stored in the ephemeral storage of the
node it runs on. An argocd-repo-server pod running for too long may exhaust the ephemeral storage,
so we should take that into consideration when requesting/limiting the storage for the plugin container.
For example:
resources:
requests:
ephemeral-storage: "10Gi"
limits:
ephemeral-storage: "10Gi"
When updateDependencies is unset or false, we will cache not only the indices but the Helm chart
packages (a series of .tgz files) in the helm cache directory (which may be set with the environment variable
HELM_CACHE_HOME and defaults to /tmp/helm-cache).
As a first implementation, files expire after a customizable TTL (CACHE_TIMEOUT environment
variable) is passed.
When TTL is not set (or set to zero), this means that the cache is ignored and chart archives are
always considered stale.
Read-only filesystem
When running dryer container with a readOnlyRootFilesystem: true security context, we need to mount:
- a volume for
/tmp. - [optional] a volume for
HELM_CACHE_HOME(if not inside/tmp), where the cached files will be stored. - another volume for
XDG_CONFIG_HOME(typically~/.config) where Helm credentials will live.
[...]
env:
- name: HELM_CACHE_HOME
value: /helm-working-dir
volumeMounts:
- mountPath: /helm-working-dir
name: helm-working-dir
- mountPath: /.config
name: helm-config
- mountPath: /tmp
name: cmp-tmp
volumes:
- emptyDir: {}
name: cmp-tmp
- emptyDir: {}
name: helm-config
- emptyDir: {}
name: helm-working-dir
[...]
On-the-fly values merging
Why
In the default rendering mode, every values file is templated against the same .Values context
(chart dependencies + valuesObject).
If one file defines a value that another file references via .Values, the reference will be empty.
The files are independent of each other during templating.
On-the-fly mode solves this by feeding resolved values from one file into the next, so cross-file
.Values references work in a single pass.
How it works
When onTheFly is enabled, the plugin processes values files in reverse order and builds an
accumulator:
- The accumulator starts with the initial values (chart dependencies +
valuesObject). - The last file (highest priority) is templated first using the accumulator as
.Values. - Its resolved values (non-nil, non-empty) are merged into the accumulator, but existing accumulator entries always win.
- The next file is templated using the enriched accumulator, and so on.
- After all files are processed, the results are merged in the original file order so that later files still override earlier ones.
graph LR
A[initialValues] --> B["File C (last)"]
B --> |resolved values| C[accumulator]
C --> D["File B"]
D --> |resolved values| E[accumulator]
E --> F["File A (first)"]
F --> G[Final merge in original order]
Priority chain: initialValues > last file > ... > first file, matching standard Helm semantics.
Example
values.base.yaml - plain YAML, no templates:
environment: staging
region: eu-west-1
values.app.tpl.yaml - references base values:
app:
endpoint: https://api.{{ .Values.region }}.example.com
env: {{ .Values.environment }}
With file order [values.app.tpl.yaml, values.base.yaml] and onTheFly: "true", the base file is
processed first (reverse order).
Its resolved region and environment values feed into the tpl file, producing the correct
endpoint without needing two-pass.
Caveats
This is an experimental feature. nil values and empty strings from unresolved template
expressions are stripped from the accumulator to prevent feeding placeholders into other files.
Intentional key: ~ deletions are preserved in the final merge output.
This stripping only affects the accumulator used for cross-file templating context. In other
words, the collection of values which Dryer will use to render the values.
The raw templated output of each file (including nil values) is preserved and participates in the
final merge. This means:
- A
nil(~) or empty-string value means "I don't set this key" during accumulator building, it cannot suppress a key defined in another file's templating context. - File ordering only affects the accumulator for keys where both files provide non-nil, non-empty values; for keys where one file provides a value and the other provides nil/empty, the concrete value always wins in the accumulator.
- Intentional
key: ~null-deletions still work in the final merge output, because the raw per-file data (with nils intact) feeds into the final merge, not the stripped accumulator.
When stripNullValues is enabled, it operates on the final merged result, not on the per-file
accumulator.
Two-pass rendering
Why
In a single-pass render, .Values only contains the injected valuesObject parameters. Go
template variables ($var) can avoid repetition within a single file, but they are scoped to that
template execution — they cannot be shared across files. When values that depend on each other
live in the same file, prefer using $var instead of enabling two-pass.
Two-pass is only needed when values are split across different files and one file needs to reference a computed value from another:
values.tpl.yaml
admins: [] # default
roles:
{{- range .Values.admins }}
- name: {{ . }}
role: admin
{{- end }}
values.stg.tpl.yaml (environment override)
admins:
- a-team
In single-pass, roles is always empty: when values.tpl.yaml renders, .Values.admins only
has valuesObject entries. The override from values.stg.tpl.yaml isn't merged into .Values
yet. With two-pass, Pass 1 merges all files so admins contains a-team, and Pass 2 renders the
range loop correctly.
How it works
When twoPass is enabled, the plugin renders the values files twice:
- Pass 1: Render values files using the injected
valuesObjectas.Valueswithmissingkey=default(missing references resolve to empty instead of erroring). This produces a partially or fully resolved set of values. - Pass 2: Merge the Pass 1 output back with the original
valuesObject, then render the values files again using this merged result as.Valueswithmissingkey=error.
graph LR
A[valuesObject] --> |".Values"| B["Pass 1 (missingkey=default)"]
B --> C[Resolved Values]
C --> |merge with valuesObject| D["Pass 2 (missingkey=error)"]
D --> E[Final Values]
Umbrella charts
For Umbrella charts, two-pass resolved values are available in a nested map keyed by dependency
name, e.g. index .Values "aws-load-balancer-controller" "foo".
Caveats
This is an experimental feature. Rendering twice may produce unexpected results if templates have side effects or if the output of Pass 1 introduces new template syntax that gets interpreted in Pass 2. Instead, define variables or duplicate the template whenever possible.
Logging
ArgoCD captures CMP stderr only when the plugin exits with a non-zero code (error).
On success, stderr is discarded by the CMP sidecar. This is ArgoCD's behavior, not Dryer's:
the CMP protocol streams stdout as the manifest output and throws away stderr on success.
For this reason, even when --debug is set nothing is shown in the container logs.
You'd only see them if an application fails to render, or if you use Dryer as a CLI.
Authentication
See docs/authentication.md for a detailed guide on configuring credentials for OCI registries and HTTP Helm repositories, including basic auth, TLS client certificates, Docker credentials files, and Kubernetes secret integration.
ArgoCD integration
As with any other Configuration Management Plugin, dryer runs as a sidecar container of
argocd-repo-server.
The easiest way to add it is by setting the following values in the ArgoCD Helm chart:
repoServer:
extraContainers:
- name: dryer-plugin
command:
- /var/run/argocd/argocd-cmp-server
args:
- --loglevel=info # print everything written by the CMP, even on successful command execution
image: ghcr.io/lansweeper-oss/helm-dryer/helm-dryer:v1.0.0
env:
- name: HELM_CACHE_HOME
value: /tmp/helm-cache
resources:
requests:
ephemeral-storage: "10Gi"
limits:
ephemeral-storage: "10Gi"
securityContext:
runAsNonRoot: true
runAsUser: 999
volumeMounts:
- mountPath: /var/run/argocd
name: var-files
- mountPath: /home/argocd/cmp-server/plugins
name: plugins
- mountPath: /home/argocd/cmp-server/config/plugin.yaml
subPath: dryer.yaml
name: plugin-config
- mountPath: /tmp
name: cmp-tmp
- mountPath: /.config
name: helm-config
volumes:
- configMap:
name: argocd-cmp-cm
name: plugin-config
- emptyDir: {}
name: cmp-tmp
- emptyDir: {}
name: helm-config
cmp:
create: true
plugins:
dryer:
version: v1.0.0 # Optional, must specify name-version if present
generate:
command:
- dryer
- render
args:
- --ignore-missing
discover: {}
parameters:
static:
- name: settings
collectionType: map
title: Plugin Settings
- name: initialValues
collectionType: array
title: Initial Values Files
- name: valueFiles
collectionType: array
title: Values Files
- name: valuesObject
collectionType: map
title: Injected Values
Build
export KO_DOCKER_REPO=...
ko build --base-import-paths [--tags ...]
Test
go test ./... -cover
Contributing
See CONTRIBUTING.md for development guidelines.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
internal
|
|
|
argo
Package argo represents the ArgoCD Application CRD.
|
Package argo represents the ArgoCD Application CRD. |
|
dryer
Package dryer provides functions to read and process templated Helm values files.
|
Package dryer provides functions to read and process templated Helm values files. |
|
helm
Package client provides functions to operate with the Helm SDK client.
|
Package client provides functions to operate with the Helm SDK client. |
|
repocreds
Package repocreds resolves ArgoCD repository credentials by URL matching.
|
Package repocreds resolves ArgoCD repository credentials by URL matching. |
|
utils
Package utils provides utility functions for the application.
|
Package utils provides utility functions for the application. |
|
values
Package values provides functions to operate with YAML values for Helm charts.
|
Package values provides functions to operate with YAML values for Helm charts. |