github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/pkg/oyafile/oyafile.go (about) 1 package oyafile 2 3 import ( 4 "fmt" 5 "io" 6 "path" 7 "path/filepath" 8 "strings" 9 10 log "github.com/sirupsen/logrus" 11 12 "github.com/tooploox/oya/pkg/errors" 13 "github.com/tooploox/oya/pkg/raw" 14 "github.com/tooploox/oya/pkg/semver" 15 "github.com/tooploox/oya/pkg/task" 16 "github.com/tooploox/oya/pkg/template" 17 "github.com/tooploox/oya/pkg/types" 18 ) 19 20 type PackReference struct { 21 ImportPath types.ImportPath 22 Version semver.Version 23 // ReplacementPath is a path relative to the root directory, when the replacement for the pack can be found, based on the Replace: directive. 24 ReplacementPath string 25 } 26 27 type PackReplacements map[types.ImportPath]string 28 29 type Oyafile struct { 30 Dir string 31 Path string 32 RootDir string 33 Shell string 34 Imports map[types.Alias]types.ImportPath 35 Tasks task.Table 36 Values template.Scope 37 Project string // Project is set for root Oyafile. 38 Ignore []string // Ignore contains directory exclusion rules. 39 Requires []PackReference 40 // Replacements map packs to local paths relative to project root directory for development based on the Replace: directive. 41 Replacements PackReplacements 42 IsBuilt bool 43 44 relPath string 45 } 46 47 func New(oyafilePath string, rootDir string) (*Oyafile, error) { 48 relPath, err := filepath.Rel(rootDir, oyafilePath) 49 log.Debug("Oyafile at ", oyafilePath, " root dir: ", rootDir) 50 if err != nil { 51 return nil, err 52 } 53 dir := path.Dir(oyafilePath) 54 return &Oyafile{ 55 Dir: filepath.Clean(dir), 56 Path: filepath.Clean(oyafilePath), 57 RootDir: filepath.Clean(rootDir), 58 Shell: "/bin/bash", 59 Imports: make(map[types.Alias]types.ImportPath), 60 Tasks: task.NewTable(), 61 Values: template.Scope{}, 62 relPath: relPath, 63 Replacements: make(PackReplacements), 64 }, nil 65 } 66 67 func Load(oyafilePath, rootDir string) (*Oyafile, bool, error) { 68 raw, found, err := raw.Load(oyafilePath, rootDir) 69 if err != nil || !found { 70 return nil, found, err 71 } 72 oyafile, err := Parse(raw) 73 if err != nil { 74 return nil, false, wrapLoadErr(err, oyafilePath) 75 } 76 return oyafile, true, nil 77 } 78 79 func LoadFromDir(dirPath, rootDir string) (*Oyafile, bool, error) { 80 raw, found, err := raw.LoadFromDir(dirPath, rootDir) 81 if err != nil || !found { 82 return nil, found, err 83 } 84 oyafile, err := Parse(raw) 85 if err != nil { 86 return nil, false, wrapLoadErr(err, raw.Path) 87 } 88 return oyafile, true, nil 89 } 90 91 func (oyafile Oyafile) RunTask(taskName task.Name, args []string, scope template.Scope, stdout, stderr io.Writer) (bool, error) { 92 if !oyafile.IsBuilt { 93 return false, errors.Errorf("Internal error: Oyafile has not been built") 94 } 95 task, ok := oyafile.Tasks.LookupTask(taskName) 96 if !ok { 97 return false, nil 98 } 99 100 err := task.Exec(oyafile.Dir, args, scope, stdout, stderr) 101 if err != nil { 102 err = oyafile.enrichError(err, taskName, args) 103 104 return true, errors.Wrap( 105 err, 106 ErrTaskFail{ 107 TaskName: taskName, 108 Args: args, 109 }, 110 errors.Location{ 111 VerboseName: fmt.Sprintf("in file %q", oyafile.Path), 112 Name: oyafile.Path, 113 }, 114 ) 115 116 } 117 return true, nil 118 } 119 120 func (oyafile Oyafile) enrichError(err error, taskName task.Name, args []string) error { 121 alias, name := taskName.Split() 122 if alias.IsEmpty() { 123 return err 124 } 125 importPath, ok := oyafile.Imports[alias] 126 if !ok { 127 return err 128 } 129 return errors.Wrap( 130 err, 131 ErrTaskFail{ 132 TaskName: task.Name(name), 133 Args: args, 134 }, 135 errors.Location{ 136 Name: importPath.String(), 137 VerboseName: fmt.Sprintf("in %v imported as %q", importPath, alias), 138 }, 139 ) 140 } 141 142 func (oyafile Oyafile) Equals(other Oyafile) bool { 143 // TODO: Far from perfect, we should ensure relative vs absolute paths work. 144 // The simplest thing is probably to ensure oyafile.Dir is always absolute. 145 return filepath.Clean(oyafile.Dir) == filepath.Clean(other.Dir) 146 } 147 148 func wrapLoadErr(err error, oyafilePath string) error { 149 return errors.Wrapf(err, "error loading Oyafile %v", oyafilePath) 150 } 151 152 func (o *Oyafile) Ignores() string { 153 return strings.Join(o.Ignore, "\n") 154 } 155 156 func (o *Oyafile) RelPath() string { 157 return o.relPath 158 }