Skip to content

Commit b93fc5b

Browse files
authored
refactor: cleanup and cosmetics (#81)
* cleanup and cosmetics Signed-off-by: nscuro <nscuro@protonmail.com> * ensure forward slashes for application subpaths Signed-off-by: nscuro <nscuro@protonmail.com>
1 parent 6276d83 commit b93fc5b

18 files changed

Lines changed: 112 additions & 94 deletions

File tree

internal/cli/cmd/app/app.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ Examples:
8686
options.ModuleDir = args[0]
8787
}
8888

89-
cliUtil.ConfigureLogger(options.LogOptions)
89+
options.LogOptions.ConfigureLogger()
9090

9191
return Exec(options)
9292
},
@@ -173,7 +173,7 @@ func Exec(options Options) error {
173173
enrichWithApplicationDetails(bom, options.ModuleDir, options.Main)
174174

175175
if options.IncludeStd {
176-
err = cliUtil.AddStdComponent(bom)
176+
err = cliUtil.AddStdComponent(bom, "")
177177
if err != nil {
178178
return fmt.Errorf("failed to add stdlib component: %w", err)
179179
}
@@ -270,7 +270,7 @@ func enrichWithApplicationDetails(bom *cdx.BOM, moduleDir, mainPkgDir string) {
270270
mainPkgDirRel = strings.TrimSuffix(mainPkgDirRel, string(os.PathSeparator))
271271

272272
oldPURL := bom.Metadata.Component.PackageURL
273-
newPURL := oldPURL + "#" + mainPkgDirRel
273+
newPURL := oldPURL + "#" + filepath.ToSlash(mainPkgDirRel)
274274

275275
log.Debug().
276276
Str("old", oldPURL).

internal/cli/cmd/bin/bin.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func New() *ffcli.Command {
4545
ShortUsage: "cyclonedx-gomod bin [FLAGS...] BINARY_PATH",
4646
LongHelp: `Generate SBOMs for binaries.
4747
48-
Although the binary is never executed, it must be executable.
48+
Although the binary is never executed by cyclonedx-gomod, it must be executable.
4949
This is a requirement by the "go version -m" command that is used to provide this functionality.
5050
5151
When license detection is enabled, all modules (including the main module)
@@ -68,7 +68,7 @@ Example:
6868
options.BinaryPath = args[0]
6969
}
7070

71-
cliUtil.ConfigureLogger(options.LogOptions)
71+
options.LogOptions.ConfigureLogger()
7272

7373
return Exec(options)
7474
},
@@ -81,7 +81,7 @@ func Exec(options Options) error {
8181
return err
8282
}
8383

84-
modules, hashes, err := gomod.LoadModulesFromBinary(options.BinaryPath)
84+
goVersion, modules, hashes, err := gomod.LoadModulesFromBinary(options.BinaryPath)
8585
if err != nil {
8686
return fmt.Errorf("failed to extract modules: %w", err)
8787
} else if len(modules) == 0 {
@@ -149,6 +149,13 @@ func Exec(options Options) error {
149149
bom.Dependencies = &dependencyGraph
150150
bom.Compositions = createCompositions(*mainComponent, components)
151151

152+
if options.IncludeStd {
153+
err = cliUtil.AddStdComponent(bom, goVersion)
154+
if err != nil {
155+
return fmt.Errorf("failed to add stdlib component: %w", err)
156+
}
157+
}
158+
152159
return cliUtil.WriteBOM(bom, options.OutputOptions)
153160
}
154161

internal/cli/cmd/mod/mod.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Examples:
6060
options.ModuleDir = args[0]
6161
}
6262

63-
cliUtil.ConfigureLogger(options.LogOptions)
63+
options.LogOptions.ConfigureLogger()
6464

6565
return Exec(options)
6666
},
@@ -142,7 +142,7 @@ func Exec(options Options) error {
142142
bom.Dependencies = &dependencyGraph
143143

144144
if options.IncludeStd {
145-
err = cliUtil.AddStdComponent(bom)
145+
err = cliUtil.AddStdComponent(bom, "")
146146
if err != nil {
147147
return fmt.Errorf("failed to add stdlib component: %w", err)
148148
}

internal/cli/cmd/mod/options.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ func (m Options) Validate() error {
8585
for i := range allowedComponentTypes {
8686
allowed[i] = string(allowedComponentTypes[i])
8787
}
88-
89-
errs = append(errs, fmt.Errorf("invalid component type: \"%s\" (allowed: %s)", m.ComponentType, strings.Join(allowed, ",")))
88+
errs = append(errs, fmt.Errorf("component type: \"%s\" is invalid (allowed: %s)", m.ComponentType, strings.Join(allowed, ",")))
9089
}
9190

9291
if len(errs) > 0 {

internal/cli/cmd/mod/options_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ func TestModOptions_Validate(t *testing.T) {
3636
require.ErrorAs(t, err, &validationError)
3737

3838
require.Len(t, validationError.Errors, 1)
39-
require.Contains(t, validationError.Errors[0].Error(), "invalid component type")
39+
require.Contains(t, validationError.Errors[0].Error(), "component type: \"foobar\" is invalid")
4040
})
4141
}

internal/cli/options/options.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ package options
2020
import (
2121
"flag"
2222
"fmt"
23+
"github.com/rs/zerolog"
24+
"github.com/rs/zerolog/log"
25+
"os"
2326

2427
"github.com/google/uuid"
2528
)
@@ -44,6 +47,20 @@ type LogOptions struct {
4447
Verbose bool
4548
}
4649

50+
// ConfigureLogger configures the global logger according to LogOptions.
51+
func (l LogOptions) ConfigureLogger() {
52+
log.Logger = log.Output(zerolog.ConsoleWriter{
53+
Out: os.Stderr,
54+
NoColor: os.Getenv("CI") != "",
55+
})
56+
57+
if l.Verbose {
58+
zerolog.SetGlobalLevel(zerolog.DebugLevel)
59+
} else {
60+
zerolog.SetGlobalLevel(zerolog.InfoLevel)
61+
}
62+
}
63+
4764
func (l *LogOptions) RegisterFlags(fs *flag.FlagSet) {
4865
fs.BoolVar(&l.Verbose, "verbose", false, "Enable verbose output")
4966
}
@@ -71,15 +88,18 @@ func (o OutputOptions) Validate() error {
7188
type SBOMOptions struct {
7289
IncludeStd bool
7390
NoSerialNumber bool
74-
Reproducible bool // Make the SBOM reproducible by omitting dynamic content
7591
ResolveLicenses bool
7692
SerialNumber string
93+
94+
// Make SBOM reproducible by omitting dynamic content.
95+
// Only used internally for testing.
96+
Reproducible bool
7797
}
7898

7999
func (s *SBOMOptions) RegisterFlags(fs *flag.FlagSet) {
80100
fs.BoolVar(&s.IncludeStd, "std", false, "Include Go standard library as component and dependency of the module")
81101
fs.BoolVar(&s.NoSerialNumber, "noserial", false, "Omit serial number")
82-
// .Reproducible is used for testing only and intentionally omitted here
102+
// Reproducible is used for testing only and intentionally omitted here
83103
fs.BoolVar(&s.ResolveLicenses, "licenses", false, "Perform license detection")
84104
fs.StringVar(&s.SerialNumber, "serial", "", "Serial number")
85105
}

internal/cli/util/util.go

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"github.com/CycloneDX/cyclonedx-gomod/internal/cli/options"
2828
"github.com/CycloneDX/cyclonedx-gomod/internal/sbom"
2929
"github.com/google/uuid"
30-
"github.com/rs/zerolog"
3130
"github.com/rs/zerolog/log"
3231
)
3332

@@ -51,15 +50,16 @@ func AddCommonMetadata(bom *cdx.BOM, sbomOptions options.SBOMOptions) error {
5150
return nil
5251
}
5352

54-
func AddStdComponent(bom *cdx.BOM) error {
53+
func AddStdComponent(bom *cdx.BOM, goVersion string) error {
5554
log.Debug().
5655
Msg("adding std component")
5756

58-
stdComponent, err := sbom.BuildStdComponent()
57+
stdComponent, err := sbom.BuildStdComponent(goVersion)
5958
if err != nil {
6059
return fmt.Errorf("failed to build std component: %w", err)
6160
}
6261

62+
// Append std to components
6363
*bom.Components = append(*bom.Components, *stdComponent)
6464

6565
// Add std to dependency graph
@@ -81,21 +81,7 @@ func AddStdComponent(bom *cdx.BOM) error {
8181
return nil
8282
}
8383

84-
func ConfigureLogger(logOptions options.LogOptions) {
85-
log.Logger = log.Output(zerolog.ConsoleWriter{
86-
Out: os.Stderr,
87-
NoColor: os.Getenv("CI") != "",
88-
})
89-
90-
if logOptions.Verbose {
91-
zerolog.SetGlobalLevel(zerolog.DebugLevel)
92-
} else {
93-
zerolog.SetGlobalLevel(zerolog.InfoLevel)
94-
}
95-
}
96-
97-
// SetSerialNumber sets the serial number of a given BOM according to the
98-
// provided SBOMOptions.
84+
// SetSerialNumber sets the serial number of a given BOM according to the provided SBOMOptions.
9985
func SetSerialNumber(bom *cdx.BOM, sbomOptions options.SBOMOptions) error {
10086
if sbomOptions.NoSerialNumber {
10187
return nil

internal/gocmd/gocmd.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func GetVersion() (string, error) {
5151
}
5252

5353
// GetEnv executes `go env -json` and returns the result as a map.
54+
// See https://pkg.go.dev/cmd/go#hdr-Print_Go_environment_information.
5455
func GetEnv() (map[string]string, error) {
5556
buf := new(bytes.Buffer)
5657
err := executeGoCommand([]string{"env", "-json"}, withStdout(buf))
@@ -67,9 +68,9 @@ func GetEnv() (map[string]string, error) {
6768
return env, nil
6869
}
6970

70-
// GetModule executes `go list -json -m` and writes the output to a given writer.
71+
// ListModule executes `go list -json -m` and writes the output to a given writer.
7172
// See https://golang.org/ref/mod#go-list-m
72-
func GetModule(moduleDir string, writer io.Writer) error {
73+
func ListModule(moduleDir string, writer io.Writer) error {
7374
return executeGoCommand([]string{"list", "-mod", "readonly", "-json", "-m"}, withDir(moduleDir), withStdout(writer))
7475
}
7576

@@ -80,6 +81,7 @@ func ListModules(moduleDir string, writer io.Writer) error {
8081
}
8182

8283
// ListPackage executes `go list -json -e <PATTERN>` and writes the output to a given writer.
84+
// See // See https://golang.org/cmd/go/#hdr-List_packages_or_modules.
8385
func ListPackage(moduleDir, packagePattern string, writer io.Writer) error {
8486
return executeGoCommand([]string{"list", "-json", "-e", packagePattern},
8587
withDir(moduleDir),
@@ -88,7 +90,7 @@ func ListPackage(moduleDir, packagePattern string, writer io.Writer) error {
8890
}
8991

9092
// ListPackages executes `go list -deps -json <PATTERN>` and writes the output to a given writer.
91-
// See https://golang.org/cmd/go/#hdr-List_packages_or_modules
93+
// See https://golang.org/cmd/go/#hdr-List_packages_or_modules.
9294
func ListPackages(moduleDir, packagePattern string, writer io.Writer) error {
9395
return executeGoCommand([]string{"list", "-deps", "-json", packagePattern},
9496
withDir(moduleDir),
@@ -98,19 +100,19 @@ func ListPackages(moduleDir, packagePattern string, writer io.Writer) error {
98100
}
99101

100102
// ListVendoredModules executes `go mod vendor -v` and writes the output to a given writer.
101-
// See https://golang.org/ref/mod#go-mod-vendor
103+
// See https://golang.org/ref/mod#go-mod-vendor.
102104
func ListVendoredModules(moduleDir string, writer io.Writer) error {
103105
return executeGoCommand([]string{"mod", "vendor", "-v", "-e"}, withDir(moduleDir), withStderr(writer))
104106
}
105107

106108
// GetModuleGraph executes `go mod graph` and writes the output to a given writer.
107-
// See https://golang.org/ref/mod#go-mod-graph
109+
// See https://golang.org/ref/mod#go-mod-graph.
108110
func GetModuleGraph(moduleDir string, writer io.Writer) error {
109111
return executeGoCommand([]string{"mod", "graph"}, withDir(moduleDir), withStdout(writer))
110112
}
111113

112114
// ModWhy executes `go mod why -m -vendor` and writes the output to a given writer.
113-
// See https://golang.org/ref/mod#go-mod-why
115+
// See https://golang.org/ref/mod#go-mod-why.
114116
func ModWhy(moduleDir string, modules []string, writer io.Writer) error {
115117
return executeGoCommand(
116118
append([]string{"mod", "why", "-m", "-vendor"}, modules...),
@@ -121,11 +123,13 @@ func ModWhy(moduleDir string, modules []string, writer io.Writer) error {
121123
}
122124

123125
// LoadModulesFromBinary executes `go version -m` and writes the output to a given writer.
126+
// See https://golang.org/ref/mod#go-version-m.
124127
func LoadModulesFromBinary(binaryPath string, writer io.Writer) error {
125128
return executeGoCommand([]string{"version", "-m", binaryPath}, withStdout(writer))
126129
}
127130

128131
// DownloadModules executes `go mod download -json` and writes the output to the given writers.
132+
// See https://golang.org/ref/mod#go-mod-download.
129133
func DownloadModules(modules []string, stdout, stderr io.Writer) error {
130134
return executeGoCommand(
131135
append([]string{"mod", "download", "-json"}, modules...),
@@ -167,5 +171,10 @@ func executeGoCommand(args []string, options ...commandOption) error {
167171
Str("dir", cmd.Dir).
168172
Msg("executing command")
169173

170-
return cmd.Run()
174+
err := cmd.Run()
175+
if err != nil {
176+
return fmt.Errorf("command `%s` failed: %w", cmd.String(), err)
177+
}
178+
179+
return nil
171180
}

internal/gocmd/gocmd_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ func TestGetEnv(t *testing.T) {
4545
require.Contains(t, env, "GOVERSION")
4646
}
4747

48-
func TestGetModuleName(t *testing.T) {
48+
func TestListModule(t *testing.T) {
4949
buf := new(bytes.Buffer)
50-
err := GetModule("../../", buf)
50+
err := ListModule("../../", buf)
5151
require.NoError(t, err)
5252

5353
mod := make(map[string]interface{})
@@ -57,7 +57,7 @@ func TestGetModuleName(t *testing.T) {
5757
assert.Equal(t, true, mod["Main"])
5858
}
5959

60-
func TestGetModuleList(t *testing.T) {
60+
func TestListModules(t *testing.T) {
6161
buf := new(bytes.Buffer)
6262
err := ListModules("../../", buf)
6363
require.NoError(t, err)

internal/gomod/binary.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,22 @@ import (
2626
"github.com/CycloneDX/cyclonedx-gomod/internal/gocmd"
2727
)
2828

29-
func LoadModulesFromBinary(binaryPath string) ([]Module, map[string]string, error) {
29+
func LoadModulesFromBinary(binaryPath string) (string, []Module, map[string]string, error) {
3030
buf := new(bytes.Buffer)
3131
if err := gocmd.LoadModulesFromBinary(binaryPath, buf); err != nil {
32-
return nil, nil, err
32+
return "", nil, nil, err
3333
}
3434

35-
modules, hashes := parseModulesFromBinary(buf)
35+
goVersion, modules, hashes := parseModulesFromBinary(binaryPath, buf)
3636

3737
sortModules(modules)
3838

39-
return modules, hashes, nil
39+
return goVersion, modules, hashes, nil
4040
}
4141

42-
func parseModulesFromBinary(reader io.Reader) ([]Module, map[string]string) {
43-
modules := make([]Module, 0)
42+
func parseModulesFromBinary(binaryPath string, reader io.Reader) (string, []Module, map[string]string) {
43+
var goVersion string
44+
var modules []Module
4445
hashes := make(map[string]string)
4546

4647
moduleIndex := 0
@@ -53,6 +54,10 @@ func parseModulesFromBinary(reader io.Reader) ([]Module, map[string]string) {
5354

5455
fields := strings.Fields(line)
5556
switch fields[0] {
57+
case binaryPath + ":":
58+
if len(fields) == 2 && strings.HasPrefix(fields[1], "go") {
59+
goVersion = strings.TrimPrefix(fields[1], "go")
60+
}
5661
case "mod": // Main module
5762
modules = append(modules, Module{
5863
Path: fields[1],
@@ -77,9 +82,11 @@ func parseModulesFromBinary(reader io.Reader) ([]Module, map[string]string) {
7782
Version: fields[2],
7883
}
7984
modules[moduleIndex-1].Replace = &module
80-
hashes[module.Coordinates()] = fields[3]
85+
if len(fields) == 4 {
86+
hashes[module.Coordinates()] = fields[3]
87+
}
8188
}
8289
}
8390

84-
return modules, hashes
91+
return goVersion, modules, hashes
8592
}

0 commit comments

Comments
 (0)