-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package codeowners | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
) | ||
|
||
var availableCheckers map[string]Checker | ||
|
||
func init() { | ||
availableCheckers = make(map[string]Checker) | ||
} | ||
|
||
// AvailableCheckers returns list of registered checkers | ||
func AvailableCheckers() []string { | ||
names := make([]string, len(availableCheckers)) | ||
i := 0 | ||
for checkerName := range availableCheckers { | ||
names[i] = checkerName | ||
i++ | ||
} | ||
return names | ||
} | ||
|
||
// RegisterChecker adds checker to be used later when checking CODEOWNERS files | ||
func RegisterChecker(name string, checker Checker) error { | ||
_, found := availableCheckers[name] | ||
if found { | ||
return fmt.Errorf("Checker %s already exists", name) | ||
} | ||
availableCheckers[name] = checker | ||
return nil | ||
} | ||
|
||
// Checker provides tools for validating CODEOWNER file contents | ||
type Checker interface { | ||
CheckLine(lineNo int, filePattern string, owners ...string) []CheckResult | ||
} | ||
|
||
// SeverityLevel exposes all possible levels of severity check results | ||
type SeverityLevel int | ||
|
||
// All possible severiy levels | ||
const ( | ||
Error SeverityLevel = iota // Error serverity level | ||
Warning // Warning serverity level | ||
) | ||
|
||
// String returns the string representation of this severity level | ||
func (l SeverityLevel) String() string { | ||
return [...]string{"Error", "Warning"}[l] | ||
} | ||
|
||
// CheckResult provides structured way to evaluate results of a CODEOWNERS validation check | ||
type CheckResult struct { | ||
LineNo int | ||
Message string | ||
Severity SeverityLevel | ||
CheckName string | ||
} | ||
|
||
// Check evaluates the file contents against the checkers and return the results back. | ||
func Check(r io.Reader, checkers ...string) ([]CheckResult, error) { | ||
results := []CheckResult{} | ||
decoder := NewDecoder(r) | ||
for decoder.More() { | ||
token, lineNo := decoder.Token() | ||
for _, checker := range checkers { | ||
c, ok := availableCheckers[checker] | ||
if !ok { | ||
return nil, fmt.Errorf("'%s' not found", checker) | ||
} | ||
lineResults := c.CheckLine(lineNo, token.Path(), token.Owners()...) | ||
results = append(results, lineResults...) | ||
} | ||
} | ||
|
||
return results, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package codeowners_test | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/fmenezes/codeowners" | ||
_ "github.com/fmenezes/codeowners/checkers" | ||
) | ||
|
||
const dummyCheckerName string = "dummy" | ||
|
||
type dummyChecker struct { | ||
} | ||
|
||
func (c dummyChecker) CheckLine(lineNo int, filePath string, owners ...string) []codeowners.CheckResult { | ||
return []codeowners.CheckResult{ | ||
{ | ||
LineNo: 1, | ||
Message: "Dummy Error", | ||
Severity: codeowners.Error, | ||
CheckName: dummyCheckerName, | ||
}, | ||
} | ||
} | ||
|
||
func TestRegisterChecker(t *testing.T) { | ||
err := codeowners.RegisterChecker(dummyCheckerName, dummyChecker{}) | ||
if err != nil { | ||
t.Error(err) | ||
} | ||
found := false | ||
for _, checker := range codeowners.AvailableCheckers() { | ||
if checker == dummyCheckerName { | ||
found = true | ||
} | ||
} | ||
if !found { | ||
t.Errorf("%s not properly registered", dummyCheckerName) | ||
} | ||
} | ||
|
||
func TestRegisterCheckerAgain(t *testing.T) { | ||
codeowners.RegisterChecker(dummyCheckerName, dummyChecker{}) | ||
err := codeowners.RegisterChecker(dummyCheckerName, dummyChecker{}) | ||
if err == nil { | ||
t.Errorf("%s should be already registered, expecting an error", dummyCheckerName) | ||
} | ||
} | ||
|
||
func TestSeverityLevelLabels(t *testing.T) { | ||
if codeowners.Error.String() != "Error" { | ||
t.Errorf("codeowners.Error.String() should evaluate to 'Error'") | ||
} | ||
if codeowners.Warning.String() != "Warning" { | ||
t.Errorf("codeowners.Warning.String() should evaluate to 'Warning'") | ||
} | ||
} | ||
|
||
func TestSimpleCheck(t *testing.T) { | ||
input := `filepattern @owner` | ||
want := []codeowners.CheckResult{ | ||
{ | ||
LineNo: 1, | ||
Message: "Dummy Error", | ||
Severity: codeowners.Error, | ||
CheckName: dummyCheckerName, | ||
}, | ||
} | ||
|
||
codeowners.RegisterChecker(dummyCheckerName, dummyChecker{}) | ||
got, err := codeowners.Check(strings.NewReader(input), dummyCheckerName) | ||
if err != nil { | ||
t.Errorf("Input %s, Error %v", input, err) | ||
} | ||
if !reflect.DeepEqual(want, got) { | ||
t.Errorf("Input %s, Want %v, Got %v", input, want, got) | ||
} | ||
} | ||
|
||
func ExampleCheck() { | ||
contents := strings.NewReader(`filepattern`) | ||
checks, err := codeowners.Check(contents, "NoOwner") | ||
if err != nil { | ||
|
||
} | ||
for _, check := range checks { | ||
fmt.Printf("%d ::%s:: %s [%s]\n", check.LineNo, check.Severity, check.Message, check.CheckName) | ||
} | ||
//Output: | ||
//1 ::Error:: No owners specified [NoOwner] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package checkers contain pre built checkers to validate CODEOWNER files | ||
package checkers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package checkers | ||
|
||
import "github.com/fmenezes/codeowners" | ||
|
||
const noOwnerCheckerName string = "NoOwner" | ||
|
||
func init() { | ||
codeowners.RegisterChecker(noOwnerCheckerName, NoOwner{}) | ||
} | ||
|
||
// NoOwner represents checker to decide validate presence of owners in each of CODEOWNERS lines | ||
type NoOwner struct{} | ||
|
||
// CheckLine runs this NoOwner's check against each line | ||
func (c NoOwner) CheckLine(lineNo int, pattern string, owners ...string) []codeowners.CheckResult { | ||
results := []codeowners.CheckResult{} | ||
|
||
if len(owners) == 0 { | ||
results = append(results, codeowners.CheckResult{ | ||
LineNo: lineNo, | ||
Message: "No owners specified", | ||
Severity: codeowners.Error, | ||
CheckName: noOwnerCheckerName, | ||
}) | ||
|
||
} | ||
return results | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package checkers_test | ||
|
||
import ( | ||
"reflect" | ||
"testing" | ||
|
||
"github.com/fmenezes/codeowners" | ||
"github.com/fmenezes/codeowners/checkers" | ||
) | ||
|
||
func TestNoOwnerCheck(t *testing.T) { | ||
input := struct { | ||
lineNo int | ||
pattern string | ||
owners []string | ||
}{ | ||
lineNo: 1, | ||
pattern: "filepattern", | ||
owners: []string{}, | ||
} | ||
want := []codeowners.CheckResult{ | ||
{ | ||
LineNo: 1, | ||
Message: "No owners specified", | ||
Severity: codeowners.Error, | ||
CheckName: "NoOwner", | ||
}, | ||
} | ||
|
||
checker := checkers.NoOwner{} | ||
got := checker.CheckLine(input.lineNo, input.pattern, input.owners...) | ||
if !reflect.DeepEqual(got, want) { | ||
t.Errorf("Input: %v, Want: %v, Got: %v", input, want, got) | ||
} | ||
} |