mirror of
https://github.com/anchore/syft.git
synced 2026-11-03 07:56:36 +01:00
Merge pull request #94 from anchore/table-output
Add default table presenter
This commit is contained in:
@@ -6,17 +6,20 @@ const (
|
||||
UnknownPresenter Option = iota
|
||||
JSONPresenter
|
||||
TextPresenter
|
||||
TablePresenter
|
||||
)
|
||||
|
||||
var optionStr = []string{
|
||||
"UnknownPresenter",
|
||||
"json",
|
||||
"text",
|
||||
"table",
|
||||
}
|
||||
|
||||
var Options = []Option{
|
||||
JSONPresenter,
|
||||
TextPresenter,
|
||||
TablePresenter,
|
||||
}
|
||||
|
||||
type Option int
|
||||
@@ -27,6 +30,8 @@ func ParseOption(userStr string) Option {
|
||||
return JSONPresenter
|
||||
case strings.ToLower(TextPresenter.String()):
|
||||
return TextPresenter
|
||||
case strings.ToLower(TablePresenter.String()):
|
||||
return TablePresenter
|
||||
default:
|
||||
return UnknownPresenter
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/presenter/json"
|
||||
"github.com/anchore/syft/syft/presenter/table"
|
||||
"github.com/anchore/syft/syft/presenter/text"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
)
|
||||
@@ -20,6 +21,8 @@ func GetPresenter(option Option, s scope.Scope, catalog *pkg.Catalog) Presenter
|
||||
return json.NewPresenter(catalog, s)
|
||||
case TextPresenter:
|
||||
return text.NewPresenter(catalog, s)
|
||||
case TablePresenter:
|
||||
return table.NewPresenter(catalog, s)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
)
|
||||
|
||||
type Presenter struct {
|
||||
catalog *pkg.Catalog
|
||||
scope scope.Scope
|
||||
}
|
||||
|
||||
func NewPresenter(catalog *pkg.Catalog, s scope.Scope) *Presenter {
|
||||
return &Presenter{
|
||||
catalog: catalog,
|
||||
scope: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (pres *Presenter) Present(output io.Writer) error {
|
||||
rows := make([][]string, 0)
|
||||
|
||||
columns := []string{"Name", "Version", "Type"}
|
||||
for p := range pres.catalog.Enumerate() {
|
||||
row := []string{
|
||||
p.Name,
|
||||
p.Version,
|
||||
p.Type.String(),
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
// sort by name, version, then type
|
||||
sort.SliceStable(rows, func(i, j int) bool {
|
||||
for col := 0; col < len(columns); col++ {
|
||||
if rows[i][0] != rows[j][0] {
|
||||
return rows[i][col] < rows[j][col]
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
table := tablewriter.NewWriter(output)
|
||||
|
||||
table.SetHeader(columns)
|
||||
table.SetHeaderLine(false)
|
||||
table.SetBorder(false)
|
||||
table.SetAutoWrapText(false)
|
||||
table.SetAutoFormatHeaders(true)
|
||||
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetAlignment(tablewriter.ALIGN_LEFT)
|
||||
table.SetCenterSeparator("")
|
||||
table.SetColumnSeparator("")
|
||||
table.SetRowSeparator("")
|
||||
table.SetTablePadding("\t")
|
||||
table.SetNoWhiteSpace(true)
|
||||
|
||||
table.AppendBulk(rows)
|
||||
table.Render()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/go-testutils"
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/scope"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
)
|
||||
|
||||
var update = flag.Bool("update", false, "update the *.golden files for table presenters")
|
||||
|
||||
func TestTablePresenter(t *testing.T) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
testImage := "image-simple"
|
||||
|
||||
catalog := pkg.NewCatalog()
|
||||
img, cleanup := testutils.GetFixtureImage(t, "docker-archive", testImage)
|
||||
defer cleanup()
|
||||
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
Source: []file.Reference{
|
||||
*img.SquashedTree().File("/somefile-1.txt"),
|
||||
},
|
||||
Type: pkg.DebPkg,
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-2",
|
||||
Version: "2.0.1",
|
||||
Source: []file.Reference{
|
||||
*img.SquashedTree().File("/somefile-2.txt"),
|
||||
},
|
||||
Type: pkg.DebPkg,
|
||||
})
|
||||
|
||||
s, err := scope.NewScopeFromImage(img, scope.AllLayersScope)
|
||||
pres := NewPresenter(catalog, s)
|
||||
|
||||
// run presenter
|
||||
err = pres.Present(&buffer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
actual := buffer.Bytes()
|
||||
|
||||
if *update {
|
||||
testutils.UpdateGoldenFileContents(t, actual)
|
||||
}
|
||||
|
||||
var expected = testutils.GetGoldenFileContents(t)
|
||||
|
||||
if !bytes.Equal(expected, actual) {
|
||||
dmp := diffmatchpatch.New()
|
||||
diffs := dmp.DiffMain(string(actual), string(expected), true)
|
||||
t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs))
|
||||
}
|
||||
|
||||
// TODO: add me back in when there is a JSON schema
|
||||
// validateAgainstV1Schema(t, string(actual))
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Note: changes to this file will result in updating several test values. Consider making a new image fixture instead of editing this one.
|
||||
FROM scratch
|
||||
ADD file-1.txt /somefile-1.txt
|
||||
ADD file-2.txt /somefile-2.txt
|
||||
# note: adding a directory will behave differently on docker engine v18 vs v19
|
||||
ADD target /
|
||||
@@ -0,0 +1 @@
|
||||
this file has contents
|
||||
@@ -0,0 +1 @@
|
||||
file-2 contents!
|
||||
@@ -0,0 +1,2 @@
|
||||
another file!
|
||||
with lines...
|
||||
@@ -0,0 +1,3 @@
|
||||
NAME VERSION TYPE
|
||||
package-1 1.0.1 deb
|
||||
package-2 2.0.1 deb
|
||||
Reference in New Issue
Block a user