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  }