@@ -28,6 +28,7 @@ import (
2828
2929 "github.com/opdev/getocprange"
3030 "helm.sh/helm/v3/pkg/action"
31+ "helm.sh/helm/v3/pkg/chart"
3132 "helm.sh/helm/v3/pkg/lint"
3233 "helm.sh/helm/v3/pkg/lint/support"
3334
@@ -234,14 +235,107 @@ func NotContainCRDs(opts *CheckOptions) (Result, error) {
234235
235236 r := NewResult (true , ChartDoesNotContainCRDs )
236237
237- if len (c .CRDObjects ()) > 0 {
238+ // Check standard CRD directory in main chart and dependencies
239+ if hasCRDObjects (c ) {
240+ r .Ok = false
241+ r .SetResult (false , ChartContainCRDs )
242+ return r , nil
243+ }
244+
245+ // Check for CRDs in templates (main chart and dependencies)
246+ if hasCRDInTemplates (c ) {
247+ r .Ok = false
248+ r .SetResult (false , ChartContainCRDs )
249+ return r , nil
250+ }
251+
252+ // Check for CRDs in files (root directory of main chart and dependencies)
253+ if hasCRDInFiles (c ) {
238254 r .Ok = false
239255 r .SetResult (false , ChartContainCRDs )
256+ return r , nil
240257 }
241258
242259 return r , nil
243260}
244261
262+ func hasCRDObjects (c * chart.Chart ) bool {
263+ // Check main chart CRDs directory
264+ if len (c .CRDObjects ()) > 0 {
265+ return true
266+ }
267+
268+ // Recursively check dependencies' CRDs directories
269+ for _ , dep := range c .Dependencies () {
270+ if hasCRDObjects (dep ) {
271+ return true
272+ }
273+ }
274+
275+ return false
276+ }
277+
278+ func hasCRDInTemplates (c * chart.Chart ) bool {
279+ // Check main chart templates
280+ for _ , f := range c .Templates {
281+ if ! strings .HasSuffix (f .Name , ".yaml" ) && ! strings .HasSuffix (f .Name , ".yml" ) {
282+ continue
283+ }
284+ if isCRDFile (f .Data ) {
285+ return true
286+ }
287+ }
288+
289+ // Check dependency/subchart templates
290+ for _ , dep := range c .Dependencies () {
291+ if hasCRDInTemplates (dep ) {
292+ return true
293+ }
294+ }
295+
296+ return false
297+ }
298+
299+ func isCRDFile (data []byte ) bool {
300+ // Split on YAML document separator for multi-doc files
301+ docs := strings .Split (string (data ), "\n ---" )
302+ for _ , doc := range docs {
303+ for _ , line := range strings .Split (doc , "\n " ) {
304+ trimmed := strings .TrimSpace (line )
305+ if strings .HasPrefix (trimmed , "kind:" ) {
306+ kind := strings .TrimSpace (strings .TrimPrefix (trimmed , "kind:" ))
307+ // Remove surrounding quotes if present (both single and double)
308+ kind = strings .Trim (kind , "\" '" )
309+ if kind == "CustomResourceDefinition" {
310+ return true
311+ }
312+ }
313+ }
314+ }
315+ return false
316+ }
317+
318+ func hasCRDInFiles (c * chart.Chart ) bool {
319+ // Check this chart's files (root directory)
320+ for _ , f := range c .Files {
321+ if ! strings .HasSuffix (f .Name , ".yaml" ) && ! strings .HasSuffix (f .Name , ".yml" ) {
322+ continue
323+ }
324+ if isCRDFile (f .Data ) {
325+ return true
326+ }
327+ }
328+
329+ // Recursively check dependencies
330+ for _ , dep := range c .Dependencies () {
331+ if hasCRDInFiles (dep ) {
332+ return true
333+ }
334+ }
335+
336+ return false
337+ }
338+
245339func HelmLint (opts * CheckOptions ) (Result , error ) {
246340 _ , p , err := LoadChartFromURI (opts )
247341 if err != nil {
0 commit comments