| hip | 0027 | |
|---|---|---|
| title | Bring .helmignore to parity with .gitignore file targeting syntax | |
| authors |
|
|
| created | 2025-12-14 | |
| type | feature | |
| status | draft | |
| requires |
|
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), existing charts continue to work unchanged while v3 charts opt into consistent, predictable ignore behavior.
Users familiar with .gitignore expect the same behavior from .helmignore. The current divergence causes:
-
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). -
Missing recursive globs: The
**pattern is explicitly unsupported, forcing verbose workarounds for common cases like**/test/(#12592). -
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.
-
Documentation gaps: Current docs don't clearly explain limitations or differences from
.gitignore(#4638, helm-www#1312).
A comprehensive search of helm/helm issues reveals 27 directly related issues and 5 pull requests. Key themes:
- Pattern matching logic: #8688, #3622, #1776, #12592
- Scope/behavior confusion: #6075, #3050, #10764
- Documentation issues: #4638, helm-www#1171, helm-www#1312, helm-www#1460
The recurring user expectation is clear: .helmignore should work like .gitignore.
-
Reduced cognitive load: Developers already know
.gitignoresemantics. Aligning.helmignoreenables existing knowledge and patterns to be reused with Helm. -
Well-documented spec: Git's ignore format is thoroughly documented and battle-tested across millions of repositories.
-
Ecosystem consistency: Tools like Docker (
.dockerignore), npm (.npmignore), and FluxCD (.sourceignore) all follow.gitignoresemantics.
Per HIP-0020, Charts v3 provides a clean opt-in boundary for breaking changes:
-
Breaking change isolation: Charts explicitly declare
apiVersion: v3, accepting new semantics. Charts v2 behavior is preserved unchanged. -
No forced migration: Existing charts continue to work. Users migrate on their own schedule.
-
Independent evolution: Chart changes are decoupled from Helm version, allowing adequate time for testing and adoption.
This is the appropriate scope because changing .helmignore semantics would break charts that depend on current (albeit surprising) behavior.
Charts v3 .helmignore files should support the full .gitignore pattern format as documented at git-scm.com/docs/gitignore.
.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.
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).
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.
**/foo— matchesfooin all directoriesfoo/**— matches everything insidefoo/a/**/b— matchesa/b,a/x/b,a/x/y/b, etc.
Last matching rule wins. All rules are evaluated; the final matching rule determines whether a path is included or excluded. This enables patterns like:
# Exclude all top-level entries (files and directories)
/*
# But include these
!Chart.yaml
!values.yaml
!templates/
!patternre-includes files that match, reversing a prior exclusion- Limitation: Cannot re-include a file if its parent directory was excluded (Git skips listing excluded directories for performance)
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.
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.
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.
-
Parser/Matcher: Implement a
.gitignore-compatible lexer, parser, and matcher supporting all pattern types above. -
Last-match semantics: Evaluate all rules; final matching rule determines outcome.
-
Path normalization: Normalize paths to forward slashes for cross-platform consistency.
-
Directory short-circuit: Once a directory is excluded, skip traversing its contents (matches Git's performance optimization).
The new .gitignore-parity matching should apply in all existing chart file operations (unchanged scope):
helm packagehelm linthelm templatehelm install/helm upgrade(for local charts).Files.Get/.Files.Glob(file access in templates)
This list is representative; the matching logic applies wherever chart files are loaded or accessed.
- Charts v3 (
apiVersion: v3): New.gitignore-parity semantics - Charts v2 (
apiVersion: v2): Existing behavior unchanged
No behavior change. Existing .helmignore files continue to work exactly as before, preserving compatibility for all current charts.
Charts opting into v3 accept new .helmignore semantics. Potential breaking changes for charts migrating from v2:
| Current Behavior | New Behavior | Migration Impact |
|---|---|---|
| First-match evaluation | Last-match evaluation | Patterns relying on early termination may behave differently |
| Negation inverts match | Negation re-includes | Whitelist patterns will finally work correctly |
** causes error |
** works |
No breakage (feature addition) |
| Trailing spaces stripped | Preserved with \ |
Unlikely to affect real charts |
| No escape sequences | \#, \!, \ supported |
No breakage (feature addition) |
None. This change only affects which local files are considered during chart operations. It does not:
- Introduce new attack surfaces
- Add remote dependencies
- Change network behavior
- Affect chart signing or verification
-
Primary statement: "Charts v3
.helmignoreuses identical pattern rules to.gitignore." -
Link to Git documentation: Reference git-scm.com/docs/gitignore as background documentation for the pattern semantics that
.helmignorefollows. -
Migration guide: Document behavior differences between v2 and v3.
-
Examples: Provide side-by-side comparisons showing:
- Whitelist patterns (
/*,!Chart.yaml) - Recursive globs (
**/test/) - Last-match ordering
- Whitelist patterns (
-
Improve v2 and v3 documentation: Existing
.helmignoredocumentation is insufficient, leading to end user confusion, so this should be documented as well. -
File location: Clarify that
.helmignoremust be in the chart root directory. Unlike.gitignore, nested.helmignorefiles are not loaded. This existing behavior is unchanged.
Update the default .helmignore generated by helm create for Charts v3. The current scaffold has been largely unchanged since 2016 (helm#1028). The v3 scaffold should demonstrate gitignore-parity patterns (e.g., ** globs, whitelist patterns with !).
- Highlight UX improvement for users expecting
.gitignorebehavior - Link to this HIP and HIP-0020 for context
- Emphasize opt-in nature via Charts v3
# Comments start with #
# Ignore all dotfiles
.*
# But keep .helmignore itself
!.helmignore
# Ignore test directories anywhere
**/test/
**/tests/
# Ignore IDE files
.idea/
.vscode/
*.swp
*.swo
*~
# Whitelist approach: exclude everything, then include specifics
/*
!Chart.yaml
!values.yaml
!values.schema.json
!templates/
!charts/
!crds/
!files/
# Directory-only patterns (trailing slash)
vendor/
node_modules/
# Anchored to root (leading slash)
/local-only.yamlA proof-of-concept is available at github.com/scottrigby/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.
Implementation will be gated behind Charts v3 as specified in HIP-0020:
- New matcher package under
internal/chart/v3/ignore/using go-git's gitignore library - Integration with v3 chart loader
- Test suite covering all pattern types from this specification
- Migration tests validating v2 charts remain unchanged
- Update
helm createscaffold for v3 charts (once PR #31592 merges)
Calling the git binary would provide perfect behavioral parity. Rejected because:
- Helm binaries must not have external runtime dependencies
- Adds complexity for containerized/minimal environments
- Performance overhead for repeated invocations
Rejected because partial fixes would leave confusing inconsistencies. Users expect .gitignore behavior; half-measures perpetuate the problem.
Rejected because it would break existing charts that depend on current (albeit surprising) semantics. Charts v3 provides clean isolation for breaking changes.
Should .helmignore files in subchart directories (charts/*/) be respected when processing an umbrella chart?
Current behavior: Only the parent chart's .helmignore is loaded; subchart .helmignore files are ignored.
Open question: Is this intended behavior, a bug, or an oversight? Arguments exist both ways:
- For respecting: Chart authors expect their ignore rules to apply
- 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
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.
- go-git/go-git gitignore — Recommended implementation library
These issues are resolved by implementing .gitignore pattern matching parity:
- #8688 — Negation semantics are inverted;
!patternignores non-matches instead of re-including matches. - #3622 — Whitelist patterns (
/*then!Chart.yaml) fail with "chart metadata missing" due to broken negation logic. - #1776 — Pattern
.*incorrectly matched the current directory, breaking charts. Shows user expectation of gitignore behavior. - #12592 — Patterns like
charts/*/README.mddon't work;**glob support and improved matching would help. - #12265 (PR) — Attempted partial fix for negation, stalled 2 years as a breaking change. This HIP provides the proper scope via Charts v3.
These issues concern when .helmignore applies, not pattern syntax. Included for context but not addressed by this HIP:
- #6075 — Users expected ignore to affect only
helm package, but it affects all commands. This is intentional; HIP clarifies but doesn't change this. - #3050 — Files in
.helmignoreare inaccessible to.Files.Get. Architectural issue about ignore scope, not pattern matching. - #10764 — README files consume release storage; users want "exclude from release but include in package." Requires new scope distinction.
- #9436 —
.helmignoredoesn't exclude itself from packages. This is intentional becuase.helmignoreis used for more than packaging, so various workflows could be disrupted (see helm/helm#9436 (comment)). Documentation update via this HIP should clarify this.
- #1674, #5675 — Global
~/.helmignoresupport. Already partially implemented; could adopt gitignore parity separately.
These show user confusion that better docs (and gitignore parity) would reduce:
- #4638 — Requested more
.helmignoredocumentation; shows need for clearer syntax docs. - helm-www#1171 — Docs say ignore affects "packaging" only, but it affects all operations. Needs correction regardless of HIP.
- helm-www#1312 — Docs don't specify
.helmignoremust be in chart root, not working directory. - helm-www#1460 — Example pattern
/temp*broke charts by matchingtemplates/. Shows need for better examples.
- #13293 — Fix for broken symlinks in
.helmignore. Tangentially related to ignore handling.