github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/artifact/artifact.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 artifact 10 11 import ( 12 "bufio" 13 "errors" 14 "fmt" 15 "os" 16 "sort" 17 "strconv" 18 "strings" 19 20 "github.com/fatih/color" 21 22 "github.com/vchain-us/vcn/pkg/api" 23 "github.com/vchain-us/vcn/pkg/meta" 24 ) 25 26 type ColumnID uint 27 28 const ( 29 ColNameVersion ColumnID = 1 << iota 30 ColHash 31 ColTrustLevel 32 MaxColumn = iota 33 ) 34 35 type OutputOptions uint 36 37 const ( 38 Silent OutputOptions = iota 39 Progress 40 Debug 41 ) 42 43 // Artifact is a result of build process. 44 // It is a language- and/or environment-specific interface which finds dependencies 45 type Artifact interface { 46 Path() string 47 Type() string 48 Dependencies() []Dependency 49 ResolveDependencies(output OutputOptions) ([]Dependency, error) 50 } 51 52 type GenericArtifact struct { 53 Deps []Dependency 54 } 55 56 type LoadedArtifact struct { 57 GenericArtifact 58 path string 59 kind string 60 } 61 62 func (a GenericArtifact) Dependencies() []Dependency { 63 return a.Deps 64 } 65 66 func Store(a Artifact, bomFileName string) error { 67 deps := a.Dependencies() 68 f, err := os.OpenFile(bomFileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 69 if err != nil { 70 return err 71 } 72 defer f.Close() 73 74 fmt.Fprintf(f, "%s\t%s\n", a.Type(), a.Path()) 75 for _, dep := range deps { 76 _, err = fmt.Fprintf(f, "%s\t%s\t%s\t%d\t%s\t%d\n", dep.Name, dep.Version, dep.SignerID, dep.HashType, dep.Hash, dep.TrustLevel) 77 if err != nil { 78 return err 79 } 80 } 81 return nil 82 } 83 84 func Load(bomFileName string) (*LoadedArtifact, error) { 85 f, err := os.Open(bomFileName) 86 if err != nil { 87 return nil, err 88 } 89 defer f.Close() 90 91 var header []string 92 res := make([]Dependency, 0, 16) 93 scanner := bufio.NewScanner(f) 94 if scanner.Scan() { 95 header = strings.Split(scanner.Text(), "\t") 96 if len(header) != 2 { 97 return nil, errors.New("malformed BoM header") 98 } 99 } else { 100 return nil, errors.New("malformed BoM file") 101 } 102 for scanner.Scan() { 103 fields := strings.Split(scanner.Text(), "\t") 104 if len(fields) != 6 { 105 return nil, errors.New("malformed BoM record") 106 } 107 hashType, err := strconv.Atoi(fields[3]) 108 if err != nil || HashType(hashType) > maxHash { 109 return nil, errors.New("malformed BoM hashType field") 110 } 111 trustLevel, err := strconv.Atoi(fields[5]) 112 if err != nil || TrustLevel(trustLevel) > MaxTrustLevel { 113 return nil, errors.New("malformed BoM trustLevel field") 114 } 115 res = append(res, Dependency{ 116 Name: fields[0], 117 Version: fields[1], 118 SignerID: fields[2], 119 HashType: HashType(hashType), 120 Hash: fields[4], 121 TrustLevel: TrustLevel(trustLevel), 122 }) 123 } 124 125 return &LoadedArtifact{kind: header[0], path: header[1], GenericArtifact: GenericArtifact{Deps: res}}, nil 126 } 127 128 func LoadFromDb(hash string, signerID string, lcUser *api.LcUser) (*LoadedArtifact, error) { 129 ar, _, err := lcUser.LoadArtifact(hash, signerID, "", 0, nil) 130 if err != nil { 131 return nil, err 132 } 133 134 return &LoadedArtifact{kind: ar.Kind, path: ar.Name}, nil 135 } 136 137 func Display(a Artifact, columns ColumnID) { 138 deps := a.Dependencies() 139 140 maxColWidth := make([]int, MaxColumn) 141 sort.SliceStable(deps, (func(q, b int) bool { return deps[q].Name < deps[b].Name })) 142 for _, dep := range deps { 143 width := len(dep.Name) + len(dep.Version) + 1 144 if width > maxColWidth[0] { 145 maxColWidth[0] = width 146 } 147 width = len(dep.Hash) 148 if width > maxColWidth[1] { 149 maxColWidth[1] = width 150 } 151 } 152 153 for _, dep := range deps { 154 if ColNameVersion&columns != 0 { 155 fmt.Printf("%-*s", maxColWidth[0]+1, dep.Name+"@"+dep.Version) 156 } 157 if ColHash&columns != 0 { 158 fmt.Printf("%-*s", maxColWidth[1]+1, dep.Hash) 159 } 160 if ColTrustLevel&columns != 0 { 161 switch dep.TrustLevel { 162 case Trusted: 163 color.Set(meta.StyleSuccess()) 164 case Unknown: 165 color.Set(meta.StyleWarning()) 166 case Unsupported, Untrusted: 167 color.Set(meta.StyleError()) 168 } 169 fmt.Print(TrustLevelName(dep.TrustLevel)) 170 color.Unset() 171 } 172 fmt.Println() 173 } 174 } 175 176 func (a LoadedArtifact) Type() string { 177 return a.kind 178 } 179 180 func (a LoadedArtifact) Path() string { 181 return a.path 182 } 183 184 func (a LoadedArtifact) Dependencies() []Dependency { 185 return a.Deps 186 } 187 188 func (a LoadedArtifact) ResolveDependencies(output OutputOptions) ([]Dependency, error) { 189 return a.Deps, nil 190 }