Repository: helm/helm
File: pkg/lint/rules/template.go (Go)

Code before:
	"k8s.io/apimachinery/pkg/util/validation/field"
	"k8s.io/apimachinery/pkg/util/yaml"

	"helm.sh/helm/v4/pkg/chart/v2/loader"
	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
	"helm.sh/helm/v4/pkg/engine"
	"helm.sh/helm/v4/pkg/lint/support"
)

// Templates lints the templates in the Linter.
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, _ bool) {
	TemplatesWithKubeVersion(linter, values, namespace, nil)
}

// TemplatesWithKubeVersion lints the templates in the Linter, allowing to specify the kubernetes version.
func TemplatesWithKubeVersion(linter *support.Linter, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion) {
	TemplatesWithSkipSchemaValidation(linter, values, namespace, kubeVersion, false)
}

// TemplatesWithSkipSchemaValidation lints the templates in the Linter, allowing to specify the kubernetes version and if schema validation is enabled or not.
func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion, skipSchemaValidation bool) {
	fpath := "templates/"
	templatesPath := filepath.Join(linter.ChartDir, fpath)

	// Templates directory is optional for now
	if _, err := os.Stat(templatesPath); errors.Is(err, os.ErrNotExist) {
		return
	}

	linter.RunLinterRule(support.WarningSev, fpath, validateTemplatesDir(templatesPath))

	// Load chart and parse templates
	chart, err := loader.Load(linter.ChartDir)

	chartLoaded := linter.RunLinterRule(support.ErrorSev, fpath, err)

	if !chartLoaded {
		return
	}

	options := chartutil.ReleaseOptions{
		Name:      "test-release",
		Namespace: namespace,
	}

	caps := chartutil.DefaultCapabilities.Copy()
	if kubeVersion != nil {
		caps.KubeVersion = *kubeVersion
	}

	// lint ignores import-values

Review comment (suggestion):
The `templates/` directory is required for Helm charts. The linter should report if it is missing I think.

I think this `errors.Is(err, os.ErrNotExist)` check should be moved into `validateTemplatesDir`.

And we need to retain the `if !templatesDirExist { return }` logic

Code after:
	"k8s.io/apimachinery/pkg/util/validation/field"
	"k8s.io/apimachinery/pkg/util/yaml"

	"helm.sh/helm/v4/pkg/chart/v2/loader"
	chartutil "helm.sh/helm/v4/pkg/chart/v2/util"
	"helm.sh/helm/v4/pkg/engine"
	"helm.sh/helm/v4/pkg/lint/support"
)

// Templates lints the templates in the Linter.
func Templates(linter *support.Linter, values map[string]interface{}, namespace string, _ bool) {
	TemplatesWithKubeVersion(linter, values, namespace, nil)
}

// TemplatesWithKubeVersion lints the templates in the Linter, allowing to specify the kubernetes version.
func TemplatesWithKubeVersion(linter *support.Linter, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion) {
	TemplatesWithSkipSchemaValidation(linter, values, namespace, kubeVersion, false)
}

// TemplatesWithSkipSchemaValidation lints the templates in the Linter, allowing to specify the kubernetes version and if schema validation is enabled or not.
func TemplatesWithSkipSchemaValidation(linter *support.Linter, values map[string]interface{}, namespace string, kubeVersion *chartutil.KubeVersion, skipSchemaValidation bool) {
	fpath := "templates/"
	templatesPath := filepath.Join(linter.ChartDir, fpath)

	// Templates directory is optional for now
	templatesDirExists := linter.RunLinterRule(support.WarningSev, fpath, templatesDirExists(templatesPath))
	if !templatesDirExists {
		return
	}

	validTemplatesDir := linter.RunLinterRule(support.ErrorSev, fpath, validateTemplatesDir(templatesPath))
	if !validTemplatesDir {
		return
	}

	// Load chart and parse templates
	chart, err := loader.Load(linter.ChartDir)

	chartLoaded := linter.RunLinterRule(support.ErrorSev, fpath, err)

	if !chartLoaded {
		return
	}

	options := chartutil.ReleaseOptions{
		Name:      "test-release",
		Namespace: namespace,
	}

	caps := chartutil.DefaultCapabilities.Copy()
	if kubeVersion != nil {