Skip to content

Commit 3157fb1

Browse files
feat(helm): support wildcard glob patterns for valueFiles (cherry-pick #26768 for 3.4) (#26919)
Signed-off-by: nitishfy <justnitish06@gmail.com> Co-authored-by: Nitish Kumar <justnitish06@gmail.com>
1 parent e70034a commit 3157fb1

File tree

11 files changed

+1021
-1
lines changed

11 files changed

+1021
-1
lines changed

docs/user-guide/helm.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,241 @@ source:
9090
ignoreMissingValueFiles: true
9191
```
9292

93+
## Glob Patterns in Value Files
94+
95+
Glob patterns can be used in `valueFiles` entries to match multiple files at once. This is useful
96+
when the set of environment-specific override files is not known in advance, or when you want to
97+
pick up new files automatically without updating the Application spec.
98+
99+
```bash
100+
# Single quotes prevent the shell from expanding the glob before Argo CD receives it
101+
argocd app set helm-guestbook --values 'envs/*.yaml'
102+
```
103+
104+
In the declarative syntax:
105+
106+
```yaml
107+
source:
108+
helm:
109+
valueFiles:
110+
- envs/*.yaml
111+
```
112+
113+
### Supported pattern syntax
114+
115+
Glob expansion uses the [doublestar](https://github.com/bmatcuk/doublestar) library.
116+
117+
| Pattern | Description |
118+
|---------|-------------|
119+
| `*` | Matches any sequence of non-separator characters within a single directory level |
120+
| `?` | Matches any single non-separator character |
121+
| `[abc]` | Matches one of the characters listed inside the brackets |
122+
| `[a-z]` | Matches any character in the given range |
123+
| `**` | Matches any sequence of characters including `/` (recursive across directory levels) |
124+
125+
### How files are passed to Helm
126+
127+
Each matched file is passed to `helm template` as a separate `--values <path>` flag, in the same
128+
order they appear after expansion. This is identical to listing each file individually in
129+
`valueFiles`. Argo CD does the expansion before invoking Helm.
130+
131+
Matched files are expanded **in-place** within the `valueFiles` list and sorted in **lexical
132+
(alphabetical) order**. Because Helm gives higher precedence to later `--values` flags, lexical
133+
order determines which file wins when the same key appears in multiple files.
134+
135+
```
136+
envs/
137+
a.yaml # sets foo: a-value
138+
b.yaml # sets foo: b-value
139+
```
140+
141+
```yaml
142+
# envs/*.yaml expands to: envs/a.yaml, envs/b.yaml (lexical order)
143+
# b.yaml is last → foo = "b-value"
144+
source:
145+
helm:
146+
valueFiles:
147+
- envs/*.yaml
148+
```
149+
150+
When you have multiple entries in `valueFiles`, the relative order between entries is preserved.
151+
Glob expansion only reorders files within a single pattern:
152+
153+
```yaml
154+
valueFiles:
155+
- base.yaml # passed first
156+
- overrides/*.yaml # expanded in lexical order, passed after base.yaml
157+
- final.yaml # passed last, highest precedence
158+
```
159+
160+
### Recursive matching with `**`
161+
162+
Use `**` to match files at any depth below a directory:
163+
164+
```yaml
165+
# envs/**/*.yaml processes each directory's own files before descending into subdirectories,
166+
# with directories and files sorted alphabetically at each level.
167+
#
168+
# envs/a.yaml ← 'a' (flat file in envs/)
169+
# envs/z.yaml ← 'z' (flat file in envs/, processed before descending)
170+
# envs/nested/c.yaml ← inside envs/nested/, processed after envs/ flat files
171+
#
172+
# nested/c.yaml is last → foo = "nested-value"
173+
source:
174+
helm:
175+
valueFiles:
176+
- envs/**/*.yaml
177+
```
178+
179+
> [!NOTE]
180+
> `**` matches zero or more path segments, so `envs/**/*.yaml` also matches files directly
181+
> inside `envs/` (not just subdirectories). doublestar traverses directories in lexical order
182+
> and processes each directory's own files (alphabetically) before descending into its
183+
> subdirectories. This means `envs/z.yaml` always comes before `envs/nested/c.yaml`, even
184+
> though `'n' < 'z'` alphabetically. To make ordering fully explicit and predictable,
185+
> use numeric prefixes (see [Naming conventions](#naming-conventions)).
186+
187+
### Using environment variables in glob patterns
188+
189+
[Build environment variables](./build-environment.md) are substituted **before** the glob is
190+
evaluated, so you can construct patterns dynamically:
191+
192+
```yaml
193+
source:
194+
helm:
195+
valueFiles:
196+
- envs/$ARGOCD_APP_NAME/*.yaml
197+
```
198+
199+
This lets a single Application template expand to the right set of files per app name.
200+
201+
### Glob patterns with multiple sources
202+
203+
Glob patterns work with [value files from an external repository](./multiple_sources.md#helm-value-files-from-external-git-repository).
204+
The `$ref` variable is resolved first to the external repo's root, and the rest of the pattern is
205+
evaluated within that repo's directory tree:
206+
207+
```yaml
208+
sources:
209+
- repoURL: https://git.example.com/my-configs.git
210+
ref: configs
211+
- repoURL: https://git.example.com/my-chart.git
212+
path: chart
213+
helm:
214+
valueFiles:
215+
- $configs/envs/*.yaml # matches files in the 'my-configs' repo under envs/
216+
```
217+
218+
### Naming conventions
219+
220+
Because files are sorted lexically, the sort order controls merge precedence. A common pattern is
221+
to use a numeric prefix to make the intended order explicit:
222+
223+
```
224+
values/
225+
00-defaults.yaml
226+
10-region.yaml
227+
20-env.yaml
228+
30-override.yaml
229+
```
230+
231+
```yaml
232+
valueFiles:
233+
- values/*.yaml
234+
# expands to: 00-defaults.yaml, 10-region.yaml, 20-env.yaml, 30-override.yaml
235+
# 30-override.yaml has the highest precedence
236+
```
237+
238+
Without a prefix, pure alphabetical ordering applies. Be careful with names that sort
239+
unexpectedly, for example `values-10.yaml` sorts before `values-9.yaml` because `"1"` < `"9"`
240+
lexically.
241+
242+
### Constraints and limitations
243+
244+
**Path boundary**: Glob patterns cannot match files outside the repository root, even with
245+
patterns like `../../secrets/*.yaml`. Argo CD resolves the pattern's base path against the
246+
repository root before expanding it, and any match that would escape the root is rejected.
247+
248+
**Symlinks**: Argo CD follows symlinks when checking the path boundary. A symlink that lives
249+
inside the repository but points to a target outside the repository root is rejected, even though
250+
the symlink's own path is within the repo. This check applies to every file produced by glob
251+
expansion, including multi-hop symlink chains. Symlinks that resolve to a target still inside the
252+
repository are allowed.
253+
254+
**Absolute paths**: A path starting with `/` is treated as relative to the **repository root**,
255+
not the filesystem root. The pattern `/configs/*.yaml` matches files in the `configs/` directory
256+
at the top of the repository.
257+
258+
**Remote URLs are not glob-expanded**: Entries that are remote URLs (e.g.
259+
`https://raw.githubusercontent.com/.../values.yaml`) are passed to Helm as-is. Glob characters
260+
in a URL have no special meaning and will cause the URL to fail if the literal characters are not
261+
part of the URL.
262+
263+
**Shell quoting on the CLI**: Shells expand glob patterns before passing arguments to programs.
264+
Always quote patterns to prevent unintended shell expansion:
265+
266+
```bash
267+
# Correct: single quotes pass the literal pattern to Argo CD
268+
argocd app set myapp --values 'envs/*.yaml'
269+
270+
# Incorrect: the shell expands *.yaml against the current directory first
271+
argocd app set myapp --values envs/*.yaml
272+
```
273+
274+
### Deduplication
275+
276+
Each file is included only once, but **explicit entries take priority over glob matches** when
277+
determining position. If a file appears both in a glob pattern and as an explicit entry, the glob
278+
skips it and the explicit entry places it at its declared position.
279+
280+
```yaml
281+
valueFiles:
282+
- envs/*.yaml # expands to base.yaml, prod.yaml — but prod.yaml is listed explicitly below,
283+
# so the glob skips it: only base.yaml is added here
284+
- envs/prod.yaml # placed here at the end, giving it highest Helm precedence
285+
```
286+
287+
This means you can use a glob to pick up all files in a directory and then pin a specific file to
288+
the end (highest precedence) by listing it explicitly after the glob.
289+
290+
If the same file (same absolute path) is matched by two glob patterns, it is included at the
291+
position of the first match. Subsequent glob matches for that exact path are silently dropped.
292+
Files with the same name but at different paths are treated as distinct files and are always included.
293+
294+
```yaml
295+
valueFiles:
296+
- envs/*.yaml # matches envs/base.yaml, envs/prod.yaml
297+
- envs/**/*.yaml # envs/prod.yaml already matched above and is skipped;
298+
# envs/nested/prod.yaml is a different path and is still included
299+
```
300+
301+
### No-match behavior
302+
303+
If a glob pattern matches no files, Argo CD saves the Application spec (the spec is not invalid and
304+
the files may be added to the repository later) and surfaces a `ComparisonError` condition on the
305+
Application:
306+
307+
```
308+
values file glob "nonexistent/*.yaml" matched no files
309+
```
310+
311+
The app will remain in a degraded state until the pattern matches at least one file or the pattern
312+
is removed. No spec update is required once the files are added to the repository.
313+
314+
To silently skip a pattern that matches no files instead of raising an error, combine the glob with
315+
`ignoreMissingValueFiles`:
316+
317+
```yaml
318+
source:
319+
helm:
320+
valueFiles:
321+
- envs/*.yaml
322+
ignoreMissingValueFiles: true
323+
```
324+
325+
This is useful for implementing a default/override pattern where override files may not exist in
326+
every environment.
327+
93328
## Values
94329

95330
Argo CD supports the equivalent of a values file directly in the Application manifest using the `source.helm.valuesObject` key.

0 commit comments

Comments
 (0)