github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/pkg/project/changeset.go (about)

     1  package project
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/tooploox/oya/pkg/changeset"
    10  	"github.com/tooploox/oya/pkg/oyafile"
    11  	"github.com/tooploox/oya/pkg/task"
    12  )
    13  
    14  // Changeset returns the list of Oyafiles for the changed directories, based on the
    15  // Changeset: directives.
    16  //
    17  // The algorithm:
    18  //
    19  // - Add default Changeset task to root Oyafile if there isn’t one
    20  // - For each project Oyafile:
    21  //   - If Changeset task defined:
    22  //     - Contribute to changeset
    23  // - Deduplicate changeset
    24  // - Exclude changes outside work directory
    25  func (p *Project) Changeset(workDir string) ([]*oyafile.Oyafile, error) {
    26  	oyafiles, err := p.Oyafiles()
    27  	if err != nil {
    28  		return nil, err
    29  	}
    30  	if len(oyafiles) == 0 {
    31  		return nil, ErrNoOyafiles{Path: workDir}
    32  	}
    33  
    34  	rootOyafile := oyafiles[0]
    35  
    36  	if rel, err := filepath.Rel(rootOyafile.Dir, p.RootDir); err != nil || rel != "." {
    37  		panic("Internal error: expecting root Oyafile to be the first item in array returned from project.Project#Oyafiles method")
    38  	}
    39  
    40  	_, ok := rootOyafile.Tasks.LookupTask("Changeset")
    41  	if !ok {
    42  		rootOyafile.Tasks.AddTask("Changeset", defaultRootChangesetTask(oyafiles))
    43  	}
    44  
    45  	// return changeset.Calculate(p.Root, oyafiles)
    46  	changeset, err := changeset.Calculate(oyafiles)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	return restrictToDir(workDir, changeset)
    51  }
    52  
    53  func restrictToDir(dir string, changeset []*oyafile.Oyafile) ([]*oyafile.Oyafile, error) {
    54  	restricted := make([]*oyafile.Oyafile, 0, len(changeset))
    55  	for _, o := range changeset {
    56  		if isInside(dir, o) {
    57  			restricted = append(restricted, o)
    58  		}
    59  	}
    60  	return restricted, nil
    61  }
    62  
    63  func isInside(dir string, o *oyafile.Oyafile) bool {
    64  	d := filepath.Clean(dir)
    65  	r, err := filepath.Rel(d, o.Dir)
    66  	return err == nil && !strings.Contains(r, "..")
    67  }
    68  
    69  func defaultRootChangesetTask(oyafiles []*oyafile.Oyafile) task.Task {
    70  	return task.Builtin{
    71  		OnExec: func(stdout, stderr io.Writer) error {
    72  			for _, o := range oyafiles {
    73  				relPath := filepath.Dir(o.RelPath())
    74  				_, err := stdout.Write([]byte(fmt.Sprintf("+%v\n", relPath)))
    75  				if err != nil {
    76  					return err
    77  				}
    78  			}
    79  
    80  			return nil
    81  		},
    82  	}
    83  }