Skip to content

Commit dfb4031

Browse files
committed
Diff command demo
Signed-off-by: houdini91 <[email protected]>
1 parent 15ed6c1 commit dfb4031

File tree

6 files changed

+188
-2
lines changed

6 files changed

+188
-2
lines changed

cmd/cli/convert.go

+20
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,26 @@ func parseFormat(f, e string, r io.ReadSeekCloser) (*format.Format, error) {
131131
return format, nil
132132
}
133133

134+
// parseFormat parses the format string and returns target format
135+
// if format string is empty, it will try to detect the format automatically and return the detected one
136+
func parseFormatNoInverse(f, e string, r io.ReadSeekCloser) (*format.Format, error) {
137+
if f == "" {
138+
df, err := format.Detect(r)
139+
if err != nil {
140+
return nil, err
141+
}
142+
143+
return df, nil
144+
}
145+
146+
format, err := format.Parse(f, e)
147+
if err != nil {
148+
return nil, err
149+
}
150+
151+
return format, nil
152+
}
153+
134154
func createOutputStream(out string, frmt *format.Format) (io.WriteCloser, *string, error) {
135155
log := zap.S()
136156

cmd/cli/diff.go

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package cli
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/spf13/viper"
10+
"go.uber.org/zap"
11+
12+
"github.com/bom-squad/sbom-convert/cmd/cli/options"
13+
"github.com/bom-squad/sbom-convert/pkg/diff"
14+
"github.com/bom-squad/sbom-convert/pkg/format"
15+
)
16+
17+
func DiffCommand() *cobra.Command {
18+
co := &options.DiffOptions{}
19+
c := &cobra.Command{
20+
Use: "diff",
21+
Aliases: []string{"df"},
22+
SuggestFor: []string{"diff"},
23+
Short: "Diff between two SBOMS, agnostic to format CycloneDX and SPDX SBOMs",
24+
Long: `Diff between two SBOMS, agonstic to format CycloneDX and SPDX.`,
25+
Example: `
26+
sbom-diff diff sbom_1.cdx.json sbom_2.cdx.json output to stdout in inverse format
27+
sbom-diff diff sbom_1.spdx.json sbom_2.cdx.json -o sbom.cdx.json output to a file
28+
sbom-diff diff sbom_1.cdx.json sbom_2.cdx.json -f spdx-2.3 select to a specific format
29+
sbom-diff diff sbom_1.cdx.json sbom_2.cdx.json -f spdx -e text select specific encoding
30+
`,
31+
SilenceErrors: true,
32+
SilenceUsage: true,
33+
DisableAutoGenTag: true,
34+
Args: cobra.ExactArgs(2),
35+
RunE: func(cmd *cobra.Command, args []string) error {
36+
return runDiff(cmd.Context(), co, args)
37+
},
38+
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
39+
if err := cmd.Parent().PersistentPreRunE(cmd.Parent(), args); err != nil {
40+
return err
41+
}
42+
43+
if err := options.BindConfig(viper.GetViper(), cmd); err != nil {
44+
return err
45+
}
46+
47+
return validateDiffOptions(co, args)
48+
},
49+
}
50+
51+
co.AddFlags(c)
52+
53+
return c
54+
}
55+
56+
func validateDiffOptions(_ *options.DiffOptions, _ []string) error {
57+
return nil
58+
}
59+
60+
// runDiff is the main entrypoint for the `diff` command
61+
func runDiff(ctx context.Context, co *options.DiffOptions, args []string) error {
62+
log := zap.S()
63+
log.Info("Running Diff command ...")
64+
path1 := args[0]
65+
path2 := args[1]
66+
67+
f1, err := os.Open(path1)
68+
if err != nil {
69+
return err
70+
}
71+
defer f1.Close()
72+
73+
f2, err := os.Open(path2)
74+
if err != nil {
75+
return err
76+
}
77+
defer f2.Close()
78+
79+
output_frmt, err := parseFormatNoInverse(co.Format, co.Encoding, f1)
80+
if err != nil {
81+
return err
82+
}
83+
log.Debugf("Output format %s", output_frmt)
84+
85+
df1, err := format.Detect(f1)
86+
if err != nil {
87+
return err
88+
}
89+
90+
log.Debugf("First '%s' format '%s'", path1, df1)
91+
92+
df2, err := format.Detect(f1)
93+
if err != nil {
94+
return err
95+
}
96+
log.Debugf("Second '%s' format '%s'", path2, df2)
97+
98+
overwrited := true
99+
out1, outPath1, err := createOutputStream(fmt.Sprintf("%s.added", co.OutputPath), output_frmt)
100+
if err != nil {
101+
return err
102+
}
103+
out2, outPath2, err := createOutputStream(fmt.Sprintf("%s.removed", co.OutputPath), output_frmt)
104+
if err != nil {
105+
return err
106+
}
107+
108+
dfs := diff.NewService(
109+
diff.WithFormat(output_frmt),
110+
)
111+
112+
if err := dfs.Diff(ctx, f1, f2, out1, out2); err != nil {
113+
out1.Close()
114+
out2.Close()
115+
116+
if !overwrited {
117+
return err
118+
}
119+
120+
log.Debugf("removing output file %s", *outPath1)
121+
if rerr := os.Remove(*outPath1); rerr != nil {
122+
log.Info("failed to remove output file", zap.String("path", *outPath1), zap.Error(rerr))
123+
}
124+
log.Debugf("removing output file %s", *outPath2)
125+
if rerr := os.Remove(*outPath2); rerr != nil {
126+
log.Info("failed to remove output file", zap.String("path", *outPath2), zap.Error(rerr))
127+
}
128+
129+
return err
130+
}
131+
132+
return nil
133+
}

cmd/cli/options/diff.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package options
2+
3+
import (
4+
"github.com/spf13/cobra"
5+
)
6+
7+
// ConvertOptions defines the options for the `convert` command
8+
type DiffOptions struct {
9+
Format string
10+
Encoding string
11+
OutputPath string
12+
}
13+
14+
// AddFlags adds command line flags for the ConvertOptions struct
15+
func (o *DiffOptions) AddFlags(cmd *cobra.Command) {
16+
cmd.Flags().StringVarP(&o.Format, "format", "f", "", "the output format [spdx, spdx-2.3, cyclonedx, cyclonedx-1.0, cyclonedx-1.1, cyclonedx-1.2, cyclonedx-1.3, cyclonedx-1.4, cyclonedx-1.5]") //nolint: lll
17+
cmd.Flags().StringVarP(&o.Encoding, "encoding", "e", "json", "the output encoding [spdx: [text, json] cyclonedx: [json]")
18+
cmd.Flags().StringVarP(&o.OutputPath, "output", "o", "", "path to write the diff SBOM")
19+
}

cmd/cli/root.go

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ func NewRootCmd() *cobra.Command {
7474
cvtCmd := ConvertCommand()
7575
rootCmd.AddCommand(cvtCmd)
7676

77+
// Commands
78+
diffCmd := DiffCommand()
79+
rootCmd.AddCommand(diffCmd)
80+
7781
// Manpages
7882
rootCmd.AddCommand(ManCommand(rootCmd))
7983
return rootCmd

go.mod

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/google/go-cmp v0.6.0
88
github.com/muesli/mango-cobra v1.2.0
99
github.com/muesli/roff v0.1.0
10+
github.com/olekukonko/tablewriter v0.0.5
1011
github.com/spf13/cobra v1.8.0
1112
github.com/spf13/pflag v1.0.5
1213
github.com/spf13/viper v1.18.2
@@ -23,6 +24,7 @@ require (
2324
github.com/hashicorp/hcl v1.0.0 // indirect
2425
github.com/inconshreveable/mousetrap v1.1.0 // indirect
2526
github.com/magiconair/properties v1.8.7 // indirect
27+
github.com/mattn/go-runewidth v0.0.9 // indirect
2628
github.com/mitchellh/mapstructure v1.5.0 // indirect
2729
github.com/muesli/mango v0.1.0 // indirect
2830
github.com/muesli/mango-pflag v0.1.0 // indirect
@@ -44,3 +46,7 @@ require (
4446
gopkg.in/yaml.v3 v3.0.1 // indirect
4547
sigs.k8s.io/release-utils v0.7.7 // indirect
4648
)
49+
50+
replace github.com/bom-squad/protobom v0.3.0 => github.com/scribe-security/protobom v0.0.0-20240225125754-709fd9fb85bb
51+
52+
// replace github.com/bom-squad/protobom => ../protobom

go.sum

+6-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ github.com/CycloneDX/cyclonedx-go v0.8.0/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7B
33
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
44
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9 h1:6COpXWpHbhWM1wgcQN95TdsmrLTba8KQfPgImBXzkjA=
55
github.com/anchore/go-struct-converter v0.0.0-20230627203149-c72ef8859ca9/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
6-
github.com/bom-squad/protobom v0.3.0 h1:1kdKbTmnhigxCQK3f0BJZWeevE+Z0bSGy0o76CFm0Ak=
7-
github.com/bom-squad/protobom v0.3.0/go.mod h1:IhdpGUnU5LTI5E7blHlGY9SDwJewK0xZd/YdW+ESKY0=
86
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
97
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
108
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
@@ -33,6 +31,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
3331
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
3432
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
3533
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
34+
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
35+
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
3636
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
3737
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
3838
github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
@@ -43,6 +43,8 @@ github.com/muesli/mango-pflag v0.1.0 h1:UADqbYgpUyRoBja3g6LUL+3LErjpsOwaC9ywvBWe
4343
github.com/muesli/mango-pflag v0.1.0/go.mod h1:YEQomTxaCUp8PrbhFh10UfbhbQrM/xJ4i2PB8VTLLW0=
4444
github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8=
4545
github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig=
46+
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
47+
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
4648
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
4749
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
4850
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -55,6 +57,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
5557
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
5658
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
5759
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
60+
github.com/scribe-security/protobom v0.0.0-20240225125754-709fd9fb85bb h1:L4+zRL/AuN+kK/ZKW/sJSCE4aKrO4qAWagd/sVMcYA8=
61+
github.com/scribe-security/protobom v0.0.0-20240225125754-709fd9fb85bb/go.mod h1:zv9Q1atQgfwhsaW4crHLQdsKTYJ6mlWwe9k4VMdP9+g=
5862
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
5963
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
6064
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=

0 commit comments

Comments
 (0)