Skip to content

Commit

Permalink
introduce multi-command cli (#45)
Browse files Browse the repository at this point in the history
* introduce multi-command cli

Signed-off-by: nscuro <[email protected]>

* basic options validation for cli

Signed-off-by: nscuro <[email protected]>

* add short usage for root command

Signed-off-by: nscuro <[email protected]>

* minor wording changes

Signed-off-by: nscuro <[email protected]>

* out -> output

Signed-off-by: nscuro <[email protected]>

* build: set pseudo version for Makefile builds

Signed-off-by: nscuro <[email protected]>

* docs: regenerate example sboms

Signed-off-by: nscuro <[email protected]>

* ci: add some more linters

Signed-off-by: nscuro <[email protected]>

* cmd.go -> root.go

Signed-off-by: nscuro <[email protected]>

* minor simplifications

Signed-off-by: nscuro <[email protected]>

* build: use makefile for local docker image

Signed-off-by: nscuro <[email protected]>

* indicate that PATH is optional for mod command

Signed-off-by: nscuro <[email protected]>
  • Loading branch information
nscuro authored Jul 31, 2021
1 parent 9cff325 commit 404d7ee
Show file tree
Hide file tree
Showing 28 changed files with 569 additions and 283 deletions.
6 changes: 6 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
linters:
enable:
- asciicheck
- exhaustive
- gochecknoinits
- goconst
- gofmt
- gosec
- predeclared
- unconvert
- unparam
- wastedassign
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
FROM golang:1.16-alpine as build
ARG VERSION=latest
WORKDIR /tmp/cyclonedx-gomod
RUN apk --no-cache add git make
COPY . .
RUN go install
RUN make install

FROM golang:1.16-alpine
COPY --from=build /go/bin/cyclonedx-gomod /usr/local/bin/
Expand Down
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
LDFLAGS="-s -w -X github.com/CycloneDX/cyclonedx-gomod/internal/version.Version=v0.0.0-$(shell git show -s --date=format:'%Y%m%d%H%M%S' --format=%cd HEAD)-$(shell git rev-parse HEAD | head -c 12)"

build:
go build -v
go build -v -ldflags=${LDFLAGS}
.PHONY: build

install:
go install -v -ldflags=${LDFLAGS}
.PHONY: install

generate:
go generate -v ./...
.PHONY: generate
Expand Down
14 changes: 7 additions & 7 deletions examples/cyclonedx-go-v0.4.0.bom.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.2",
"serialNumber": "urn:uuid:6851773a-8365-4efc-9210-2db3cbc7dcf8",
"serialNumber": "urn:uuid:4b21c403-047b-45d4-91ba-9b45448c0b69",
"version": 1,
"metadata": {
"timestamp": "2021-07-17T08:48:10+02:00",
"timestamp": "2021-07-31T22:31:40+02:00",
"tools": [
{
"vendor": "CycloneDX",
"name": "cyclonedx-gomod",
"version": "v0.9.0",
"version": "v0.0.0-20210729183245-27eb9c8d1f90",
"hashes": [
{
"alg": "MD5",
"content": "fbd6c9be6da0f447c40095c2713fe922"
"content": "876cb6fddc1cf5faa72bb4f6f4356edf"
},
{
"alg": "SHA-1",
"content": "086cc0f0ffd38240d4362b87f2c2d66a7e83aa07"
"content": "9711bd6a951a5f30481a3f163ee1398ebb3d515c"
},
{
"alg": "SHA-256",
"content": "a05c272164e0f59937063a6304015286faa90f93b2985e67b60788dd7c69ad00"
"content": "9bc8fb8d2245a3b1f115e5baf51a88afa785e679469377587048ae652730b6b5"
},
{
"alg": "SHA-512",
"content": "1938e8d1add284d2f308910a28f3ca8dd84ff4c84b67957a361e12527fef14cfae39ad2a02a5815dee6fba40b360a2e4cc74aeea2a5b5a39dc97dfb1545c0e34"
"content": "9c506ceb3915657824425ad180737dc2cf74f749db13edeb4a5fadc7ad0f54e43390945abe3c1ea405568f74a5980dcfa0b72f887ee1baf0bc4822a78829eb78"
}
]
}
Expand Down
26 changes: 13 additions & 13 deletions examples/proton-bridge-v1.6.3.bom.xml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" serialNumber="urn:uuid:d27ac3f9-c887-454a-b748-fcd625524a71" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.2" serialNumber="urn:uuid:e5f3ff1e-050e-437c-92ac-62381af63ff9" version="1">
<metadata>
<timestamp>2021-07-17T08:49:52+02:00</timestamp>
<timestamp>2021-07-31T22:33:34+02:00</timestamp>
<tools>
<tool>
<vendor>CycloneDX</vendor>
<name>cyclonedx-gomod</name>
<version>v0.9.0</version>
<version>v0.0.0-20210729183245-27eb9c8d1f90</version>
<hashes>
<hash alg="MD5">fbd6c9be6da0f447c40095c2713fe922</hash>
<hash alg="SHA-1">086cc0f0ffd38240d4362b87f2c2d66a7e83aa07</hash>
<hash alg="SHA-256">a05c272164e0f59937063a6304015286faa90f93b2985e67b60788dd7c69ad00</hash>
<hash alg="SHA-512">1938e8d1add284d2f308910a28f3ca8dd84ff4c84b67957a361e12527fef14cfae39ad2a02a5815dee6fba40b360a2e4cc74aeea2a5b5a39dc97dfb1545c0e34</hash>
<hash alg="MD5">876cb6fddc1cf5faa72bb4f6f4356edf</hash>
<hash alg="SHA-1">9711bd6a951a5f30481a3f163ee1398ebb3d515c</hash>
<hash alg="SHA-256">9bc8fb8d2245a3b1f115e5baf51a88afa785e679469377587048ae652730b6b5</hash>
<hash alg="SHA-512">9c506ceb3915657824425ad180737dc2cf74f749db13edeb4a5fadc7ad0f54e43390945abe3c1ea405568f74a5980dcfa0b72f887ee1baf0bc4822a78829eb78</hash>
</hashes>
</tool>
</tools>
Expand Down Expand Up @@ -1471,10 +1471,10 @@
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/jaytaylor/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/keybase/[email protected]">
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/davecgh/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/pkg/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/stretchr/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
</dependency>
<dependency ref="pkg:golang/github.com/logrusorgru/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/mattn/[email protected]">
Expand Down Expand Up @@ -1511,10 +1511,10 @@
<dependency ref="pkg:golang/gopkg.in/[email protected]"></dependency>
</dependency>
<dependency ref="pkg:golang/github.com/therecipe/[email protected]">
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/gopherjs/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/sirupsen/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/stretchr/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/golang.org/x/[email protected]"></dependency>
<dependency ref="pkg:golang/golang.org/x/[email protected]"></dependency>
</dependency>
Expand Down Expand Up @@ -1544,7 +1544,11 @@
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]">
<dependency ref="pkg:golang/github.com/0xAX/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/Masterminds/semver/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
Expand All @@ -1554,8 +1558,6 @@
<dependency ref="pkg:golang/github.com/abiosoft/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/allan-simon/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/cucumber/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/emersion/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/emersion/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/emersion/[email protected]"></dependency>
Expand All @@ -1575,7 +1577,6 @@
<dependency ref="pkg:golang/github.com/google/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/gopherjs/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/hashicorp/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/jaytaylor/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/keybase/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/logrusorgru/[email protected]"></dependency>
Expand All @@ -1592,7 +1593,6 @@
<dependency ref="pkg:golang/github.com/urfave/cli/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/vmihailenco/msgpack/[email protected]"></dependency>
<dependency ref="pkg:golang/go.etcd.io/[email protected]"></dependency>
<dependency ref="pkg:golang/github.com/ProtonMail/[email protected]"></dependency>
<dependency ref="pkg:golang/golang.org/x/[email protected]"></dependency>
<dependency ref="pkg:golang/golang.org/x/[email protected]"></dependency>
</dependency>
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/go-enry/go-license-detector/v4 v4.3.0
github.com/go-git/go-git/v5 v5.4.2
github.com/google/uuid v1.3.0
github.com/peterbourgon/ff/v3 v3.1.0
github.com/stretchr/testify v1.7.0
golang.org/x/mod v0.4.2
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/CycloneDX/cyclonedx-go v0.4.0 h1:Wz4QZ9B4RXGWIWTypVLEOVJgOdFfy5mcS5PGNzUkZxU=
github.com/CycloneDX/cyclonedx-go v0.4.0/go.mod h1:rmRcf//gT7PIzovatusbWi377xqCg1FS4jyST0GH20E=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
Expand Down Expand Up @@ -85,6 +86,9 @@ github.com/montanaflynn/stats v0.0.0-20151014174947-eeaced052adb/go.mod h1:wL8QJ
github.com/neurosnap/sentences v1.0.6 h1:iBVUivNtlwGkYsJblWV8GGVFmXzZzak907Ci8aA0VTE=
github.com/neurosnap/sentences v1.0.6/go.mod h1:pg1IapvYpWCJJm/Etxeh0+gtMf1rI1STY9S7eUCPbDc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
github.com/peterbourgon/ff/v3 v3.1.0 h1:5JAeDK5j/zhKFjyHEZQXwXBoDijERaos10RE+xamOsY=
github.com/peterbourgon/ff/v3 v3.1.0/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down
155 changes: 155 additions & 0 deletions internal/cli/mod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// This file is part of CycloneDX GoMod
//
// Licensed under the Apache License, Version 2.0 (the “License”);
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an “AS IS” BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) OWASP Foundation. All Rights Reserved.

package cli

import (
"context"
"errors"
"flag"
"fmt"
"io"
"os"

cdx "github.com/CycloneDX/cyclonedx-go"
"github.com/CycloneDX/cyclonedx-gomod/internal/sbom"
"github.com/google/uuid"
"github.com/peterbourgon/ff/v3/ffcli"
)

// ModOptions provides options for the `mod` command.
type ModOptions struct {
OutputOptions
SBOMOptions

ModuleDir string
ResolveLicenses bool
}

func (m *ModOptions) RegisterFlags(fs *flag.FlagSet) {
m.OutputOptions.RegisterFlags(fs)
m.SBOMOptions.RegisterFlags(fs)

fs.BoolVar(&m.ResolveLicenses, "licenses", false, "Resolve module licenses")
}

func (m ModOptions) Validate() error {
errs := make([]error, 0)

if err := m.OutputOptions.Validate(); err != nil {
var verr *OptionsValidationError
if errors.As(err, &verr) {
errs = append(errs, verr.Errors...)
} else {
return err
}
}
if err := m.SBOMOptions.Validate(); err != nil {
var verr *OptionsValidationError
if errors.As(err, &verr) {
errs = append(errs, verr.Errors...)
} else {
return err
}
}

if len(errs) > 0 {
return &OptionsValidationError{Errors: errs}
}

return nil
}

func newModCmd() *ffcli.Command {
fs := flag.NewFlagSet("cyclonedx-gomod mod", flag.ExitOnError)

var options ModOptions
options.RegisterFlags(fs)

return &ffcli.Command{
Name: "mod",
ShortHelp: "Generate SBOM for a module",
ShortUsage: "cyclonedx-gomod mod [FLAGS...] [PATH]",
FlagSet: fs,
Exec: func(ctx context.Context, args []string) error {
if len(args) > 1 {
return flag.ErrHelp
}
if len(args) == 0 {
options.ModuleDir = "."
} else {
options.ModuleDir = args[0]
}

return execModCmd(options)
},
}
}

func execModCmd(options ModOptions) error {
if err := options.Validate(); err != nil {
return err
}

var serial *uuid.UUID
if !options.NoSerialNumber && options.SerialNumber != "" {
serialUUID := uuid.MustParse(options.SerialNumber)
serial = &serialUUID
}

bom, err := sbom.Generate(options.ModuleDir, sbom.GenerateOptions{
ComponentType: cdx.ComponentType(options.ComponentType),
IncludeStdLib: options.IncludeStd,
IncludeTest: options.IncludeTest,
NoSerialNumber: options.NoSerialNumber,
NoVersionPrefix: options.NoVersionPrefix,
Reproducible: options.Reproducible,
ResolveLicenses: options.ResolveLicenses,
SerialNumber: serial,
})
if err != nil {
return fmt.Errorf("failed to generate sbom: %w", err)
}

var outputFormat cdx.BOMFileFormat
if options.UseJSON {
outputFormat = cdx.BOMFileFormatJSON
} else {
outputFormat = cdx.BOMFileFormatXML
}

var outputWriter io.Writer
if options.FilePath == "" || options.FilePath == "-" {
outputWriter = os.Stdout
} else {
outputFile, err := os.Create(options.FilePath)
if err != nil {
return fmt.Errorf("failed to create output file %s: %w", options.FilePath, err)
}
defer outputFile.Close()
outputWriter = outputFile
}

encoder := cdx.NewBOMEncoder(outputWriter, outputFormat)
encoder.SetPretty(true)

if err = encoder.Encode(bom); err != nil {
return fmt.Errorf("failed to encode sbom: %w", err)
}

return nil
}
Loading

0 comments on commit 404d7ee

Please sign in to comment.