Skip to content

Commit 84cdc8c

Browse files
authored
✨ cmd: Refactor to make importable (#1696)
* cmd: Refactor to make importable * options: Add support for parsing via environment variables * options: Support setting feature flags via option * cmd: Replace `version` with sigs.k8s.io/release-utils/version * cmd: Move option validation into pre-run function Signed-off-by: Stephen Augustus <foo@auggie.dev>
1 parent 738b246 commit 84cdc8c

File tree

8 files changed

+333
-209
lines changed

8 files changed

+333
-209
lines changed

cmd/root.go

Lines changed: 31 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import (
2424
"strings"
2525

2626
"github.com/spf13/cobra"
27+
"sigs.k8s.io/release-utils/version"
2728

2829
"github.com/ossf/scorecard/v4/checker"
29-
"github.com/ossf/scorecard/v4/checks"
3030
"github.com/ossf/scorecard/v4/clients"
3131
docs "github.com/ossf/scorecard/v4/docs/checks"
3232
sclog "github.com/ossf/scorecard/v4/log"
@@ -35,6 +35,8 @@ import (
3535
"github.com/ossf/scorecard/v4/policy"
3636
)
3737

38+
var opts = options.New()
39+
3840
const (
3941
scorecardLong = "A program that shows security scorecard for an open source software."
4042
scorecardUse = `./scorecard [--repo=<repo_url>] [--local=folder] [--checks=check1,...]
@@ -43,80 +45,36 @@ const (
4345
scorecardShort = "Security Scorecards"
4446
)
4547

46-
var rootCmd = &cobra.Command{
47-
Use: scorecardUse,
48-
Short: scorecardShort,
49-
Long: scorecardLong,
50-
Run: scorecardCmd,
51-
}
52-
53-
var opts = options.New()
54-
55-
//nolint:gochecknoinits
56-
func init() {
57-
rootCmd.Flags().StringVar(&opts.Repo, "repo", "", "repository to check")
58-
rootCmd.Flags().StringVar(&opts.Local, "local", "", "local folder to check")
59-
rootCmd.Flags().StringVar(&opts.Commit, "commit", options.DefaultCommit, "commit to analyze")
60-
rootCmd.Flags().StringVar(
61-
&opts.LogLevel,
62-
"verbosity",
63-
options.DefaultLogLevel,
64-
"set the log level",
65-
)
66-
rootCmd.Flags().StringVar(
67-
&opts.NPM, "npm", "",
68-
"npm package to check, given that the npm package has a GitHub repository")
69-
rootCmd.Flags().StringVar(
70-
&opts.PyPI, "pypi", "",
71-
"pypi package to check, given that the pypi package has a GitHub repository")
72-
rootCmd.Flags().StringVar(
73-
&opts.RubyGems, "rubygems", "",
74-
"rubygems package to check, given that the rubygems package has a GitHub repository")
75-
rootCmd.Flags().StringSliceVar(
76-
&opts.Metadata, "metadata", []string{}, "metadata for the project. It can be multiple separated by commas")
77-
rootCmd.Flags().BoolVar(&opts.ShowDetails, "show-details", false, "show extra details about each check")
78-
checkNames := []string{}
79-
for checkName := range checks.GetAll() {
80-
checkNames = append(checkNames, checkName)
81-
}
82-
rootCmd.Flags().StringSliceVar(&opts.ChecksToRun, "checks", []string{},
83-
fmt.Sprintf("Checks to run. Possible values are: %s", strings.Join(checkNames, ",")))
84-
85-
// TODO(cmd): Extract logic
86-
if options.IsSarifEnabled() {
87-
rootCmd.Flags().StringVar(&opts.PolicyFile, "policy", "", "policy to enforce")
88-
rootCmd.Flags().StringVar(&opts.Format, "format", options.FormatDefault,
89-
"output format allowed values are [default, sarif, json]")
90-
} else {
91-
rootCmd.Flags().StringVar(&opts.Format, "format", options.FormatDefault,
92-
"output format allowed values are [default, json]")
93-
}
94-
}
95-
96-
// Execute runs the Scorecard commandline.
97-
func Execute() {
98-
if err := rootCmd.Execute(); err != nil {
99-
fmt.Fprintln(os.Stderr, err)
100-
os.Exit(1)
101-
}
102-
}
103-
104-
func scorecardCmd(cmd *cobra.Command, args []string) {
105-
RunScorecard(args)
48+
// New creates a new instance of the scorecard command.
49+
func New() *cobra.Command {
50+
cmd := &cobra.Command{
51+
Use: scorecardUse,
52+
Short: scorecardShort,
53+
Long: scorecardLong,
54+
PreRunE: func(cmd *cobra.Command, args []string) error {
55+
err := opts.Validate()
56+
if err != nil {
57+
return fmt.Errorf("validating options: %w", err)
58+
}
59+
60+
return nil
61+
},
62+
// TODO(cmd): Consider using RunE here
63+
Run: func(cmd *cobra.Command, args []string) {
64+
rootCmd(opts)
65+
},
66+
}
67+
68+
opts.AddFlags(cmd)
69+
70+
// Add sub-commands.
71+
cmd.AddCommand(serveCmd())
72+
cmd.AddCommand(version.Version())
73+
return cmd
10674
}
10775

108-
// RunScorecard runs scorecard checks given a set of arguments.
109-
// TODO(cmd): Is `args` required?
110-
func RunScorecard(args []string) {
111-
// TODO(cmd): Catch validation errors
112-
valErrs := opts.Validate()
113-
if len(valErrs) != 0 {
114-
log.Panicf(
115-
"the following validation errors occurred: %+v",
116-
valErrs,
117-
)
118-
}
119-
76+
// rootCmd runs scorecard checks given a set of arguments.
77+
func rootCmd(opts *options.Options) {
12078
// Set `repo` from package managers.
12179
pkgResp, err := fetchGitRepositoryFromPackageManagers(opts.NPM, opts.PyPI, opts.RubyGems)
12280
if err != nil {

cmd/serve.go

Lines changed: 62 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -30,79 +30,77 @@ import (
3030
"github.com/ossf/scorecard/v4/pkg"
3131
)
3232

33-
//nolint:gochecknoinits
34-
func init() {
35-
rootCmd.AddCommand(serveCmd)
36-
}
37-
38-
var serveCmd = &cobra.Command{
39-
Use: "serve",
40-
Short: "Serve the scorecard program over http",
41-
Long: ``,
42-
Run: func(cmd *cobra.Command, args []string) {
43-
logger := log.NewLogger(log.ParseLevel(opts.LogLevel))
44-
45-
t, err := template.New("webpage").Parse(tpl)
46-
if err != nil {
47-
// TODO(log): Should this actually panic?
48-
logger.Error(err, "parsing webpage template")
49-
panic(err)
50-
}
33+
// TODO(cmd): Determine if this should be exported.
34+
func serveCmd() *cobra.Command {
35+
return &cobra.Command{
36+
Use: "serve",
37+
Short: "Serve the scorecard program over http",
38+
Long: ``,
39+
Run: func(cmd *cobra.Command, args []string) {
40+
logger := log.NewLogger(log.ParseLevel(opts.LogLevel))
5141

52-
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
53-
repoParam := r.URL.Query().Get("repo")
54-
const length = 3
55-
s := strings.SplitN(repoParam, "/", length)
56-
if len(s) != length {
57-
rw.WriteHeader(http.StatusBadRequest)
58-
}
59-
repo, err := githubrepo.MakeGithubRepo(repoParam)
42+
t, err := template.New("webpage").Parse(tpl)
6043
if err != nil {
61-
rw.WriteHeader(http.StatusBadRequest)
62-
}
63-
ctx := r.Context()
64-
repoClient := githubrepo.CreateGithubRepoClient(ctx, logger)
65-
ossFuzzRepoClient, err := githubrepo.CreateOssFuzzRepoClient(ctx, logger)
66-
vulnsClient := clients.DefaultVulnerabilitiesClient()
67-
if err != nil {
68-
logger.Error(err, "initializing clients")
69-
rw.WriteHeader(http.StatusInternalServerError)
70-
}
71-
defer ossFuzzRepoClient.Close()
72-
ciiClient := clients.DefaultCIIBestPracticesClient()
73-
repoResult, err := pkg.RunScorecards(
74-
ctx, repo, clients.HeadSHA /*commitSHA*/, false /*raw*/, checks.AllChecks, repoClient,
75-
ossFuzzRepoClient, ciiClient, vulnsClient)
76-
if err != nil {
77-
logger.Error(err, "running enabled scorecard checks on repo")
78-
rw.WriteHeader(http.StatusInternalServerError)
44+
// TODO(log): Should this actually panic?
45+
logger.Error(err, "parsing webpage template")
46+
panic(err)
7947
}
8048

81-
if r.Header.Get("Content-Type") == "application/json" {
82-
if err := repoResult.AsJSON(opts.ShowDetails, log.ParseLevel(opts.LogLevel), rw); err != nil {
49+
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
50+
repoParam := r.URL.Query().Get("repo")
51+
const length = 3
52+
s := strings.SplitN(repoParam, "/", length)
53+
if len(s) != length {
54+
rw.WriteHeader(http.StatusBadRequest)
55+
}
56+
repo, err := githubrepo.MakeGithubRepo(repoParam)
57+
if err != nil {
58+
rw.WriteHeader(http.StatusBadRequest)
59+
}
60+
ctx := r.Context()
61+
repoClient := githubrepo.CreateGithubRepoClient(ctx, logger)
62+
ossFuzzRepoClient, err := githubrepo.CreateOssFuzzRepoClient(ctx, logger)
63+
vulnsClient := clients.DefaultVulnerabilitiesClient()
64+
if err != nil {
65+
logger.Error(err, "initializing clients")
66+
rw.WriteHeader(http.StatusInternalServerError)
67+
}
68+
defer ossFuzzRepoClient.Close()
69+
ciiClient := clients.DefaultCIIBestPracticesClient()
70+
repoResult, err := pkg.RunScorecards(
71+
ctx, repo, clients.HeadSHA /*commitSHA*/, false /*raw*/, checks.AllChecks, repoClient,
72+
ossFuzzRepoClient, ciiClient, vulnsClient)
73+
if err != nil {
74+
logger.Error(err, "running enabled scorecard checks on repo")
75+
rw.WriteHeader(http.StatusInternalServerError)
76+
}
77+
78+
if r.Header.Get("Content-Type") == "application/json" {
79+
if err := repoResult.AsJSON(opts.ShowDetails, log.ParseLevel(opts.LogLevel), rw); err != nil {
80+
// TODO(log): Improve error message
81+
logger.Error(err, "")
82+
rw.WriteHeader(http.StatusInternalServerError)
83+
}
84+
return
85+
}
86+
if err := t.Execute(rw, repoResult); err != nil {
8387
// TODO(log): Improve error message
8488
logger.Error(err, "")
85-
rw.WriteHeader(http.StatusInternalServerError)
8689
}
87-
return
90+
})
91+
port := os.Getenv("PORT")
92+
if port == "" {
93+
port = "8080"
8894
}
89-
if err := t.Execute(rw, repoResult); err != nil {
90-
// TODO(log): Improve error message
91-
logger.Error(err, "")
95+
fmt.Printf("Listening on localhost:%s\n", port)
96+
err = http.ListenAndServe(fmt.Sprintf("0.0.0.0:%s", port), nil)
97+
if err != nil {
98+
// TODO(log): Should this actually panic?
99+
logger.Error(err, "listening and serving")
100+
panic(err)
92101
}
93-
})
94-
port := os.Getenv("PORT")
95-
if port == "" {
96-
port = "8080"
97-
}
98-
fmt.Printf("Listening on localhost:%s\n", port)
99-
err = http.ListenAndServe(fmt.Sprintf("0.0.0.0:%s", port), nil)
100-
if err != nil {
101-
// TODO(log): Should this actually panic?
102-
logger.Error(err, "listening and serving")
103-
panic(err)
104-
}
105-
},
102+
},
103+
}
106104
}
107105

108106
const tpl = `

cmd/version.go

Lines changed: 0 additions & 44 deletions
This file was deleted.

go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ require (
4444
mvdan.cc/sh/v3 v3.4.3
4545
)
4646

47-
require github.com/onsi/ginkgo/v2 v2.1.3
47+
require (
48+
github.com/caarlos0/env/v6 v6.9.1
49+
github.com/onsi/ginkgo/v2 v2.1.3
50+
sigs.k8s.io/release-utils v0.5.0
51+
)
4852

4953
require (
5054
cloud.google.com/go v0.100.2 // indirect
@@ -56,6 +60,7 @@ require (
5660
github.com/acomagu/bufpipe v1.0.3 // indirect
5761
github.com/aws/aws-sdk-go v1.40.34 // indirect
5862
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
63+
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
5964
github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect
6065
github.com/containerd/typeurl v1.0.2 // indirect
6166
github.com/docker/cli v20.10.12+incompatible // indirect

0 commit comments

Comments
 (0)