Skip to content

Commit

Permalink
feat: Enhance list command json output (#34)
Browse files Browse the repository at this point in the history
---------

Signed-off-by: Christopher Phillips <[email protected]>
  • Loading branch information
spiffcs authored Jan 24, 2024
1 parent 079b567 commit 2b8cd0e
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 69 deletions.
57 changes: 8 additions & 49 deletions cmd/grant/cli/internal/check/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,52 +92,11 @@ type Response struct {
}

type Evaluation struct {
Input string `json:"input" yaml:"input"`
License License `json:"license" yaml:"license"`
Package Package `json:"package" yaml:"package"`
Passed bool `json:"passed" yaml:"passed"`
Reasons []string `json:"reasons" yaml:"reasons"`
}

type License struct {
SPDXExpression string `json:"spdx_expression" yaml:"spdx_expression"`
Name string `json:"name" yaml:"name"`
Locations []string `json:"locations" yaml:"locations"`
Reference string `json:"reference" yaml:"reference"`
IsDeprecated bool `json:"is_deprecated" yaml:"is_deprecated"`
LicenseID string `json:"license_id" yaml:"license_id"`
SeeAlso []string `json:"see_also" yaml:"see_also"`
IsOsiApproved bool `json:"is_osi_approved" yaml:"is_osi_approved"`
}

func newLicense(l grant.License) License {
return License{
SPDXExpression: l.SPDXExpression,
Name: l.Name,
Locations: l.Locations,
Reference: l.Reference,
IsDeprecated: l.IsDeprecatedLicenseID,
LicenseID: l.LicenseID,
SeeAlso: l.SeeAlso,
IsOsiApproved: l.IsOsiApproved,
}
}

type Package struct {
Name string `json:"name" yaml:"name"`
Version string `json:"version" yaml:"version"`
Locations []string `json:"locations" yaml:"locations"`
}

func newPackage(p *grant.Package) Package {
if p == nil {
return Package{}
}
return Package{
Name: p.Name,
Version: p.Version,
Locations: p.Locations,
}
Input string `json:"input" yaml:"input"`
License internal.License `json:"license" yaml:"license"`
Package internal.Package `json:"package" yaml:"package"`
Passed bool `json:"passed" yaml:"passed"`
Reasons []string `json:"reasons" yaml:"reasons"`
}

func NewEvaluation(input string, le evalutation.LicenseEvaluation) Evaluation {
Expand All @@ -151,10 +110,10 @@ func NewEvaluation(input string, le evalutation.LicenseEvaluation) Evaluation {
reasons = append(reasons, details)
}

license := newLicense(le.License)
var pkg Package
license := internal.NewLicense(le.License)
var pkg internal.Package
if le.Package != nil {
pkg = newPackage(le.Package)
pkg = internal.NewPackage(le.Package)
}

re := Evaluation{
Expand Down
55 changes: 54 additions & 1 deletion cmd/grant/cli/internal/format.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package internal

import "github.com/google/uuid"
import (
"github.com/google/uuid"

"github.com/anchore/grant/grant"
)

type Format string

Expand All @@ -26,3 +30,52 @@ func ValidateFormat(f Format) Format {
func NewReportID() string {
return uuid.Must(uuid.NewRandom()).String()
}

type License struct {
SPDXExpression string `json:"spdx_expression" yaml:"spdx_expression"`
Name string `json:"name" yaml:"name"`
Locations []string `json:"locations" yaml:"locations"`
Reference string `json:"reference" yaml:"reference"`
IsDeprecated bool `json:"is_deprecated" yaml:"is_deprecated"`
LicenseID string `json:"license_id" yaml:"license_id"`
SeeAlso []string `json:"see_also" yaml:"see_also"`
IsOsiApproved bool `json:"is_osi_approved" yaml:"is_osi_approved"`
}

func NewLicense(l grant.License) License {
return License{
SPDXExpression: l.SPDXExpression,
Name: l.Name,
Locations: l.Locations,
Reference: l.Reference,
IsDeprecated: l.IsDeprecatedLicenseID,
LicenseID: l.LicenseID,
SeeAlso: l.SeeAlso,
IsOsiApproved: l.IsOsiApproved,
}
}

type Package struct {
Name string `json:"name" yaml:"name"`
Version string `json:"version" yaml:"version"`
Locations []string `json:"locations" yaml:"locations"`
}

func NewPackage(p *grant.Package) Package {
if p == nil {
return Package{}
}
return Package{
Name: p.Name,
Version: p.Version,
Locations: p.Locations,
}
}

func NewPackages(pkgs ...*grant.Package) []Package {
packages := make([]Package, 0)
for _, p := range pkgs {
packages = append(packages, NewPackage(p))
}
return packages
}
44 changes: 31 additions & 13 deletions cmd/grant/cli/internal/list/report.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"slices"
"time"

"github.com/gookit/color"
Expand Down Expand Up @@ -68,16 +69,18 @@ type Response struct {
}

type Result struct {
Input string `json:"input" yaml:"input"`
License string `json:"license" yaml:"license"`
Package string `json:"package" yaml:"package"`
Input string `json:"input" yaml:"input"`
License internal.License `json:"license" yaml:"license"`
Packages []internal.Package `json:"packages" yaml:"packages"`
}

func NewResult(input, license, lp string) Result {
func NewResult(input string, gl grant.License, gp ...*grant.Package) Result {
rl := internal.NewLicense(gl)
pkgs := internal.NewPackages(gp...)
return Result{
Input: input,
License: license,
Package: lp,
Input: input,
License: rl,
Packages: pkgs,
}
}

Expand All @@ -91,9 +94,12 @@ func (r *Report) renderJSON() error {

for _, c := range r.Cases {
resp.Inputs = append(resp.Inputs, c.UserInput)
licenses := c.GetLicenses()
for _, l := range licenses {
resp.Results = append(resp.Results, NewResult(c.UserInput, l.Name, ""))
// TODO: is it better to invert this here and grab packages -> licenses since package is the cases first class
licensePackages, licenses, _ := c.GetLicenses()
for key, l := range licenses {
packages := licensePackages[key]
result := NewResult(c.UserInput, l, packages...)
resp.Results = append(resp.Results, result)
}
}
jsonData, err := json.Marshal(resp)
Expand All @@ -110,13 +116,25 @@ func (r *Report) renderList() error {
for _, c := range r.Cases {
r.Monitor.Increment()
r.Monitor.AtomicStage.Set(c.UserInput)
unsortedLicenses := make([]string, 0)
resultList := list.NewWriter()
uiLists = append(uiLists, resultList)
resultList.AppendItem(color.Primary.Sprintf("%s", c.UserInput))
licenses := c.GetLicenses()
_, licenses, _ := c.GetLicenses()
for _, license := range licenses {
if license.IsSPDX() {
unsortedLicenses = append(unsortedLicenses, license.SPDXExpression)
continue
}
unsortedLicenses = append(unsortedLicenses, license.Name)
}

// sort for list output
slices.Sort(unsortedLicenses)

resultList.Indent()
for _, l := range licenses {
resultList.AppendItem(l.Name)
for _, license := range unsortedLicenses {
resultList.AppendItem(license)
}
resultList.UnIndent()
}
Expand Down
43 changes: 37 additions & 6 deletions grant/case.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,52 @@ func NewCases(userInputs ...string) []Case {
return cases
}

func (c Case) GetLicenses() []License {
licenses := make([]License, 0)
type Pair struct {
License License
Package *Package
}

func (c Case) GetLicenses() (map[string][]*Package, map[string]License, []Package) {
licensePackages := make(map[string][]*Package)
licenses := make(map[string]License)
packagesNoLicenses := make([]Package, 0)
for _, sb := range c.SBOMS {
for pkg := range sb.Artifacts.Packages.Enumerate() {
grantPkg := ConvertSyftPackage(pkg)
// TODO: how do we express packages without licenses in list
if len(grantPkg.Licenses) == 0 {
packagesNoLicenses = append(packagesNoLicenses, *grantPkg)
continue
}

licenses = append(licenses, grantPkg.Licenses...)
buildLicenseMaps(licensePackages, licenses, grantPkg)
}
}

licenses = append(licenses, c.Licenses...)
return licenses
return licensePackages, licenses, packagesNoLicenses
}

func buildLicenseMaps(licensePackages map[string][]*Package, licenses map[string]License, pkg *Package) {
for _, license := range pkg.Licenses {
if license.IsSPDX() {
if _, ok := licenses[license.SPDXExpression]; !ok {
licenses[license.SPDXExpression] = license
}
if _, ok := licensePackages[license.SPDXExpression]; !ok {
licensePackages[license.SPDXExpression] = make([]*Package, 0)
}
licensePackages[license.SPDXExpression] = append(licensePackages[license.SPDXExpression], pkg)
continue
}

// NonSPDX License
if _, ok := licenses[license.Name]; !ok {
licenses[license.Name] = license
}
if _, ok := licensePackages[license.Name]; !ok {
licensePackages[license.Name] = make([]*Package, 0)
}
licensePackages[license.Name] = append(licensePackages[license.Name], pkg)
}
}

type CaseHandler struct {
Expand Down

0 comments on commit 2b8cd0e

Please sign in to comment.