diff --git a/flint/app.go b/flint/app.go index 555fb3e..2c273e5 100644 --- a/flint/app.go +++ b/flint/app.go @@ -12,7 +12,7 @@ func NewApp() *cli.App { app := cli.NewApp() app.Name = "flint" app.Usage = "Check a project for common sources of contributor friction" - app.Version = "0.0.4" + app.Version = "0.0.4.1" app.Flags = []cli.Flag{ cli.BoolFlag{"skip-readme", "skip check for README", ""}, cli.BoolFlag{"skip-contributing", "skip check for contributing guide", ""}, diff --git a/flint/errors.go b/flint/errors.go index 8f78260..418d739 100644 --- a/flint/errors.go +++ b/flint/errors.go @@ -31,21 +31,35 @@ var ReadmeNotFoundInfo = &LintError{ "Every project begins with a README. http://bit.ly/1dqUYQF", } +var ReadmeLowercaseInfo = &LintError{ + 0, + "README file name should be in UPPERCASE for sorting.", +} + var ContributingNotFoundError = &LintError{ 2, "CONTRIBUTING guide not found", } - var ContributingNotFoundInfo = &LintError{ 0, "Add a guide for potential contributors. http://git.io/z-TiGg", } +var ContributingLowercaseInfo = &LintError{ + 0, + "CONTRIBUTING guide file name should be in UPPERCASE for sorting.", +} + var LicenseNotFoundError = &LintError{ 2, "LICENSE not found", } +var LicenseLowercaseInfo = &LintError{ + 0, + "LICENSE file name should be in UPPERCASE for sorting.", +} + var LicenseNotFoundInfo = &LintError{ 0, "Add a license to protect yourself and your users. http://choosealicense.com/", diff --git a/flint/linter.go b/flint/linter.go index 68c84ac..95e69ce 100644 --- a/flint/linter.go +++ b/flint/linter.go @@ -14,8 +14,11 @@ type Flags struct { type Project interface { CheckReadme() bool + CheckLowercaseReadme() bool CheckContributing() bool + CheckLowercaseContributing() bool CheckLicense() bool + CheckLowercaseLicense() bool CheckBootstrap() bool CheckTestScript() bool } @@ -30,21 +33,37 @@ func (l *Linter) Run(p Project, flags *Flags) (summary *Summary, err error) { summary = &Summary{} if flags.RunReadme && !p.CheckReadme() { - summary.AppendError(ReadmeNotFoundError) - summary.AppendError(ReadmeNotFoundInfo) + if p.CheckLowercaseReadme() { + summary.AppendError(ReadmeLowercaseInfo) + } else { + summary.AppendError(ReadmeNotFoundError) + summary.AppendError(ReadmeNotFoundInfo) + } } + if flags.RunContributing && !p.CheckContributing() { - summary.AppendError(ContributingNotFoundError) - summary.AppendError(ContributingNotFoundInfo) + if p.CheckLowercaseContributing(){ + summary.AppendError(ContributingLowercaseInfo) + } else { + summary.AppendError(ContributingNotFoundError) + summary.AppendError(ContributingNotFoundInfo) + } } + if flags.RunLicense && !p.CheckLicense() { - summary.AppendError(LicenseNotFoundError) - summary.AppendError(LicenseNotFoundInfo) + if p.CheckLowercaseLicense() { + summary.AppendError(LicenseLowercaseInfo) + } else { + summary.AppendError(LicenseNotFoundError) + summary.AppendError(LicenseNotFoundInfo) + } } + if flags.RunBootstrap && !p.CheckBootstrap() { summary.AppendError(BootstrapNotFoundError) summary.AppendError(BootstrapNotFoundInfo) } + if flags.RunTestScript && !p.CheckTestScript() { summary.AppendError(TestScriptNotFoundError) summary.AppendError(TestScriptNotFoundInfo) diff --git a/flint/linter_test.go b/flint/linter_test.go index dd873cd..3817672 100644 --- a/flint/linter_test.go +++ b/flint/linter_test.go @@ -45,14 +45,26 @@ func (p *FakeProject) CheckReadme() bool { return p.RunReadme } +func (p *FakeProject) CheckLowercaseReadme() bool { + return p.RunReadme +} + func (p *FakeProject) CheckContributing() bool { return p.RunContributing } +func (p *FakeProject) CheckLowercaseContributing() bool { + return p.RunContributing +} + func (p *FakeProject) CheckLicense() bool { return p.RunLicense } +func (p *FakeProject) CheckLowercaseLicense() bool { + return p.RunLicense +} + func (p *FakeProject) CheckBootstrap() bool { return p.RunBootstrap } diff --git a/flint/local_project.go b/flint/local_project.go index 35e98de..901837b 100644 --- a/flint/local_project.go +++ b/flint/local_project.go @@ -18,14 +18,26 @@ func (l *LocalProject) CheckReadme() bool { return l.searchPath("README*") } +func (l *LocalProject) CheckLowercaseReadme() bool { + return l.searchPath("[Rr]eadme*") +} + func (l *LocalProject) CheckContributing() bool { return l.searchPath("CONTRIBUTING*") } +func (l *LocalProject) CheckLowercaseContributing() bool { + return l.searchPath("[Cc]ontributing*") +} + func (l *LocalProject) CheckLicense() bool { return l.searchPath("LICENSE*") } +func (l *LocalProject) CheckLowercaseLicense() bool { + return l.searchPath("[Ll]icense*") +} + func (l *LocalProject) CheckBootstrap() bool { return l.searchPath("script/bootstrap") } diff --git a/flint/local_project_test.go b/flint/local_project_test.go index 357d739..0218a7d 100644 --- a/flint/local_project_test.go +++ b/flint/local_project_test.go @@ -21,6 +21,8 @@ var readmeTests = []scenarios{ {"README.rst", true}, {"docs/README.rst", false}, {"docs/README.md", false}, + {"Readme.md", false}, + {"readme.md", false}, } func TestLocalProjectFindsReadme(t *testing.T) { @@ -40,6 +42,36 @@ func TestLocalProjectFindsReadme(t *testing.T) { } } +var lowercaseReadmeTests = []scenarios{ + {"", false}, + {"README", false}, + {"README.md", false}, + {"README.rst", false}, + {"docs/README.rst", false}, + {"docs/README.md", false}, + {"Readme.md", true}, + {"readme.md", true}, + {"docs/Readme.rst", false}, + {"docs/Readme.md", false}, +} + +func TestLocalProjectFindsLowercaseReadme(t *testing.T) { + for _, tt := range readmeTests { + setup := setupLocalProjectTest() + defer setup.Teardown() + + if len(tt.path) > 0 { + setup.WriteFile(tt.path, "The README") + } + + project := &LocalProject{Path: setup.Path} + actual := project.CheckReadme() + + msg := fmt.Sprintf("Path: '%s', Errors: %d", tt.path, tt.result) + assert.Equal(t, tt.result, actual, msg) + } +} + var contributingTests = []scenarios{ {"", false}, {"CONTRIBUTING", true}, diff --git a/flint/remote_project.go b/flint/remote_project.go index b05e518..4406a68 100644 --- a/flint/remote_project.go +++ b/flint/remote_project.go @@ -57,15 +57,27 @@ func (l *RemoteProject) searchPath(re *regexp.Regexp) bool { } func (l *RemoteProject) CheckReadme() bool { - return l.searchPath(regexp.MustCompile(`README`)) + return l.searchPath(regexp.MustCompile(`(?i)README`)) +} + +func (l *RemoteProject) CheckLowercaseReadme() bool { + return l.searchPath(regexp.MustCompile(`[Rr]eadme`)) } func (l *RemoteProject) CheckContributing() bool { - return l.searchPath(regexp.MustCompile(`CONTRIBUTING`)) + return l.searchPath(regexp.MustCompile(`(?i)CONTRIBUTING`)) +} + +func (l *RemoteProject) CheckLowercaseContributing() bool { + return l.searchPath(regexp.MustCompile(`[Cc]ontributing`)) } func (l *RemoteProject) CheckLicense() bool { - return l.searchPath(regexp.MustCompile(`LICENSE`)) + return l.searchPath(regexp.MustCompile(`(?i)LICENSE`)) +} + +func (l *RemoteProject) CheckLowercaseLicense() bool { + return l.searchPath(regexp.MustCompile(`[Ll]icense`)) } func (l *RemoteProject) CheckBootstrap() bool { diff --git a/flint/remote_project_test.go b/flint/remote_project_test.go index d198351..2350d8c 100644 --- a/flint/remote_project_test.go +++ b/flint/remote_project_test.go @@ -41,7 +41,20 @@ func TestRemoteProjectCheckReadme(t *testing.T) { project = &RemoteProject{FullName: "projects/lowercase-names"} err = project.Fetch(fetcher) assert.Nil(t, err) - assert.False(t, project.CheckReadme()) + assert.True(t, project.CheckReadme()) +} + +func TestRemoteProjectCheckLowercaseReadme(t *testing.T) { + project := &RemoteProject{FullName: "octokit/octokit.rb"} + fetcher := &FakeProjectFetcher{} + err := project.Fetch(fetcher) + assert.Nil(t, err) + assert.True(t, project.CheckReadme()) + + project = &RemoteProject{FullName: "projects/lowercase-names"} + err = project.Fetch(fetcher) + assert.Nil(t, err) + assert.True(t, project.CheckLowercaseReadme()) } func TestRemoteProjectCheckContributing(t *testing.T) { @@ -54,7 +67,20 @@ func TestRemoteProjectCheckContributing(t *testing.T) { project = &RemoteProject{FullName: "projects/lowercase-names"} err = project.Fetch(fetcher) assert.Nil(t, err) - assert.False(t, project.CheckContributing()) + assert.True(t, project.CheckContributing()) +} + +func TestRemoteProjectCheckLowercaseContributing(t *testing.T) { + project := &RemoteProject{FullName: "octokit/octokit.rb"} + fetcher := &FakeProjectFetcher{} + err := project.Fetch(fetcher) + assert.Nil(t, err) + assert.True(t, project.CheckContributing()) + + project = &RemoteProject{FullName: "projects/lowercase-names"} + err = project.Fetch(fetcher) + assert.Nil(t, err) + assert.True(t, project.CheckLowercaseContributing()) } func TestRemoteProjectCheckLicense(t *testing.T) { @@ -67,7 +93,20 @@ func TestRemoteProjectCheckLicense(t *testing.T) { project = &RemoteProject{FullName: "projects/lowercase-names"} err = project.Fetch(fetcher) assert.Nil(t, err) - assert.False(t, project.CheckLicense()) + assert.True(t, project.CheckLicense()) +} + +func TestRemoteProjectCheckLowercaseLicense(t *testing.T) { + project := &RemoteProject{FullName: "octokit/octokit.rb"} + fetcher := &FakeProjectFetcher{} + err := project.Fetch(fetcher) + assert.Nil(t, err) + assert.True(t, project.CheckLicense()) + + project = &RemoteProject{FullName: "projects/lowercase-names"} + err = project.Fetch(fetcher) + assert.Nil(t, err) + assert.True(t, project.CheckLowercaseLicense()) } func TestRemoteProjectCheckBootstrap(t *testing.T) {