github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/python/pipenv.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 "encoding/json" 13 "errors" 14 "fmt" 15 "io/ioutil" 16 "os" 17 "path/filepath" 18 "strings" 19 20 "github.com/schollz/progressbar/v3" 21 22 "github.com/vchain-us/vcn/pkg/bom/artifact" 23 ) 24 25 // pythonArtifactFromPipEnv implements Artifact interface 26 type pythonArtifactFromPipEnv struct { 27 pythonArtifact 28 } 29 30 // Dependencies returns list of Python dependencies for the artifact 31 // for pipenv Pipfile.lock JSON file contains all needed information in "default" section 32 func (a *pythonArtifactFromPipEnv) ResolveDependencies(output artifact.OutputOptions) ([]artifact.Dependency, error) { 33 if a.Deps != nil { 34 return a.Deps, nil 35 } 36 file, err := os.Open(filepath.Join(a.path, pipenvFileName)) 37 if err != nil { 38 return nil, err 39 } 40 41 buf, err := ioutil.ReadAll(file) 42 if err != nil { 43 return nil, err 44 } 45 46 var content map[string]interface{} 47 err = json.Unmarshal(buf, &content) 48 if err != nil { 49 return nil, err 50 } 51 52 deflt, ok := content["default"] 53 if !ok { 54 return nil, errors.New("malformed Pipfile.lock - missing \"default\"") 55 } 56 packages, ok := deflt.(map[string]interface{}) 57 if !ok { 58 return nil, errors.New("malformed Pipfile.lock - \"default\" has a wrong data type") 59 } 60 61 var bar *progressbar.ProgressBar 62 if output == artifact.Progress { 63 bar = progressbar.Default(int64(len(packages))) 64 } 65 66 // init goroutine throttling - channels, start goroutines. 67 // We can be sure that there will be no more in-flight messages in channels than known modules 68 tasks := make(chan task, len(packages)) 69 results := make(chan result, len(packages)) 70 for i := 0; i < artifact.MaxGoroutines; i++ { 71 go poetryWorker(tasks, results, output, bar) // pipenv and poetry use the same worker, only hash and license 72 } 73 defer close(results) 74 defer close(tasks) // signal workers to stop 75 76 taskCount := 0 77 78 for name, pkg := range packages { 79 pkgContent, ok := pkg.(map[string]interface{}) 80 if !ok { 81 return nil, fmt.Errorf("malformed \"%s\" section", name) 82 } 83 84 field, ok := pkgContent["version"] 85 if !ok { 86 return nil, fmt.Errorf("malformed \"%s\" section", name) 87 } 88 version, ok := field.(string) 89 if !ok { 90 return nil, fmt.Errorf("malformed \"%s\" section", name) 91 } 92 version = strings.TrimPrefix(version, "==") 93 94 tasks <- task{name: name, version: version} 95 taskCount++ 96 } 97 98 res := make([]artifact.Dependency, 0, len(packages)) 99 for done := 0; taskCount == 0 || done < taskCount; done++ { 100 result := <-results 101 if result.err != nil { 102 close(tasks) // signal workers to stop 103 return nil, err 104 } 105 res = append(res, artifact.Dependency{ 106 Name: result.name, 107 Version: result.version, 108 Hash: result.hash, 109 HashType: result.hashType, 110 License: result.license}) 111 } 112 113 a.Deps = res 114 return res, nil 115 }