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  }