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

     1  package paket
     2  
     3  import (
     4  	"strings"
     5  
     6  	"github.com/fossas/fossa-cli/errors"
     7  	"github.com/fossas/fossa-cli/files"
     8  	"github.com/fossas/fossa-cli/graph"
     9  	"github.com/fossas/fossa-cli/pkg"
    10  )
    11  
    12  // Group defines a paket dependency group.
    13  type Group struct {
    14  	Name  string
    15  	Specs []Spec
    16  }
    17  
    18  // Spec defines a paket dependency and its attributes.
    19  type Spec struct {
    20  	Remote       string
    21  	Name         string
    22  	Version      string
    23  	Type         pkg.Type
    24  	Dependencies []string
    25  }
    26  
    27  // DependencyGraph parses a "paket.lock" file to create a dependency graph.
    28  func DependencyGraph(target string) (graph.Deps, error) {
    29  	groups, err := readLockfile(target)
    30  	if err != nil {
    31  		return graph.Deps{}, err
    32  	}
    33  
    34  	depGraph, err := graphFromGroups(groups)
    35  	if err != nil {
    36  		return graph.Deps{}, err
    37  	}
    38  
    39  	return depGraph, nil
    40  }
    41  
    42  func readLockfile(filename string) ([]Group, error) {
    43  	contents, err := files.Read(filename)
    44  	if err != nil {
    45  		return []Group{}, errors.Wrapf(err, "could not read lockfile: %s", filename)
    46  	}
    47  
    48  	groups := []Group{}
    49  	groupSplit := strings.Split(string(contents), "GROUP ")
    50  	for _, group := range groupSplit {
    51  		newSpec := Spec{}
    52  		newGroup := Group{}
    53  		packagetype := pkg.NuGet
    54  		remote := ""
    55  
    56  		sections := strings.Split(group, "\n")
    57  		for _, section := range sections {
    58  			switch section {
    59  			case "HTTP":
    60  				fallthrough
    61  			case "GIT":
    62  				fallthrough
    63  			case "GITHUB":
    64  				packagetype = pkg.Git
    65  			case "NUGET":
    66  				packagetype = pkg.NuGet
    67  			default:
    68  				if !strings.HasPrefix(section, "  ") {
    69  					continue
    70  				}
    71  
    72  				if strings.Contains(section, "remote:") {
    73  					remote = strings.Split(section, "remote: ")[1]
    74  					continue
    75  				}
    76  
    77  				matches := strings.Split(section, " ")
    78  				if strings.HasPrefix(section, "      ") {
    79  					if len(matches) >= 7 {
    80  						newSpec.Dependencies = append(newSpec.Dependencies, matches[6])
    81  					}
    82  					continue
    83  				}
    84  
    85  				if newSpec.Name != "" {
    86  					newGroup.Specs = append(newGroup.Specs, newSpec)
    87  				}
    88  
    89  				if len(matches) >= 6 {
    90  					revision := findRevision(matches[5])
    91  					newSpec = Spec{
    92  						Remote:  remote,
    93  						Type:    packagetype,
    94  						Name:    matches[4],
    95  						Version: revision,
    96  					}
    97  				}
    98  			}
    99  		}
   100  
   101  		if newSpec.Name != "" {
   102  			newGroup.Specs = append(newGroup.Specs, newSpec)
   103  		}
   104  		newGroup.Name = sections[0]
   105  		groups = append(groups, newGroup)
   106  	}
   107  	return groups, nil
   108  }
   109  
   110  func graphFromGroups(groups []Group) (graph.Deps, error) {
   111  	packageMap := make(map[string]pkg.Import)
   112  	for _, group := range groups {
   113  		for _, spec := range group.Specs {
   114  			name := spec.Name
   115  			if spec.Type == pkg.Git {
   116  				name = spec.Remote + "/" + spec.Name
   117  			}
   118  
   119  			packageMap[spec.Name] = pkg.Import{
   120  				Target: spec.Name,
   121  				Resolved: pkg.ID{
   122  					Type:     spec.Type,
   123  					Name:     name,
   124  					Revision: spec.Version,
   125  					Location: spec.Remote,
   126  				},
   127  			}
   128  		}
   129  	}
   130  
   131  	packageGraph := graph.Deps{
   132  		Direct:     []pkg.Import{},
   133  		Transitive: make(map[pkg.ID]pkg.Package),
   134  	}
   135  	for _, group := range groups {
   136  		for _, spec := range group.Specs {
   137  			packageImport := packageMap[spec.Name]
   138  			packageGraph.Direct = append(packageGraph.Direct, packageImport)
   139  
   140  			imports := []pkg.Import{}
   141  			for _, dep := range spec.Dependencies {
   142  				imports = append(imports, packageMap[dep])
   143  			}
   144  
   145  			packageGraph.Transitive[packageImport.Resolved] = pkg.Package{
   146  				ID:      packageImport.Resolved,
   147  				Imports: imports,
   148  			}
   149  		}
   150  	}
   151  	return packageGraph, nil
   152  }
   153  
   154  func findRevision(line string) string {
   155  	revision := strings.TrimPrefix(line, "(")
   156  	revision = strings.TrimSuffix(revision, ")")
   157  	return revision
   158  }