Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ cmd: Refactor to make importable #1696

Merged
merged 5 commits into from
Mar 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 31 additions & 73 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ import (
"strings"

"github.com/spf13/cobra"
"sigs.k8s.io/release-utils/version"

"github.com/ossf/scorecard/v4/checker"
"github.com/ossf/scorecard/v4/checks"
"github.com/ossf/scorecard/v4/clients"
docs "github.com/ossf/scorecard/v4/docs/checks"
sclog "github.com/ossf/scorecard/v4/log"
Expand All @@ -35,6 +35,8 @@ import (
"github.com/ossf/scorecard/v4/policy"
)

var opts = options.New()

const (
scorecardLong = "A program that shows security scorecard for an open source software."
scorecardUse = `./scorecard [--repo=<repo_url>] [--local=folder] [--checks=check1,...]
Expand All @@ -43,80 +45,36 @@ const (
scorecardShort = "Security Scorecards"
)

var rootCmd = &cobra.Command{
Use: scorecardUse,
Short: scorecardShort,
Long: scorecardLong,
Run: scorecardCmd,
}

var opts = options.New()

//nolint:gochecknoinits
func init() {
rootCmd.Flags().StringVar(&opts.Repo, "repo", "", "repository to check")
rootCmd.Flags().StringVar(&opts.Local, "local", "", "local folder to check")
rootCmd.Flags().StringVar(&opts.Commit, "commit", options.DefaultCommit, "commit to analyze")
rootCmd.Flags().StringVar(
&opts.LogLevel,
"verbosity",
options.DefaultLogLevel,
"set the log level",
)
rootCmd.Flags().StringVar(
&opts.NPM, "npm", "",
"npm package to check, given that the npm package has a GitHub repository")
rootCmd.Flags().StringVar(
&opts.PyPI, "pypi", "",
"pypi package to check, given that the pypi package has a GitHub repository")
rootCmd.Flags().StringVar(
&opts.RubyGems, "rubygems", "",
"rubygems package to check, given that the rubygems package has a GitHub repository")
rootCmd.Flags().StringSliceVar(
&opts.Metadata, "metadata", []string{}, "metadata for the project. It can be multiple separated by commas")
rootCmd.Flags().BoolVar(&opts.ShowDetails, "show-details", false, "show extra details about each check")
checkNames := []string{}
for checkName := range checks.GetAll() {
checkNames = append(checkNames, checkName)
}
rootCmd.Flags().StringSliceVar(&opts.ChecksToRun, "checks", []string{},
fmt.Sprintf("Checks to run. Possible values are: %s", strings.Join(checkNames, ",")))

// TODO(cmd): Extract logic
if options.IsSarifEnabled() {
rootCmd.Flags().StringVar(&opts.PolicyFile, "policy", "", "policy to enforce")
rootCmd.Flags().StringVar(&opts.Format, "format", options.FormatDefault,
"output format allowed values are [default, sarif, json]")
} else {
rootCmd.Flags().StringVar(&opts.Format, "format", options.FormatDefault,
"output format allowed values are [default, json]")
}
}

// Execute runs the Scorecard commandline.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

func scorecardCmd(cmd *cobra.Command, args []string) {
RunScorecard(args)
// New creates a new instance of the scorecard command.
func New() *cobra.Command {
cmd := &cobra.Command{
justaugustus marked this conversation as resolved.
Show resolved Hide resolved
Use: scorecardUse,
Short: scorecardShort,
Long: scorecardLong,
PreRunE: func(cmd *cobra.Command, args []string) error {
err := opts.Validate()
if err != nil {
return fmt.Errorf("validating options: %w", err)
}

return nil
},
// TODO(cmd): Consider using RunE here
Run: func(cmd *cobra.Command, args []string) {
rootCmd(opts)
},
}

opts.AddFlags(cmd)

// Add sub-commands.
cmd.AddCommand(serveCmd())
cmd.AddCommand(version.Version())
return cmd
}

// RunScorecard runs scorecard checks given a set of arguments.
// TODO(cmd): Is `args` required?
func RunScorecard(args []string) {
// TODO(cmd): Catch validation errors
valErrs := opts.Validate()
if len(valErrs) != 0 {
log.Panicf(
"the following validation errors occurred: %+v",
valErrs,
)
}

// rootCmd runs scorecard checks given a set of arguments.
func rootCmd(opts *options.Options) {
// Set `repo` from package managers.
pkgResp, err := fetchGitRepositoryFromPackageManagers(opts.NPM, opts.PyPI, opts.RubyGems)
if err != nil {
Expand Down
126 changes: 62 additions & 64 deletions cmd/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,79 +30,77 @@ import (
"github.com/ossf/scorecard/v4/pkg"
)

//nolint:gochecknoinits
func init() {
rootCmd.AddCommand(serveCmd)
}

var serveCmd = &cobra.Command{
Use: "serve",
Short: "Serve the scorecard program over http",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
logger := log.NewLogger(log.ParseLevel(opts.LogLevel))

t, err := template.New("webpage").Parse(tpl)
if err != nil {
// TODO(log): Should this actually panic?
logger.Error(err, "parsing webpage template")
panic(err)
}
// TODO(cmd): Determine if this should be exported.
func serveCmd() *cobra.Command {
return &cobra.Command{
Use: "serve",
Short: "Serve the scorecard program over http",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
logger := log.NewLogger(log.ParseLevel(opts.LogLevel))

http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
repoParam := r.URL.Query().Get("repo")
const length = 3
s := strings.SplitN(repoParam, "/", length)
if len(s) != length {
rw.WriteHeader(http.StatusBadRequest)
}
repo, err := githubrepo.MakeGithubRepo(repoParam)
t, err := template.New("webpage").Parse(tpl)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
}
ctx := r.Context()
repoClient := githubrepo.CreateGithubRepoClient(ctx, logger)
ossFuzzRepoClient, err := githubrepo.CreateOssFuzzRepoClient(ctx, logger)
vulnsClient := clients.DefaultVulnerabilitiesClient()
if err != nil {
logger.Error(err, "initializing clients")
rw.WriteHeader(http.StatusInternalServerError)
}
defer ossFuzzRepoClient.Close()
ciiClient := clients.DefaultCIIBestPracticesClient()
repoResult, err := pkg.RunScorecards(
ctx, repo, clients.HeadSHA /*commitSHA*/, false /*raw*/, checks.AllChecks, repoClient,
ossFuzzRepoClient, ciiClient, vulnsClient)
if err != nil {
logger.Error(err, "running enabled scorecard checks on repo")
rw.WriteHeader(http.StatusInternalServerError)
// TODO(log): Should this actually panic?
logger.Error(err, "parsing webpage template")
panic(err)
}

if r.Header.Get("Content-Type") == "application/json" {
if err := repoResult.AsJSON(opts.ShowDetails, log.ParseLevel(opts.LogLevel), rw); err != nil {
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
repoParam := r.URL.Query().Get("repo")
const length = 3
s := strings.SplitN(repoParam, "/", length)
if len(s) != length {
rw.WriteHeader(http.StatusBadRequest)
}
repo, err := githubrepo.MakeGithubRepo(repoParam)
if err != nil {
rw.WriteHeader(http.StatusBadRequest)
}
ctx := r.Context()
repoClient := githubrepo.CreateGithubRepoClient(ctx, logger)
ossFuzzRepoClient, err := githubrepo.CreateOssFuzzRepoClient(ctx, logger)
vulnsClient := clients.DefaultVulnerabilitiesClient()
if err != nil {
logger.Error(err, "initializing clients")
rw.WriteHeader(http.StatusInternalServerError)
}
defer ossFuzzRepoClient.Close()
ciiClient := clients.DefaultCIIBestPracticesClient()
repoResult, err := pkg.RunScorecards(
ctx, repo, clients.HeadSHA /*commitSHA*/, false /*raw*/, checks.AllChecks, repoClient,
ossFuzzRepoClient, ciiClient, vulnsClient)
if err != nil {
logger.Error(err, "running enabled scorecard checks on repo")
rw.WriteHeader(http.StatusInternalServerError)
}

if r.Header.Get("Content-Type") == "application/json" {
if err := repoResult.AsJSON(opts.ShowDetails, log.ParseLevel(opts.LogLevel), rw); err != nil {
// TODO(log): Improve error message
logger.Error(err, "")
rw.WriteHeader(http.StatusInternalServerError)
}
return
}
if err := t.Execute(rw, repoResult); err != nil {
// TODO(log): Improve error message
logger.Error(err, "")
rw.WriteHeader(http.StatusInternalServerError)
}
return
})
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
if err := t.Execute(rw, repoResult); err != nil {
// TODO(log): Improve error message
logger.Error(err, "")
fmt.Printf("Listening on localhost:%s\n", port)
err = http.ListenAndServe(fmt.Sprintf("0.0.0.0:%s", port), nil)
if err != nil {
// TODO(log): Should this actually panic?
logger.Error(err, "listening and serving")
panic(err)
}
})
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
fmt.Printf("Listening on localhost:%s\n", port)
err = http.ListenAndServe(fmt.Sprintf("0.0.0.0:%s", port), nil)
if err != nil {
// TODO(log): Should this actually panic?
logger.Error(err, "listening and serving")
panic(err)
}
},
},
}
}

const tpl = `
Expand Down
44 changes: 0 additions & 44 deletions cmd/version.go

This file was deleted.

7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ require (
mvdan.cc/sh/v3 v3.4.3
)

require github.com/onsi/ginkgo/v2 v2.1.3
require (
github.com/caarlos0/env/v6 v6.9.1
github.com/onsi/ginkgo/v2 v2.1.3
sigs.k8s.io/release-utils v0.5.0
)

require (
cloud.google.com/go v0.100.2 // indirect
Expand All @@ -56,6 +60,7 @@ require (
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/aws/aws-sdk-go v1.40.34 // indirect
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect
github.com/containerd/typeurl v1.0.2 // indirect
github.com/docker/cli v20.10.12+incompatible // indirect
Expand Down
Loading