add template output (#1051)

* add template output

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* remove dead code

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* fix template cli flag

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* implement template's own format type

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* simpler code

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* fix readme link to Go template

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* feedback changes

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* simpler func signature patter

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* nit

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* fix linter error

Signed-off-by: Jonas Xavier <jonasx@anchore.com>
This commit is contained in:
Jonas Xavier
2022-06-17 14:04:31 -04:00
committed by GitHub
parent 03e37044d4
commit aed1599c4d
20 changed files with 253 additions and 15 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ import (
func Run(ctx context.Context, app *config.Application, args []string) error {
log.Warn("convert is an experimental feature, run `syft convert -h` for help")
writer, err := options.MakeWriter(app.Outputs, app.File)
writer, err := options.MakeWriter(app.Outputs, app.File, "")
if err != nil {
return err
}
+8
View File
@@ -15,6 +15,7 @@ import (
type PackagesOptions struct {
Scope string
Output []string
OutputTemplatePath string
File string
Platform string
Host string
@@ -35,6 +36,9 @@ func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
cmd.PersistentFlags().StringArrayVarP(&o.Output, "output", "o", FormatAliases(table.ID),
fmt.Sprintf("report output format, options=%v", FormatAliases(syft.FormatIDs()...)))
cmd.PersistentFlags().StringVarP(&o.OutputTemplatePath, "template", "t", "",
"specify the path to a Go template file")
cmd.PersistentFlags().StringVarP(&o.File, "file", "", "",
"file to write the default report output to (default is STDOUT)")
@@ -84,6 +88,10 @@ func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
return err
}
if err := v.BindPFlag("output-template-path", flags.Lookup("template")); err != nil {
return err
}
if err := v.BindPFlag("platform", flags.Lookup("platform")); err != nil {
return err
}
+9 -3
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/anchore/syft/internal/formats/table"
"github.com/anchore/syft/internal/formats/template"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/sbom"
"github.com/hashicorp/go-multierror"
@@ -12,8 +13,8 @@ import (
// makeWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer
// or an error but neither both and if there is no error, sbom.Writer.Close() should be called
func MakeWriter(outputs []string, defaultFile string) (sbom.Writer, error) {
outputOptions, err := parseOutputs(outputs, defaultFile)
func MakeWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Writer, error) {
outputOptions, err := parseOutputs(outputs, defaultFile, templateFilePath)
if err != nil {
return nil, err
}
@@ -27,7 +28,7 @@ func MakeWriter(outputs []string, defaultFile string) (sbom.Writer, error) {
}
// parseOptions utility to parse command-line option strings and retain the existing behavior of default format and file
func parseOutputs(outputs []string, defaultFile string) (out []sbom.WriterOption, errs error) {
func parseOutputs(outputs []string, defaultFile, templateFilePath string) (out []sbom.WriterOption, errs error) {
// always should have one option -- we generally get the default of "table", but just make sure
if len(outputs) == 0 {
outputs = append(outputs, string(table.ID))
@@ -56,6 +57,11 @@ func parseOutputs(outputs []string, defaultFile string) (out []sbom.WriterOption
continue
}
if tmpl, ok := format.(template.OutputFormat); ok {
tmpl.SetTemplatePath(templateFilePath)
format = tmpl
}
out = append(out, sbom.NewWriterOption(format, file))
}
return out, errs
+1 -1
View File
@@ -28,7 +28,7 @@ func TestIsSupportedFormat(t *testing.T) {
}
for _, tt := range tests {
_, err := MakeWriter(tt.outputs, "")
_, err := MakeWriter(tt.outputs, "", "")
tt.wantErr(t, err)
}
}
+7 -6
View File
@@ -14,12 +14,13 @@ import (
const (
packagesExample = ` {{.appName}} {{.command}} alpine:latest a summary of discovered packages
{{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details
{{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM
{{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.2 Tag-Value formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.2 JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -vv show verbose debug information
{{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details
{{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM
{{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.2 Tag-Value formatted SBOM
{{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.2 JSON formatted SBOM
{{.appName}} {{.command}} alpine:latest -vv show verbose debug information
{{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file
Supports the following image sources:
{{.appName}} {{.command}} yourrepo/yourimage:tag defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry.
+23 -1
View File
@@ -13,6 +13,7 @@ import (
"github.com/anchore/syft/internal/anchore"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/formats/template"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/internal/version"
@@ -25,7 +26,12 @@ import (
)
func Run(ctx context.Context, app *config.Application, args []string) error {
writer, err := options.MakeWriter(app.Outputs, app.File)
err := validateOutputOptions(app)
if err != nil {
return err
}
writer, err := options.MakeWriter(app.Outputs, app.File, app.OutputTemplatePath)
if err != nil {
return err
}
@@ -185,3 +191,19 @@ func runPackageSbomUpload(src *source.Source, s sbom.SBOM, app *config.Applicati
return nil
}
func validateOutputOptions(app *config.Application) error {
var usesTemplateOutput bool
for _, o := range app.Outputs {
if o == template.ID.String() {
usesTemplateOutput = true
break
}
}
if usesTemplateOutput && app.OutputTemplatePath == "" {
return fmt.Errorf(`must specify path to template file when using "template" output format`)
}
return nil
}
+1 -1
View File
@@ -13,7 +13,7 @@ import (
const powerUserExample = ` {{.appName}} {{.command}} <image>
DEPRECATED - THIS COMMAND WILL BE REMOVED in v1.0.0
Only image sources are supported (e.g. docker: , podman: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported.
Only image sources are supported (e.g. docker: , podman: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported, template outputs are not supported.
All behavior is controlled via application configuration and environment variables (see https://github.com/anchore/syft#configuration)
`