Skip to content

Commit 222bdf8

Browse files
authored
Merge pull request #426 from scottrigby/charts-v3-helmignore-parity-gitignore
hip-0027: Bring .helmignore to parity with .gitignore file targeting syntax
2 parents eba1acf + b303877 commit 222bdf8

2 files changed

Lines changed: 350 additions & 0 deletions

File tree

hips/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,4 @@ restricted markdown format and can be found in the
3636
- [hip-0024: Improve the Helm Documentation](hip-0024.md)
3737
- [hip-0025: Better Support for Resource Creation Sequencing](hip-0025.md)
3838
- [hip-0026: H4HIP: Wasm plugin system](hip-0026.md)
39+
- [hip-0027: Bring .helmignore to parity with .gitignore file targeting syntax](hip-0027.md)

hips/hip-0027.md

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,349 @@
1+
---
2+
hip: "0027"
3+
title: "Bring .helmignore to parity with .gitignore file targeting syntax"
4+
authors: ["Scott Rigby <scott@r6by.com>"]
5+
created: "2025-12-14"
6+
type: "feature"
7+
status: "draft"
8+
requires: ["HIP-0020"]
9+
---
10+
11+
## Abstract
12+
13+
This proposal brings `.helmignore` file targeting semantics to full parity with `.gitignore` syntax and matching rules for Helm Charts v3. The current `.helmignore` implementation diverges from `.gitignore` in critical ways—most notably in rule evaluation order (first-match vs. last-match), negation pattern behavior, and lack of `**` recursive glob support. These differences cause confusion and bugs for users who reasonably expect `.helmignore` to behave like `.gitignore`. By scoping this change to Charts v3 (per [HIP-0020][hip-0020]), existing charts continue to work unchanged while v3 charts opt into consistent, predictable ignore behavior.
14+
15+
## Motivation
16+
17+
Users familiar with `.gitignore` expect the same behavior from `.helmignore`. The current divergence causes:
18+
19+
1. **Broken negation patterns**: Users cannot use whitelist-style patterns (e.g., `/*` then `!Chart.yaml`) because the current implementation has inverted negation semantics ([#8688], [#3622], [#1776]).
20+
21+
2. **Missing recursive globs**: The `**` pattern is explicitly unsupported, forcing verbose workarounds for common cases like `**/test/` ([#12592]).
22+
23+
3. **Unexpected first-match behavior**: Git uses "last matching rule wins"; Helm stops at the first match. This breaks patterns that progressively refine what to exclude.
24+
25+
4. **Documentation gaps**: Current docs don't clearly explain limitations or differences from `.gitignore` ([#4638], [helm-www#1312]).
26+
27+
### Evidence of User Pain
28+
29+
A comprehensive search of helm/helm issues reveals **27 directly related issues** and **5 pull requests**. Key themes:
30+
31+
- **Pattern matching logic**: [#8688], [#3622], [#1776], [#12592]
32+
- **Scope/behavior confusion**: [#6075], [#3050], [#10764]
33+
- **Documentation issues**: [#4638], [helm-www#1171], [helm-www#1312], [helm-www#1460]
34+
35+
The recurring user expectation is clear: `.helmignore` should work like `.gitignore`.
36+
37+
## Rationale
38+
39+
### Why .gitignore Parity?
40+
41+
1. **Reduced cognitive load**: Developers already know `.gitignore` semantics. Aligning `.helmignore` enables existing knowledge and patterns to be reused with Helm.
42+
43+
2. **Well-documented spec**: Git's ignore format is [thoroughly documented][git-gitignore] and battle-tested across millions of repositories.
44+
45+
3. **Ecosystem consistency**: Tools like Docker (`.dockerignore`), npm (`.npmignore`), and FluxCD (`.sourceignore`) all follow `.gitignore` semantics.
46+
47+
### Why Charts v3?
48+
49+
Per [HIP-0020][hip-0020], Charts v3 provides a clean opt-in boundary for breaking changes:
50+
51+
1. **Breaking change isolation**: Charts explicitly declare `apiVersion: v3`, accepting new semantics. Charts v2 behavior is preserved unchanged.
52+
53+
2. **No forced migration**: Existing charts continue to work. Users migrate on their own schedule.
54+
55+
3. **Independent evolution**: Chart changes are decoupled from Helm version, allowing adequate time for testing and adoption.
56+
57+
This is the appropriate scope because changing `.helmignore` semantics would break charts that depend on current (albeit surprising) behavior.
58+
59+
## Specification
60+
61+
### Behavioral Parity with .gitignore
62+
63+
Charts v3 `.helmignore` files should support the full `.gitignore` pattern format as documented at [git-scm.com/docs/gitignore][git-gitignore].
64+
65+
#### Design Intent
66+
67+
`.helmignore` is intended to follow Git's `.gitignore` file pattern matching semantics as documented at the time of this HIP's acceptance. These semantics have been intentionally stable for many years, and familiarity with them is a primary goal of this proposal.
68+
69+
If Helm's `.helmignore` behavior diverges from Git's documented `.gitignore` behavior, that divergence should be treated as a bug and corrected—unless the divergence is explicitly documented in this proposal (see Scope Clarification).
70+
71+
**Future Git changes**: If Git were to introduce incompatible changes to `.gitignore` matching semantics in the future, Helm would evaluate and explicitly decide whether to adopt those changes. Helm does not implicitly inherit future Git behavior changes.
72+
73+
#### Recursive Glob Semantics (`**`)
74+
75+
- `**/foo` — matches `foo` in all directories
76+
- `foo/**` — matches everything inside `foo/`
77+
- `a/**/b` — matches `a/b`, `a/x/b`, `a/x/y/b`, etc.
78+
79+
#### Rule Evaluation Order
80+
81+
**Last matching rule wins**. All rules are evaluated; the final matching rule determines whether a path is included or excluded. This enables patterns like:
82+
83+
```
84+
# Exclude all top-level entries (files and directories)
85+
/*
86+
87+
# But include these
88+
!Chart.yaml
89+
!values.yaml
90+
!templates/
91+
```
92+
93+
#### Negation Behavior
94+
95+
- `!pattern` re-includes files that match, reversing a prior exclusion
96+
- **Limitation**: Cannot re-include a file if its parent directory was excluded (Git skips listing excluded directories for performance)
97+
98+
#### Scope Clarification
99+
100+
Helm has no concept of staging or committing files. This proposal addresses **file targeting syntax and semantics only**—specifically, which files are included/excluded during chart operations. Git's behavior around staged/committed files does not apply.
101+
102+
#### File Location
103+
104+
Charts v3 `.helmignore` is loaded from the chart root directory only. Unlike `.gitignore`, which supports recursive ignore files in subdirectories within a repository, Helm loads `.helmignore` only from the chart root. Patterns in the root `.helmignore` apply to the entire chart tree.
105+
106+
Recursive `.helmignore` loading within a chart (matching Git's nested `.gitignore` behavior) is a potential future enhancement but is out of scope for this HIP.
107+
108+
### Technical Requirements
109+
110+
1. **Parser/Matcher**: Implement a `.gitignore`-compatible lexer, parser, and matcher supporting all pattern types above.
111+
112+
2. **Last-match semantics**: Evaluate all rules; final matching rule determines outcome.
113+
114+
3. **Path normalization**: Normalize paths to forward slashes for cross-platform consistency.
115+
116+
4. **Directory short-circuit**: Once a directory is excluded, skip traversing its contents (matches Git's performance optimization).
117+
118+
### Integration Points
119+
120+
The new `.gitignore`-parity matching should apply in all existing chart file operations (unchanged scope):
121+
122+
- `helm package`
123+
- `helm lint`
124+
- `helm template`
125+
- `helm install` / `helm upgrade` (for local charts)
126+
- `.Files.Get` / `.Files.Glob` (file access in templates)
127+
128+
This list is representative; the matching logic applies wherever chart files are loaded or accessed.
129+
130+
### Gating
131+
132+
- **Charts v3 (`apiVersion: v3`)**: New `.gitignore`-parity semantics
133+
- **Charts v2 (`apiVersion: v2`)**: Existing behavior unchanged
134+
135+
## Backwards Compatibility
136+
137+
### Charts v2
138+
139+
No behavior change. Existing `.helmignore` files continue to work exactly as before, preserving compatibility for all current charts.
140+
141+
### Charts v3
142+
143+
Charts opting into v3 accept new `.helmignore` semantics. Potential breaking changes for charts migrating from v2:
144+
145+
| Current Behavior | New Behavior | Migration Impact |
146+
| ------------------------ | -------------------------- | ------------------------------------------------------------ |
147+
| First-match evaluation | Last-match evaluation | Patterns relying on early termination may behave differently |
148+
| Negation inverts match | Negation re-includes | Whitelist patterns will finally work correctly |
149+
| `**` causes error | `**` works | No breakage (feature addition) |
150+
| Trailing spaces stripped | Preserved with `\ ` | Unlikely to affect real charts |
151+
| No escape sequences | `\#`, `\!`, `\ ` supported | No breakage (feature addition) |
152+
153+
## Security Implications
154+
155+
None. This change only affects which local files are considered during chart operations. It does not:
156+
157+
- Introduce new attack surfaces
158+
- Add remote dependencies
159+
- Change network behavior
160+
- Affect chart signing or verification
161+
162+
## How to Teach This
163+
164+
### Documentation Updates
165+
166+
1. **Primary statement**: "Charts v3 `.helmignore` uses identical pattern rules to `.gitignore`."
167+
168+
2. **Link to Git documentation**: Reference [git-scm.com/docs/gitignore][git-gitignore] as background documentation for the pattern semantics that `.helmignore` follows.
169+
170+
3. **Migration guide**: Document behavior differences between v2 and v3.
171+
172+
4. **Examples**: Provide side-by-side comparisons showing:
173+
- Whitelist patterns (`/*`, `!Chart.yaml`)
174+
- Recursive globs (`**/test/`)
175+
- Last-match ordering
176+
177+
5. **Improve v2 and v3 documentation**: Existing `.helmignore` documentation is insufficient, leading to end user confusion, so this should be documented as well.
178+
179+
6. **File location**: Clarify that `.helmignore` must be in the chart root directory. Unlike `.gitignore`, nested `.helmignore` files are not loaded. This existing behavior is unchanged.
180+
181+
### Scaffold Update
182+
183+
Update the default `.helmignore` generated by `helm create` for Charts v3. The current scaffold has been largely unchanged since 2016 ([helm#1028][#1028]). The v3 scaffold should demonstrate gitignore-parity patterns (e.g., `**` globs, whitelist patterns with `!`).
184+
185+
### Release Notes
186+
187+
- Highlight UX improvement for users expecting `.gitignore` behavior
188+
- Link to this HIP and HIP-0020 for context
189+
- Emphasize opt-in nature via Charts v3
190+
191+
### Example Patterns
192+
193+
```gitignore
194+
# Comments start with #
195+
196+
# Ignore all dotfiles
197+
.*
198+
199+
# But keep .helmignore itself
200+
!.helmignore
201+
202+
# Ignore test directories anywhere
203+
**/test/
204+
**/tests/
205+
206+
# Ignore IDE files
207+
.idea/
208+
.vscode/
209+
*.swp
210+
*.swo
211+
*~
212+
213+
# Whitelist approach: exclude everything, then include specifics
214+
/*
215+
!Chart.yaml
216+
!values.yaml
217+
!values.schema.json
218+
!templates/
219+
!charts/
220+
!crds/
221+
!files/
222+
223+
# Directory-only patterns (trailing slash)
224+
vendor/
225+
node_modules/
226+
227+
# Anchored to root (leading slash)
228+
/local-only.yaml
229+
```
230+
231+
## Reference Implementation
232+
233+
A proof-of-concept is available at [github.com/scottrigby/helmignore-ref][helmignore-ref], demonstrating that `go-git/go-git/v5/plumbing/format/gitignore` meets all HIP requirements with minimal wrapper code (~76 lines). The repository includes research documentation comparing library options.
234+
235+
Implementation will be gated behind Charts v3 as specified in [HIP-0020][hip-0020]:
236+
237+
1. New matcher package under `internal/chart/v3/ignore/` using go-git's gitignore library
238+
2. Integration with v3 chart loader
239+
3. Test suite covering all pattern types from this specification
240+
4. Migration tests validating v2 charts remain unchanged
241+
5. Update `helm create` scaffold for v3 charts (once [PR #31592][pr-31592] merges)
242+
243+
## Rejected Ideas
244+
245+
### Shell out to `git check-ignore`
246+
247+
Calling the `git` binary would provide perfect behavioral parity. Rejected because:
248+
249+
- Helm binaries must not have external runtime dependencies
250+
- Adds complexity for containerized/minimal environments
251+
- Performance overhead for repeated invocations
252+
253+
### Partial parity (e.g., add `**` but keep first-match)
254+
255+
Rejected because partial fixes would leave confusing inconsistencies. Users expect `.gitignore` behavior; half-measures perpetuate the problem.
256+
257+
### Change behavior for all chart versions
258+
259+
Rejected because it would break existing charts that depend on current (albeit surprising) semantics. Charts v3 provides clean isolation for breaking changes.
260+
261+
## Open Issues
262+
263+
### Subchart .helmignore Handling
264+
265+
Should `.helmignore` files in subchart directories (`charts/*/`) be respected when processing an umbrella chart?
266+
267+
**Current behavior**: Only the parent chart's `.helmignore` is loaded; subchart `.helmignore` files are ignored.
268+
269+
**Open question**: Is this intended behavior, a bug, or an oversight? Arguments exist both ways:
270+
271+
- **For respecting**: Chart authors expect their ignore rules to apply
272+
- **Against (or: may not matter)**: Parent chart may want control; also, helm-ignored files would already be excluded when the subchart was packaged, so they likely won't be present anyway
273+
274+
**Action**: Investigate history (git commits, meeting notes, previous maintainers) and discuss with community before deciding whether to change this behavior. For now, this HIP preserves existing behavior.
275+
276+
## References
277+
278+
### Helm HIPs
279+
280+
- [HIP-0020: Charts v3 Enablement][hip-0020]
281+
282+
### Git Documentation
283+
284+
- [git-scm.com/docs/gitignore][git-gitignore]
285+
286+
### Libraries
287+
288+
- [go-git/go-git gitignore][go-git-gitignore] — Recommended implementation library
289+
290+
### Issues Directly Addressed by This HIP
291+
292+
These issues are resolved by implementing `.gitignore` pattern matching parity:
293+
294+
- [#8688][#8688] — Negation semantics are inverted; `!pattern` ignores non-matches instead of re-including matches.
295+
- [#3622][#3622] — Whitelist patterns (`/*` then `!Chart.yaml`) fail with "chart metadata missing" due to broken negation logic.
296+
- [#1776][#1776] — Pattern `.*` incorrectly matched the current directory, breaking charts. Shows user expectation of gitignore behavior.
297+
- [#12592][#12592] — Patterns like `charts/*/README.md` don't work; `**` glob support and improved matching would help.
298+
- [#12265][#12265] (PR) — Attempted partial fix for negation, stalled 2 years as a breaking change. This HIP provides the proper scope via Charts v3.
299+
300+
### Issues Out of Scope (Context Only)
301+
302+
These issues concern _when_ `.helmignore` applies, not pattern syntax. Included for context but not addressed by this HIP:
303+
304+
- [#6075][#6075] — Users expected ignore to affect only `helm package`, but it affects all commands. This is intentional; HIP clarifies but doesn't change this.
305+
- [#3050][#3050] — Files in `.helmignore` are inaccessible to `.Files.Get`. Architectural issue about ignore scope, not pattern matching.
306+
- [#10764][#10764] — README files consume release storage; users want "exclude from release but include in package." Requires new scope distinction.
307+
- [#9436][#9436]`.helmignore` doesn't exclude itself from packages. This is intentional becuase `.helmignore` is used for more than packaging, so various workflows could be disrupted (see https://github.com/helm/helm/issues/9436#issuecomment-792795063). Documentation update via this HIP should clarify this.
308+
309+
### Feature Requests (Out of Scope)
310+
311+
- [#1674][#1674], [#5675][#5675] — Global `~/.helmignore` support. Already partially implemented; could adopt gitignore parity separately.
312+
313+
### Documentation Issues
314+
315+
These show user confusion that better docs (and gitignore parity) would reduce:
316+
317+
- [#4638][#4638] — Requested more `.helmignore` documentation; shows need for clearer syntax docs.
318+
- [helm-www#1171][helm-www#1171] — Docs say ignore affects "packaging" only, but it affects all operations. Needs correction regardless of HIP.
319+
- [helm-www#1312][helm-www#1312] — Docs don't specify `.helmignore` must be in chart root, not working directory.
320+
- [helm-www#1460][helm-www#1460] — Example pattern `/temp*` broke charts by matching `templates/`. Shows need for better examples.
321+
322+
### Other Related PRs
323+
324+
- [#13293][#13293] — Fix for broken symlinks in `.helmignore`. Tangentially related to ignore handling.
325+
326+
<!-- Link definitions -->
327+
328+
[hip-0020]: https://github.com/helm/community/blob/main/hips/hip-0020.md
329+
[helmignore-ref]: https://github.com/scottrigby/helmignore-ref
330+
[pr-31592]: https://github.com/helm/helm/pull/31592
331+
[git-gitignore]: https://git-scm.com/docs/gitignore
332+
[go-git-gitignore]: https://github.com/go-git/go-git/tree/main/plumbing/format/gitignore
333+
[#8688]: https://github.com/helm/helm/issues/8688
334+
[#3622]: https://github.com/helm/helm/issues/3622
335+
[#1776]: https://github.com/helm/helm/issues/1776
336+
[#12592]: https://github.com/helm/helm/issues/12592
337+
[#6075]: https://github.com/helm/helm/issues/6075
338+
[#3050]: https://github.com/helm/helm/issues/3050
339+
[#9436]: https://github.com/helm/helm/issues/9436
340+
[#10764]: https://github.com/helm/helm/issues/10764
341+
[#1674]: https://github.com/helm/helm/issues/1674
342+
[#5675]: https://github.com/helm/helm/issues/5675
343+
[#1028]: https://github.com/helm/helm/pull/1028
344+
[#4638]: https://github.com/helm/helm/issues/4638
345+
[#12265]: https://github.com/helm/helm/pull/12265
346+
[helm-www#1171]: https://github.com/helm/helm-www/issues/1171
347+
[helm-www#1312]: https://github.com/helm/helm-www/issues/1312
348+
[helm-www#1460]: https://github.com/helm/helm-www/issues/1460
349+
[#13293]: https://github.com/helm/helm/pull/13293

0 commit comments

Comments
 (0)