-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extract logic to dedicated valid_syntax checker
- Loading branch information
Showing
8 changed files
with
219 additions
and
93 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
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
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
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,77 @@ | ||
package check | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/mszostok/codeowners-validator/internal/ctxutil" | ||
) | ||
|
||
var ( | ||
// A valid username/organization name has up to 39 characters (per GitHub Join page) | ||
// and is matched by the following regex: /^[a-z\d](?:[a-z\d]|-(?=[a-z\d])){0,38}$/i | ||
// A valid team name consists of alphanumerics, underscores and dashes | ||
usernameOrTeamRegexp = regexp.MustCompile(`^@(?i:[a-z\d](?:[a-z\d-]){0,37}[a-z\d](/[a-z\d](?:[a-z\d_-]*)[a-z\d])?)$`) | ||
|
||
// Per: https://davidcel.is/posts/stop-validating-email-addresses-with-regex/ | ||
// just check if there is '@' and a '.' afterwards | ||
emailRegexp = regexp.MustCompile(`.+@.+\..+`) | ||
) | ||
|
||
// ValidSyntax provides a syntax validation for CODEOWNERS file. | ||
// | ||
// If any line in your CODEOWNERS file contains invalid syntax, the file will not be detected and will | ||
// not be used to request reviews. Invalid syntax includes inline comments and user or team names that do not exist on GitHub. | ||
type ValidSyntax struct{} | ||
|
||
// NewValidSyntax returns new ValidSyntax instance. | ||
func NewValidSyntax() *ValidSyntax { | ||
return &ValidSyntax{} | ||
} | ||
|
||
// Check for syntax issues in your CODEOWNERS file. | ||
func (ValidSyntax) Check(ctx context.Context, in Input) (Output, error) { | ||
var bldr OutputBuilder | ||
|
||
for _, entry := range in.CodeownersEntries { | ||
if ctxutil.ShouldExit(ctx) { | ||
return Output{}, ctx.Err() | ||
} | ||
|
||
if entry.Pattern == "" { | ||
bldr.ReportIssue("Missing pattern", WithEntry(entry)) | ||
} | ||
|
||
if len(entry.Owners) == 0 { | ||
bldr.ReportIssue("Missing owner, at least one owner is required", WithEntry(entry)) | ||
} | ||
|
||
ownersLoop: | ||
for _, item := range entry.Owners { | ||
switch { | ||
case strings.EqualFold(item, "#"): | ||
msg := "Comment (# sign) is not allowed in line with pattern entry. The correct format is: pattern owner1 ... ownerN" | ||
bldr.ReportIssue(msg, WithEntry(entry)) | ||
break ownersLoop // no need to check for the rest items in this line, as the whole line is already marked as invalid | ||
case strings.HasPrefix(item, "@"): | ||
if !usernameOrTeamRegexp.MatchString(item) { | ||
msg := fmt.Sprintf("Owner '%s' does not look like a GitHub username or team name", item) | ||
bldr.ReportIssue(msg, WithEntry(entry), WithSeverity(Warning)) | ||
} | ||
default: | ||
if !emailRegexp.MatchString(item) { | ||
msg := fmt.Sprintf("Owner '%s' does not look like an email", item) | ||
bldr.ReportIssue(msg, WithEntry(entry)) | ||
} | ||
} | ||
} | ||
} | ||
|
||
return bldr.Output(), nil | ||
} | ||
|
||
func (ValidSyntax) Name() string { | ||
return "Valid Syntax Checker" | ||
} |
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,125 @@ | ||
package check_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/mszostok/codeowners-validator/internal/check" | ||
"github.com/mszostok/codeowners-validator/internal/ptr" | ||
"github.com/mszostok/codeowners-validator/pkg/codeowners" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestValidSyntaxChecker(t *testing.T) { | ||
tests := map[string]struct { | ||
codeowners string | ||
issue check.Issue | ||
}{ | ||
"No owners": { | ||
codeowners: `*`, | ||
issue: check.Issue{ | ||
Severity: check.Error, | ||
LineNo: ptr.Uint64Ptr(1), | ||
Message: "Missing owner, at least one owner is required", | ||
}, | ||
}, | ||
"Bad username": { | ||
codeowners: `pkg/github.com/** @-`, | ||
issue: check.Issue{ | ||
Severity: check.Warning, | ||
LineNo: ptr.Uint64Ptr(1), | ||
Message: "Owner '@-' does not look like a GitHub username or team name", | ||
}, | ||
}, | ||
"Bad org": { | ||
codeowners: `* @bad+org`, | ||
issue: check.Issue{ | ||
Severity: check.Warning, | ||
LineNo: ptr.Uint64Ptr(1), | ||
Message: "Owner '@bad+org' does not look like a GitHub username or team name", | ||
}, | ||
}, | ||
"Bad team name on first place": { | ||
codeowners: `* @org/+not+a+good+name`, | ||
issue: check.Issue{ | ||
Severity: check.Warning, | ||
LineNo: ptr.Uint64Ptr(1), | ||
Message: "Owner '@org/+not+a+good+name' does not look like a GitHub username or team name", | ||
}, | ||
}, | ||
"Bad team name on second place": { | ||
codeowners: `* @org/hakuna-matata @org/-a-team`, | ||
issue: check.Issue{ | ||
Severity: check.Warning, | ||
LineNo: ptr.Uint64Ptr(1), | ||
Message: "Owner '@org/-a-team' does not look like a GitHub username or team name", | ||
}, | ||
}, | ||
"Doesn't look like username, team name, nor email": { | ||
codeowners: `* something_weird`, | ||
issue: check.Issue{ | ||
Severity: check.Error, | ||
LineNo: ptr.Uint64Ptr(1), | ||
Message: "Owner 'something_weird' does not look like an email", | ||
}, | ||
}, | ||
"Comment in pattern line": { | ||
codeowners: `* @org/hakuna-matata # this should be an error`, | ||
issue: check.Issue{ | ||
Severity: check.Error, | ||
LineNo: ptr.Uint64Ptr(1), | ||
Message: "Comment (# sign) is not allowed in line with pattern entry. The correct format is: pattern owner1 ... ownerN", | ||
}, | ||
}, | ||
} | ||
for tn, tc := range tests { | ||
t.Run(tn, func(t *testing.T) { | ||
// when | ||
out, err := check.NewValidSyntax(). | ||
Check(context.Background(), loadInput(tc.codeowners)) | ||
|
||
// then | ||
require.NoError(t, err) | ||
|
||
require.Len(t, out.Issues, 1) | ||
assert.EqualValues(t, tc.issue, out.Issues[0]) | ||
}) | ||
} | ||
} | ||
|
||
func TestValidSyntaxZeroValueEntry(t *testing.T) { | ||
// given | ||
zeroValueInput := check.Input{ | ||
CodeownersEntries: []codeowners.Entry{ | ||
{ | ||
LineNo: 0, | ||
Pattern: "", | ||
Owners: nil, | ||
}, | ||
}, | ||
} | ||
expIssues := []check.Issue{ | ||
{ | ||
LineNo: ptr.Uint64Ptr(0), | ||
Severity: check.Error, | ||
Message: "Missing pattern", | ||
}, | ||
{ | ||
LineNo: ptr.Uint64Ptr(0), | ||
Severity: check.Error, | ||
Message: "Missing owner, at least one owner is required", | ||
}, | ||
} | ||
|
||
// when | ||
out, err := check.NewValidSyntax(). | ||
Check(context.Background(), zeroValueInput) | ||
|
||
// then | ||
require.NoError(t, err) | ||
|
||
require.Len(t, out.Issues, 2) | ||
assert.EqualValues(t, expIssues, out.Issues) | ||
} |
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
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
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