diff --git a/internal/chart/v3/lint/rules/template.go b/internal/chart/v3/lint/rules/template.go index d4c62839f..408492584 100644 --- a/internal/chart/v3/lint/rules/template.go +++ b/internal/chart/v3/lint/rules/template.go @@ -123,7 +123,9 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string fileName := template.Name fpath = fileName - linter.RunLinterRule(support.ErrorSev, fpath, validateAllowedExtension(fileName)) + if !isHelperFile(fileName) { + linter.RunLinterRule(support.ErrorSev, fpath, validateAllowedExtension(fileName)) + } // We only apply the following lint rules to yaml files if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" { @@ -219,6 +221,18 @@ func validateTemplatesDir(templatesPath string) error { return nil } +// checks if the file starts with '_'. +func isHelperFile(fileName string) bool { + baseName := filepath.Base(fileName) + ext := filepath.Ext(fileName) + + if strings.HasPrefix(baseName, "_") && ext != ".yaml" && ext != ".yml" { + return true + } + + return false +} + func validateAllowedExtension(fileName string) error { ext := filepath.Ext(fileName) validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"} diff --git a/internal/chart/v3/lint/rules/template_test.go b/internal/chart/v3/lint/rules/template_test.go index 40bcfa26b..0d42be968 100644 --- a/internal/chart/v3/lint/rules/template_test.go +++ b/internal/chart/v3/lint/rules/template_test.go @@ -401,6 +401,7 @@ func TestEmptyWithCommentsManifests(t *testing.T) { t.Fatalf("Expected 0 lint errors, got %d", l) } } + func TestValidateListAnnotations(t *testing.T) { md := &k8sYamlStruct{ APIVersion: "v1", @@ -439,3 +440,35 @@ items: t.Fatalf("List objects keep annotations should pass. got: %s", err) } } + +func TestIsHelperFile(t *testing.T) { + tests := []struct { + fileName string + expected bool + }{ + // Should return true (helper files, non-yaml/yml) + {"_helpers.go", true}, + {"_helpers.json", true}, + {"subdir/_helpers.json", true}, + {"subdir/_partial.tpl", true}, + {"_config.txt", true}, + + // Should return false (yaml/yml files, even with _) + {"_helpers.yaml", false}, + {"_config.yml", false}, + {"subdir/_partial.yaml", false}, + + // Should return false (regular files without _) + {"helpers.go", false}, + {"config.json", false}, + {"deployment.yaml", false}, + } + for _, tt := range tests { + t.Run(tt.fileName, func(t *testing.T) { + result := isHelperFile(tt.fileName) + if result != tt.expected { + t.Errorf("isHelperFile(%q) = %v, expected %v", tt.fileName, result, tt.expected) + } + }) + } +} diff --git a/pkg/chart/v2/lint/rules/template.go b/pkg/chart/v2/lint/rules/template.go index 5c84d0f68..52167174a 100644 --- a/pkg/chart/v2/lint/rules/template.go +++ b/pkg/chart/v2/lint/rules/template.go @@ -123,7 +123,9 @@ func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string fileName := template.Name fpath = fileName - linter.RunLinterRule(support.ErrorSev, fpath, validateAllowedExtension(fileName)) + if !isHelperFile(fileName) { + linter.RunLinterRule(support.ErrorSev, fpath, validateAllowedExtension(fileName)) + } // We only apply the following lint rules to yaml files if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" { @@ -219,6 +221,18 @@ func validateTemplatesDir(templatesPath string) error { return nil } +// checks if the file starts with '_'. +func isHelperFile(fileName string) bool { + baseName := filepath.Base(fileName) + ext := filepath.Ext(fileName) + + if strings.HasPrefix(baseName, "_") && ext != ".yaml" && ext != ".yml" { + return true + } + + return false +} + func validateAllowedExtension(fileName string) error { ext := filepath.Ext(fileName) validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"} diff --git a/pkg/chart/v2/lint/rules/template_test.go b/pkg/chart/v2/lint/rules/template_test.go index 3e8e0b371..41b396838 100644 --- a/pkg/chart/v2/lint/rules/template_test.go +++ b/pkg/chart/v2/lint/rules/template_test.go @@ -439,3 +439,35 @@ items: t.Fatalf("List objects keep annotations should pass. got: %s", err) } } + +func TestIsHelperFile(t *testing.T) { + tests := []struct { + fileName string + expected bool + }{ + // Should return true (helper files, non-yaml/yml) + {"_helpers.go", true}, + {"_helpers.json", true}, + {"subdir/_helpers.json", true}, + {"subdir/_partial.tpl", true}, + {"_config.txt", true}, + + // Should return false (yaml/yml files, even with _) + {"_helpers.yaml", false}, + {"_config.yml", false}, + {"subdir/_partial.yaml", false}, + + // Should return false (regular files without _) + {"helpers.go", false}, + {"config.json", false}, + {"deployment.yaml", false}, + } + for _, tt := range tests { + t.Run(tt.fileName, func(t *testing.T) { + result := isHelperFile(tt.fileName) + if result != tt.expected { + t.Errorf("isHelperFile(%q) = %v, expected %v", tt.fileName, result, tt.expected) + } + }) + } +}