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 support for syntax which finds directory anywhere in repository #69

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
37 changes: 35 additions & 2 deletions internal/check/file_exists.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"

"github.com/mszostok/codeowners-validator/internal/ctxutil"

Expand All @@ -28,6 +29,7 @@ func (f *FileExist) Check(ctx context.Context, in Input) (Output, error) {

fullPath := filepath.Join(in.RepoDir, f.fnmatchPattern(entry.Pattern))
matches, err := zglob.Glob(fullPath)

switch {
case err == nil:
case errors.Is(err, os.ErrNotExist):
Expand All @@ -38,7 +40,20 @@ func (f *FileExist) Check(ctx context.Context, in Input) (Output, error) {
return Output{}, errors.Wrapf(err, "while checking if there is any file in %s matching pattern %s", in.RepoDir, entry.Pattern)
}

if len(matches) == 0 {
// Glob returns also matched directories, but only files count
foundFile := false
for _, name := range matches {
fi, err := os.Stat(name)
if err != nil {
return Output{}, errors.Wrap(err, "while checking if found path is a file")
}
if fi.Mode().IsRegular() {
foundFile = true
break
}
}

if !foundFile {
msg := fmt.Sprintf("%q does not match any files in repository", entry.Pattern)
bldr.ReportIssue(msg, WithEntry(entry))
}
Expand All @@ -48,13 +63,31 @@ func (f *FileExist) Check(ctx context.Context, in Input) (Output, error) {
}

func (*FileExist) fnmatchPattern(pattern string) string {
if len(pattern) >= 2 && pattern[:1] == "*" && pattern[1:2] != "*" {
noOfChars := len(pattern)
if noOfChars >= 2 && pattern[:1] == "*" && pattern[1:2] != "*" {
return "**/" + pattern
}

if noOfChars > 1 && pattern[:1] != "/" && !strings.HasSuffix(pattern, "*") {
return "**/" + pattern + "**/*"
}

lastIdx := strings.LastIndex(pattern, "/")
// handle such pattern: `**/foo`
// we need to add the **/* to find all files, without that Glob
// will return only the first match which can be still a directory
if lastIdx != -1 && !hasSingleAsterisk(pattern) {
return pattern + "**/*"
}

return pattern
}

// do not match patterns like `docs/*` but match `docs/**`
func hasSingleAsterisk(pattern string) bool {
return strings.HasSuffix(pattern, "*") && !strings.HasSuffix(pattern, "**")
}

func (*FileExist) Name() string {
return "File Exist Checker"
}
27 changes: 27 additions & 0 deletions internal/check/file_exists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,33 @@ func TestFileExists(t *testing.T) {
newErrIssue(`"a/**/b" does not match any files in repository`),
},
},
"Should match any file in an `apps` directory anywhere in your repository": {
codeownersInput: `
apps/ @octocat
`,
paths: []string{
"a/somewhere/apps/the/a/the/b/the/c/the/d/the/e/z/somewhere/appz/the/f/the/g/the/h/the/i/the/j/foo.js",
},
},
"Should match files like `docs/getting-started.md`": {
codeownersInput: `
docs/* docs@example.com
`,
paths: []string{
"/docs/getting-started.md",
},
},
"Should not match nested files like `docs/build-app/troubleshooting.md`": {
codeownersInput: `
docs/* docs@example.com
`,
paths: []string{
"/docs/build-app/troubleshooting.md",
},
expectedIssues: []check.Issue{
newErrIssue(`"docs/*" does not match any files in repository`),
},
},
}

for tn, tc := range tests {
Expand Down