Skip to content

Commit

Permalink
feat: add required-scopes option for stricter scope validations
Browse files Browse the repository at this point in the history
  • Loading branch information
kowalczykp authored and kodiakhq[bot] committed Jan 27, 2021
1 parent 7e7e7ef commit 001d0e8
Show file tree
Hide file tree
Showing 9 changed files with 91 additions and 3 deletions.
12 changes: 9 additions & 3 deletions config/commits.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ func CommitConfig() root_runner.RunnerOptions {
strict := true
limit := 0
all := false
requiredScopes := []string{}

if viper.IsSet("commits.strict") {
strict = viper.GetBool("commits.strict")
Expand All @@ -24,9 +25,14 @@ func CommitConfig() root_runner.RunnerOptions {
all = viper.GetBool("commits.all")
}

if viper.IsSet("commits.required-scopes") {
requiredScopes = viper.GetStringSlice("commits.required-scopes")
}

return root_runner.RunnerOptions{
Strict: strict,
Limit: limit,
AllCommits: all,
Strict: strict,
Limit: limit,
AllCommits: all,
RequiredScopes: requiredScopes,
}
}
2 changes: 2 additions & 0 deletions config/commits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ func TestCommitConfig(t *testing.T) {
assert.Equal(t, true, defaultConfig.Strict, "expect strict to be true by default")
assert.Equal(t, 0, defaultConfig.Limit, "expect the limit to be 0 by default")
assert.Equal(t, false, defaultConfig.AllCommits, "expect AllCommits to be true by default")
assert.Equal(t, []string{}, defaultConfig.RequiredScopes, "expect required scopes to be empty slice by default")

err := os.Setenv(CommitsarConfigPath, "./testdata")
assert.NoError(t, err)
Expand All @@ -27,6 +28,7 @@ func TestCommitConfig(t *testing.T) {
assert.Equal(t, false, commitConfig.Strict, "expect strict to be false as opposed to the default of true")
assert.Equal(t, 100, commitConfig.Limit, "expect limit to be 100 as opposed to the default of 0")
assert.Equal(t, true, commitConfig.AllCommits, "expect strict to be false as opposed to the default of false")
assert.Equal(t, []string{}, commitConfig.RequiredScopes, "expect required scopes to be empty slice same as default for backward compatibility")

os.Clearenv()

Expand Down
2 changes: 2 additions & 0 deletions internal/commitpipeline/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type Options struct {
// AllCommits will check all the commits on the upstream branch. Regardless of Limit setting.
AllCommits bool
Strict bool
// RequiredScopes will check scope in commit message against list of required ones
RequiredScopes []string
}

func New(logger, debugLogger *log.Logger, options *Options, args ...string) (*Pipeline, error) {
Expand Down
9 changes: 9 additions & 0 deletions internal/commitpipeline/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func (pipeline *Pipeline) Run() (*dispatcher.PipelineSuccess, error) {
}

var faultyCommits []text.FailingCommit
requiredScopeChecker := text.RequiredScopeChecker(pipeline.options.RequiredScopes)

for _, commitHash := range filteredCommits {
commitObject, commitErr := gitRepo.Commit(commitHash)
Expand All @@ -85,7 +86,15 @@ func (pipeline *Pipeline) Run() (*dispatcher.PipelineSuccess, error) {

if textErr != nil {
faultyCommits = append(faultyCommits, text.FailingCommit{Hash: commitHash.String(), Message: parsedCommit.Heading, Error: textErr})
continue
}

scopeErr := requiredScopeChecker(parsedCommit.Scope)

if scopeErr != nil {
faultyCommits = append(faultyCommits, text.FailingCommit{Hash: commitHash.String(), Message: parsedCommit.Heading, Error: scopeErr})
}

}

if len(faultyCommits) != 0 {
Expand Down
2 changes: 2 additions & 0 deletions internal/root_runner/root_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type RunnerOptions struct {
// AllCommits will check all the commits on the upstream branch. Regardless of Limit setting.
AllCommits bool
Strict bool
// RequiredScopes will check scope in commit message against list of required ones
RequiredScopes []string
}

// New returns a new instance of a RootRunner with fallback for logging
Expand Down
1 change: 1 addition & 0 deletions internal/root_runner/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func (runner *Runner) Run(options RunnerOptions, args ...string) error {
Path: options.Path,
Strict: options.Strict,
UpstreamBranch: options.UpstreamBranch,
RequiredScopes: options.RequiredScopes,
}

commitPipe, err := commitpipeline.New(runner.Logger, runner.DebugLogger, &commitOptions, args...)
Expand Down
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ func bindRootFlags(rootCmd *cobra.Command) error {
if err != nil {
return err
}
rootCmd.Flags().StringSlice("required-scopes", nil, "forces scope to match one of the provided values")
err = viper.BindPFlag("commits.required-scopes", rootCmd.Flags().Lookup("required-scopes"))
if err != nil {
return err
}

// Not used. TODO: Documentation
rootCmd.Flags().StringP("path", "d", ".", "dir points to the path of the repository")
Expand Down
25 changes: 25 additions & 0 deletions pkg/text/check_required_scopes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package text

import (
"errors"
)

var errMissingRequiredScope = errors.New("required scope missing")

// RequiredScopeChecker creates case sensitive strict checker, validating that
// provided scope matches one from required scopes list
func RequiredScopeChecker(requiredScopes []string) func(string) error {
return func(scope string) error {
if len(requiredScopes) < 1 {
return nil
}

for _, rs := range requiredScopes {
if scope == rs {
return nil
}
}

return errMissingRequiredScope
}
}
36 changes: 36 additions & 0 deletions pkg/text/check_required_scopes_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package text

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestRequiredScopesChecker(t *testing.T) {
tests := map[error][]struct {
scope string
requiredScopes []string
}{
nil: {
{"ci", []string{}},
{"ui", nil},
{"ci", []string{"ci", "project-1", "repo", "ui"}},
{"ci", []string{"ui", "project-1", "repo", "ci"}},
{"repo", []string{"repo"}},
},
errMissingRequiredScope: {
{"SECURITY", []string{"security", "ci", "ui"}},
{"SeCuRiTy", []string{"security", "ci", "ui"}},
{"sth", []string{"security", "ci", "ui"}},
{"se curity", []string{"security", "ci", "ui"}},
{"", []string{"repo"}},
},
}

for expected, testCases := range tests {
for _, testCase := range testCases {
err := RequiredScopeChecker(testCase.requiredScopes)(testCase.scope)
assert.Equal(t, expected, err)
}
}
}

0 comments on commit 001d0e8

Please sign in to comment.