Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enhance list command json output #34

Merged
merged 4 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading