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

Add new git "count-contributors" command. #91

Merged
merged 19 commits into from
Jun 30, 2024
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
6 changes: 6 additions & 0 deletions cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,11 @@ func GetJfrogCliSecurityApp() components.App {
Commands: getXrayNameSpaceCommands(),
Category: "Command Namespaces",
})
app.Subcommands = append(app.Subcommands, components.Namespace{
Name: "git",
Description: "Git commands.",
Commands: getGitNameSpaceCommands(),
Category: "Command Namespaces",
})
return app
}
37 changes: 30 additions & 7 deletions cli/docs/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package docs

import (
"fmt"
"github.com/jfrog/jfrog-cli-security/commands/git"
"strings"

"github.com/jfrog/jfrog-cli-core/v2/common/cliutils"
Expand All @@ -13,13 +14,14 @@ import (

const (
// Security Commands Keys
XrCurl = "xr-curl"
OfflineUpdate = "offline-update"
XrScan = "xr-scan"
BuildScan = "build-scan"
DockerScan = "docker scan"
Audit = "audit"
CurationAudit = "curation-audit"
XrCurl = "xr-curl"
OfflineUpdate = "offline-update"
XrScan = "xr-scan"
BuildScan = "build-scan"
DockerScan = "docker scan"
Audit = "audit"
CurationAudit = "curation-audit"
GitCountContributors = "count-contributors"

// TODO: Deprecated commands (remove at next CLI major version)
AuditMvn = "audit-maven"
Expand Down Expand Up @@ -113,6 +115,15 @@ const (

// Unique curation flags
CurationOutput = "curation-format"

// Unique git flags
ScmType = "scm-type"
ScmApiUrl = "scm-api-url"
Token = "token"
Owner = "owner"
RepoName = "repo-name"
Months = "months"
DetailedSummary = "detailed-summary"
)

// Mapping between security commands (key) and their flags (key).
Expand All @@ -138,6 +149,9 @@ var commandFlags = map[string][]string{
CurationAudit: {
CurationOutput, WorkingDirs, Threads, RequirementsFile,
},
GitCountContributors: {
ScmType, ScmApiUrl, Token, Owner, RepoName, Months, DetailedSummary,
},
// TODO: Deprecated commands (remove at next CLI major version)
AuditMvn: {
url, user, password, accessToken, ServerId, InsecureTls, Project, ExclusionsAudit, Watches, RepoPath, Licenses, OutputFormat, Fail, ExtendedTable, useWrapperAudit,
Expand Down Expand Up @@ -233,6 +247,15 @@ var flagsMap = map[string]components.Flag{
Sast: components.NewBoolFlag(Sast, fmt.Sprintf("Selective scanners mode: Execute SAST sub-scan. Can be combined with --%s, --%s and --%s.", Sca, Secrets, Iac)),
Secrets: components.NewBoolFlag(Secrets, fmt.Sprintf("Selective scanners mode: Execute Secrets sub-scan. Can be combined with --%s, --%s and --%s.", Sca, Sast, Iac)),
WithoutCA: components.NewBoolFlag(WithoutCA, fmt.Sprintf("Selective scanners mode: Disable Contextual Analysis scanner after SCA. Relevant only with --%s flag.", Sca)),

// Git flags
ScmType: components.NewStringFlag(ScmType, fmt.Sprintf("SCM type. Possible values are: %s.", git.NewScmType().GetValidScmTypeString()), components.SetMandatory()),
ScmApiUrl: components.NewStringFlag(ScmApiUrl, "SCM API URL. For example: 'https://api.github.com'.", components.SetMandatory()),
Token: components.NewStringFlag(Token, fmt.Sprintf("SCM API token. In the absence of a flag, tokens should be passed in the %s enviroment variable, or in the corresponding environment variables '%s'.", git.GenericGitTokenEnvVar, git.NewScmType().GetOptionalScmTypeTokenEnvVars())),
Owner: components.NewStringFlag(Owner, "The owner of the repository. Depends on the git provider - on github and gitlab the owner is usually an individual or an organization, on bitbucket it is a project.", components.SetMandatory()),
RepoName: components.NewStringFlag(RepoName, "Specific repository name to analyze, If not provided all repositories in the project will be analyzed."),
Months: components.NewStringFlag(Months, "Number of months to analyze.", components.WithIntDefaultValue(git.DefaultContContributorsMonths)),
DetailedSummary: components.NewBoolFlag(DetailedSummary, "Set to true to get a contributors detailed summary."),
}

func GetCommandFlags(cmdKey string) []components.Flag {
Expand Down
5 changes: 5 additions & 0 deletions cli/docs/git/help.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package git

func GetContContributorsDescription() string {
return "List all GIT providers' contributing developers."
}
109 changes: 109 additions & 0 deletions cli/gitcommands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package cli

import (
"github.com/jfrog/froggit-go/vcsutils"
"github.com/jfrog/jfrog-cli-core/v2/common/progressbar"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
flags "github.com/jfrog/jfrog-cli-security/cli/docs"
gitDocs "github.com/jfrog/jfrog-cli-security/cli/docs/git"
"github.com/jfrog/jfrog-cli-security/commands/git"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"os"
)

func getGitNameSpaceCommands() []components.Command {
return []components.Command{
{
Name: "count-contributors",
Aliases: []string{"cc"},
Flags: flags.GetCommandFlags(flags.GitCountContributors),
Description: gitDocs.GetContContributorsDescription(),
Hidden: true,
Action: GitCountContributorsCmd,
},
}
}

func GetCountContributorsParams(c *components.Context) (*git.CountContributorsParams, error) {
params := git.CountContributorsParams{}
// Mandatory flags
scmTypes := git.NewScmType()
// ScmType
scmType := c.GetStringFlagValue(flags.ScmType)
if scmType == "" {
return nil, errorutils.CheckErrorf("The --%s option is mandatory", flags.ScmType)
} else {
if scmTypeVal, ok := scmTypes.ScmTypeMap[scmType]; ok {
params.ScmType = scmTypeVal
} else {
return nil, errorutils.CheckErrorf("Unsupported SCM type: %s, Possible values are: %v", scmType, scmTypes.GetValidScmTypeString())
}
}
// Token
params.Token = c.GetStringFlagValue(flags.Token)
if params.Token == "" {
var envVarToken string
switch params.ScmType {
case vcsutils.BitbucketServer:
envVarToken = os.Getenv(git.BitbucketTokenEnvVar)
case vcsutils.GitLab:
envVarToken = os.Getenv(git.GitlabTokenEnvVar)
case vcsutils.GitHub:
envVarToken = os.Getenv(git.GithubTokenEnvVar)
default:
return nil, errorutils.CheckErrorf("Unsupported SCM type: %s, Possible values are: %v", scmType, scmTypes.GetValidScmTypeString())
}
if envVarToken != "" {
params.Token = envVarToken
} else {
envVarToken = os.Getenv(git.GenericGitTokenEnvVar)
if envVarToken != "" {
params.Token = envVarToken
} else {
return nil, errorutils.CheckErrorf("Providing a token is mandatory. should use --%s flag, the token enviromment variable %s, or coresponding provider enviromment variable %s.", flags.Token, git.GenericGitTokenEnvVar, scmTypes.GetOptionalScmTypeTokenEnvVars())
}
}
}
// Owner
params.Owner = c.GetStringFlagValue(flags.Owner)
if params.Owner == "" {
return nil, errorutils.CheckErrorf("The --%s option is mandatory", flags.Owner)
}
// ScmApiUrl
params.ScmApiUrl = c.GetStringFlagValue(flags.ScmApiUrl)
if params.ScmApiUrl == "" {
return nil, errorutils.CheckErrorf("The --%s option is mandatory", flags.ScmApiUrl)
}

// Optional flags
// Repository name
params.Repository = c.GetStringFlagValue(flags.RepoName)
// Months
if !c.IsFlagSet(flags.Months) {
params.MonthsNum = git.DefaultContContributorsMonths
} else {
months, err := c.GetIntFlagValue(flags.Months)
if err != nil {
return nil, err
}
if months <= 0 {
return nil, errorutils.CheckErrorf("Invalid value for '--%s=%d'. If set, should be positive number.", flags.Months, months)
}
params.MonthsNum = months
}
// DetailedSummery
params.DetailedSummery = c.GetBoolFlagValue(flags.DetailedSummary)
return &params, nil
}

func GitCountContributorsCmd(c *components.Context) error {
gitContrParams, err := GetCountContributorsParams(c)
if err != nil {
return err
}
gitContributionCommand, err := git.NewCountContributorsCommand(gitContrParams)
if err != nil {
return err
}
return progressbar.ExecWithProgress(gitContributionCommand)
}
Loading
Loading