github.com/databricks/cli@v0.203.0/bundle/config/mutator/process_root_includes.go (about) 1 package mutator 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/databricks/cli/bundle" 11 "github.com/databricks/cli/bundle/config" 12 "golang.org/x/exp/slices" 13 ) 14 15 // Get extra include paths from environment variable 16 func GetExtraIncludePaths() []string { 17 value, exists := os.LookupEnv(bundle.ExtraIncludePathsKey) 18 if !exists { 19 return nil 20 } 21 return strings.Split(value, string(os.PathListSeparator)) 22 } 23 24 type processRootIncludes struct{} 25 26 // ProcessRootIncludes expands the patterns in the configuration's include list 27 // into a list of mutators for each matching file. 28 func ProcessRootIncludes() bundle.Mutator { 29 return &processRootIncludes{} 30 } 31 32 func (m *processRootIncludes) Name() string { 33 return "ProcessRootIncludes" 34 } 35 36 func (m *processRootIncludes) Apply(ctx context.Context, b *bundle.Bundle) error { 37 var out []bundle.Mutator 38 39 // Map with files we've already seen to avoid loading them twice. 40 var seen = map[string]bool{} 41 42 for _, file := range config.FileNames { 43 seen[file] = true 44 } 45 46 // Maintain list of files in order of files being loaded. 47 // This is stored in the bundle configuration for observability. 48 var files []string 49 50 // Converts extra include paths from environment variable to relative paths 51 for _, extraIncludePath := range GetExtraIncludePaths() { 52 if filepath.IsAbs(extraIncludePath) { 53 rel, err := filepath.Rel(b.Config.Path, extraIncludePath) 54 if err != nil { 55 return fmt.Errorf("unable to include file '%s': %w", extraIncludePath, err) 56 } 57 extraIncludePath = rel 58 } 59 b.Config.Include = append(b.Config.Include, extraIncludePath) 60 } 61 62 // For each glob, find all files to load. 63 // Ordering of the list of globs is maintained in the output. 64 // For matches that appear in multiple globs, only the first is kept. 65 for _, entry := range b.Config.Include { 66 // Include paths must be relative. 67 if filepath.IsAbs(entry) { 68 return fmt.Errorf("%s: includes must be relative paths", entry) 69 } 70 71 // Anchor includes to the bundle root path. 72 matches, err := filepath.Glob(filepath.Join(b.Config.Path, entry)) 73 if err != nil { 74 return err 75 } 76 77 // If the entry is not a glob pattern and no matches found, 78 // return an error because the file defined is not found 79 if len(matches) == 0 && !strings.ContainsAny(entry, "*?[") { 80 return fmt.Errorf("%s defined in 'include' section does not match any files", entry) 81 } 82 83 // Filter matches to ones we haven't seen yet. 84 var includes []string 85 for _, match := range matches { 86 rel, err := filepath.Rel(b.Config.Path, match) 87 if err != nil { 88 return err 89 } 90 if _, ok := seen[rel]; ok { 91 continue 92 } 93 seen[rel] = true 94 includes = append(includes, rel) 95 } 96 97 // Add matches to list of mutators to return. 98 slices.Sort(includes) 99 files = append(files, includes...) 100 for _, include := range includes { 101 out = append(out, ProcessInclude(filepath.Join(b.Config.Path, include), include)) 102 } 103 } 104 105 // Swap out the original includes list with the expanded globs. 106 b.Config.Include = files 107 108 return bundle.Apply(ctx, b, bundle.Seq(out...)) 109 }