github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/sbt/sbt.go (about)

     1  package sbt
     2  
     3  import (
     4  	"regexp"
     5  	"strings"
     6  
     7  	"github.com/apex/log"
     8  	"github.com/pkg/errors"
     9  
    10  	"github.com/fossas/fossa-cli/exec"
    11  	"github.com/fossas/fossa-cli/files"
    12  	"github.com/fossas/fossa-cli/pkg"
    13  )
    14  
    15  type SBT struct {
    16  	Bin string
    17  }
    18  
    19  func (s *SBT) Clean(dir, project, configuration string) error {
    20  	_, _, err := exec.Run(exec.Cmd{
    21  		Dir:  dir,
    22  		Name: s.Bin,
    23  		Argv: []string{Task(project, configuration, "clean")},
    24  	})
    25  	return err
    26  }
    27  
    28  func (s *SBT) Compile(dir, project, configuration string) error {
    29  	_, _, err := exec.Run(exec.Cmd{
    30  		Dir:  dir,
    31  		Name: s.Bin,
    32  		Argv: []string{Task(project, configuration, "compile")},
    33  	})
    34  	return err
    35  }
    36  
    37  func (s *SBT) Projects(dir string) ([]string, error) {
    38  	output, _, err := exec.Run(exec.Cmd{
    39  		Dir:  dir,
    40  		Name: s.Bin,
    41  		Argv: []string{"-no-colors", "projects"},
    42  	})
    43  	if err != nil {
    44  		return nil, errors.Wrap(err, "could not get projects from SBT")
    45  	}
    46  
    47  	// Filter lines to only include projects.
    48  	var projects []string
    49  	for _, project := range FilterLines(strings.Split(output, "\n")) {
    50  		log.WithField("project", project).Debug("project line")
    51  		noPrefix := strings.TrimPrefix(project, "[info] 	   ")
    52  		noSelectedPrefix := strings.TrimPrefix(noPrefix, "[info] 	 * ")
    53  		log.WithField("project", project).Debug("trimmed project line")
    54  		projects = append(projects, noSelectedPrefix)
    55  	}
    56  	return projects, nil
    57  }
    58  
    59  type Dependency struct {
    60  	Name    string
    61  	Version string
    62  }
    63  
    64  func (s *SBT) DependencyTree(dir, project, configuration string) (pkg.Imports, pkg.Deps, error) {
    65  	output, _, err := exec.Run(exec.Cmd{
    66  		Dir:  dir,
    67  		Name: s.Bin,
    68  		Argv: []string{"-no-colors", Task(project, configuration, "dependencyGraphMl")},
    69  	})
    70  	if err != nil {
    71  		return nil, nil, errors.Wrap(err, "could not get dependency graph from SBT")
    72  	}
    73  
    74  	file := ""
    75  	r := regexp.MustCompile("^\\[info\\] Wrote dependency graph to '(.*?)'$")
    76  	for _, line := range strings.Split(output, "\n") {
    77  		matches := r.FindStringSubmatch(line)
    78  		if matches != nil {
    79  			file = matches[1]
    80  		}
    81  	}
    82  	log.WithField("file", file).Debug("graph XML file")
    83  
    84  	var root GraphML
    85  	err = files.ReadXML(&root, file)
    86  	if err != nil {
    87  		return nil, nil, errors.Wrap(err, "could not parse SBT dependency graph")
    88  	}
    89  	log.WithField("root", root).Debug("parsed XML graph")
    90  
    91  	evicted, _, err := exec.Run(exec.Cmd{
    92  		Dir:  dir,
    93  		Name: s.Bin,
    94  		Argv: []string{"-no-colors", Task(project, configuration, "evicted")},
    95  	})
    96  	if err != nil {
    97  		return nil, nil, errors.Wrap(err, "could not get version conflict resolutions from SBT")
    98  	}
    99  
   100  	return ParseDependencyGraph(root.Graph, evicted)
   101  }
   102  
   103  func (s *SBT) DependencyList(dir, project, configuration string) (string, error) {
   104  	output, _, err := exec.Run(exec.Cmd{
   105  		Dir:  dir,
   106  		Name: s.Bin,
   107  		Argv: []string{"-no-colors", Task(project, configuration, "dependencyList")},
   108  	})
   109  	if err != nil {
   110  		return "", errors.Wrap(err, "could not get dependency list from SBT")
   111  	}
   112  
   113  	// Filter lines to only include dependency list.
   114  	return strings.Join(FilterLines(strings.Split(output, "\n")), "\n"), err
   115  }
   116  
   117  func FilterLine(line string) bool {
   118  	trimmed := strings.TrimSpace(line)
   119  	if !strings.HasPrefix(trimmed, "[info] ") {
   120  		return false
   121  	}
   122  	infoMsg := strings.TrimPrefix(trimmed, "[info] ")
   123  	return !(strings.HasPrefix(infoMsg, "Loading ") ||
   124  		strings.HasPrefix(infoMsg, "Compiling ") ||
   125  		strings.HasPrefix(infoMsg, "Non-compiled module ") ||
   126  		strings.HasPrefix(strings.TrimSpace(infoMsg), "Compilation ") ||
   127  		strings.HasPrefix(infoMsg, "Resolving ") ||
   128  		strings.HasPrefix(infoMsg, "Resolved ") ||
   129  		strings.HasPrefix(infoMsg, "Fetching ") ||
   130  		strings.HasPrefix(infoMsg, "Fetched ") ||
   131  		strings.HasPrefix(infoMsg, "Set ") ||
   132  		strings.HasPrefix(infoMsg, "In file:") ||
   133  		strings.HasPrefix(infoMsg, "Updating ") ||
   134  		strings.HasPrefix(infoMsg, "Done ") ||
   135  		strings.HasPrefix(infoMsg, "downloading ") ||
   136  		strings.HasPrefix(strings.TrimSpace(infoMsg), "[SUCCESSFUL ]"))
   137  }
   138  
   139  func FilterLines(lines []string) []string {
   140  	var filtered []string
   141  	for _, line := range lines {
   142  		if FilterLine(line) {
   143  			log.WithField("line", line).Debug("matched line")
   144  			filtered = append(filtered, line)
   145  		} else {
   146  			log.WithField("line", line).Debug("ignoring line")
   147  		}
   148  	}
   149  	return filtered
   150  }
   151  
   152  func Task(project, configuration, task string) string {
   153  	cmd := task
   154  	if configuration != "" {
   155  		cmd = configuration + ":" + cmd
   156  	}
   157  	if project != "" {
   158  		cmd = project + "/" + cmd
   159  	}
   160  	return cmd
   161  }