github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/python/poetry.go (about)

     1  /*
     2   * Copyright (c) 2021 CodeNotary, Inc. All Rights Reserved.
     3   * This software is released under GPL3.
     4   * The full license information can be found under:
     5   * https://www.gnu.org/licenses/gpl-3.0.en.html
     6   *
     7   */
     8  
     9  package python
    10  
    11  import (
    12  	"fmt"
    13  	"path/filepath"
    14  
    15  	"github.com/BurntSushi/toml"
    16  	"github.com/schollz/progressbar/v3"
    17  
    18  	"github.com/vchain-us/vcn/pkg/bom/artifact"
    19  )
    20  
    21  // pythonArtifactFromPoetry implements Artifact interface
    22  type pythonArtifactFromPoetry struct {
    23  	pythonArtifact
    24  }
    25  
    26  // poetry.lock TOML structure
    27  type poetryFile struct {
    28  	Packages []pkg `toml:"package"`
    29  }
    30  type pkg struct {
    31  	Name    string
    32  	Version string
    33  }
    34  
    35  // poetry.lock file contains list of all dependencies with hashes
    36  func (a *pythonArtifactFromPoetry) ResolveDependencies(output artifact.OutputOptions) ([]artifact.Dependency, error) {
    37  	if a.Deps != nil {
    38  		return a.Deps, nil
    39  	}
    40  	var poetry poetryFile
    41  	_, err := toml.DecodeFile(filepath.Join(a.path, poetryFileName), &poetry)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	var bar *progressbar.ProgressBar
    46  	if output == artifact.Progress {
    47  		bar = progressbar.Default(int64(len(poetry.Packages)))
    48  	}
    49  
    50  	// init goroutine throttling - channels, start goroutines.
    51  	// We can be sure that there will be no more in-flight messages in channels than known modules
    52  	tasks := make(chan task, len(poetry.Packages))
    53  	results := make(chan result, len(poetry.Packages))
    54  	for i := 0; i < artifact.MaxGoroutines; i++ {
    55  		go poetryWorker(tasks, results, output, bar)
    56  	}
    57  	defer close(results)
    58  	defer close(tasks) // signal workers to stop
    59  
    60  	taskCount := 0
    61  
    62  	for _, pkg := range poetry.Packages {
    63  		tasks <- task{name: pkg.Name, version: pkg.Version}
    64  		taskCount++
    65  	}
    66  
    67  	res := make([]artifact.Dependency, 0, len(poetry.Packages))
    68  	for done := 0; taskCount == 0 || done < taskCount; done++ {
    69  		result := <-results
    70  		if result.err != nil {
    71  			close(tasks) // signal workers to stop
    72  			return nil, err
    73  		}
    74  		res = append(res, artifact.Dependency{
    75  			Name:     result.name,
    76  			Version:  result.version,
    77  			Hash:     result.hash,
    78  			HashType: result.hashType,
    79  			License:  result.license})
    80  	}
    81  
    82  	a.Deps = res
    83  	return res, nil
    84  }
    85  
    86  func poetryWorker(tasks <-chan task, results chan<- result, output artifact.OutputOptions, bar *progressbar.ProgressBar) {
    87  	for task := range tasks {
    88  		lic, hashType, hash, err := QueryPkgDetails(task.name, task.version)
    89  		if err != nil {
    90  			results <- result{err: err}
    91  			continue
    92  		}
    93  
    94  		results <- result{name: task.name, version: task.version, hash: hash, hashType: hashType, license: lic, err: nil}
    95  		switch output {
    96  		case artifact.Progress:
    97  			bar.Add(1)
    98  		case artifact.Debug:
    99  			fmt.Printf("%s@%s (%s)\n", task.name, task.version, hash)
   100  		}
   101  	}
   102  }