Skip to content

Commit b7b4633

Browse files
committed
Add checks for missing license file
If a file with one of the names supported by the licensee gem is in the root of the repository containing the text of a standard license, GitHub recognizes the license type and enables some nice integrations. So having such a file is best practices. These checks are only for the presence of the file, rather than their contents.
1 parent 2ba9047 commit b7b4633

File tree

6 files changed

+82
-6
lines changed

6 files changed

+82
-6
lines changed

check/checkconfigurations/checkconfigurations.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,21 @@ var configurations = []Type{
971971
ErrorModes: []checkmode.Type{checkmode.Strict},
972972
CheckFunction: checkfunctions.MissingReadme,
973973
},
974+
{
975+
ProjectType: projecttype.Library,
976+
Category: "structure",
977+
Subcategory: "",
978+
ID: "",
979+
Brief: "no license file",
980+
Description: "",
981+
MessageTemplate: "No license file found. See: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license",
982+
DisableModes: nil,
983+
EnableModes: []checkmode.Type{checkmode.Default},
984+
InfoModes: nil,
985+
WarningModes: []checkmode.Type{checkmode.Default},
986+
ErrorModes: []checkmode.Type{checkmode.Strict},
987+
CheckFunction: checkfunctions.MissingLicenseFile,
988+
},
974989
{
975990
ProjectType: projecttype.Sketch,
976991
Category: "structure",
@@ -1166,6 +1181,21 @@ var configurations = []Type{
11661181
ErrorModes: []checkmode.Type{checkmode.Strict},
11671182
CheckFunction: checkfunctions.MissingReadme,
11681183
},
1184+
{
1185+
ProjectType: projecttype.Sketch,
1186+
Category: "structure",
1187+
Subcategory: "",
1188+
ID: "",
1189+
Brief: "no license file",
1190+
Description: "",
1191+
MessageTemplate: "No license file found. See: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license",
1192+
DisableModes: nil,
1193+
EnableModes: []checkmode.Type{checkmode.Default},
1194+
InfoModes: nil,
1195+
WarningModes: []checkmode.Type{checkmode.Default},
1196+
ErrorModes: []checkmode.Type{checkmode.Strict},
1197+
CheckFunction: checkfunctions.MissingLicenseFile,
1198+
},
11691199
{
11701200
ProjectType: projecttype.Platform,
11711201
Category: "configuration files",
@@ -1196,6 +1226,21 @@ var configurations = []Type{
11961226
ErrorModes: []checkmode.Type{checkmode.Default},
11971227
CheckFunction: checkfunctions.BoardsTxtFormat,
11981228
},
1229+
{
1230+
ProjectType: projecttype.Platform,
1231+
Category: "structure",
1232+
Subcategory: "",
1233+
ID: "",
1234+
Brief: "no license file",
1235+
Description: "",
1236+
MessageTemplate: "No license file found. See: https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license",
1237+
DisableModes: nil,
1238+
EnableModes: []checkmode.Type{checkmode.Default},
1239+
InfoModes: nil,
1240+
WarningModes: []checkmode.Type{checkmode.Default},
1241+
ErrorModes: []checkmode.Type{checkmode.Strict},
1242+
CheckFunction: checkfunctions.MissingLicenseFile,
1243+
},
11991244
{
12001245
ProjectType: projecttype.PackageIndex,
12011246
Category: "data",

check/checkfunctions/checkfunctions.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,25 +77,45 @@ func MissingReadme() (result checkresult.Type, output string) {
7777
readmeRegexp := regexp.MustCompile(`(?i)^readme\.(markdown)|(mdown)|(mkdn)|(md)|(textile)|(rdoc)|(org)|(creole)|(mediawiki)|(wiki)|(rst)|(asciidoc)|(adoc)|(asc)|(pod)|(txt)$`)
7878

7979
// https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-readmes#about-readmes
80-
if pathContainsReadme(checkdata.ProjectPath(), readmeRegexp) ||
81-
(checkdata.ProjectPath().Join("docs").Exist() && pathContainsReadme(checkdata.ProjectPath().Join("docs"), readmeRegexp)) ||
82-
(checkdata.ProjectPath().Join(".github").Exist() && pathContainsReadme(checkdata.ProjectPath().Join(".github"), readmeRegexp)) {
80+
if pathContainsRegexpMatch(checkdata.ProjectPath(), readmeRegexp) ||
81+
(checkdata.ProjectPath().Join("docs").Exist() && pathContainsRegexpMatch(checkdata.ProjectPath().Join("docs"), readmeRegexp)) ||
82+
(checkdata.ProjectPath().Join(".github").Exist() && pathContainsRegexpMatch(checkdata.ProjectPath().Join(".github"), readmeRegexp)) {
8383
return checkresult.Pass, ""
8484
}
8585

8686
return checkresult.Fail, ""
8787
}
8888

89-
// pathContainsReadme checks if the provided path contains a readme file recognized by GitHub.
90-
func pathContainsReadme(path *paths.Path, readmeRegexp *regexp.Regexp) bool {
89+
// MissingLicenseFile checks if the project has a license file that will be recognized by GitHub.
90+
func MissingLicenseFile() (result checkresult.Type, output string) {
91+
if checkdata.ProjectType() != checkdata.SuperProjectType() {
92+
return checkresult.NotRun, "License file not required for subprojects"
93+
}
94+
95+
// https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/licensing-a-repository#detecting-a-license
96+
// https://github.com/licensee/licensee/blob/master/docs/what-we-look-at.md#detecting-the-license-file
97+
// Should be `(?i)^(((un)?licen[sc]e)|(copy(ing|right))|(ofl)|(patents))(\.(?!spdx|header|gemspec).+)?$` but regexp package doesn't support negative lookahead, so only using "preferred extensions".
98+
// github.com/dlclark/regexp2 does support negative lookahead, but I'd prefer to stick with the standard package.
99+
licenseRegexp := regexp.MustCompile(`(?i)^(((un)?licen[sc]e)|(copy(ing|right))|(ofl)|(patents))(\.((md)|(markdown)|(txt)|(html)))?$`)
100+
101+
// License file must be in root of repo
102+
if pathContainsRegexpMatch(checkdata.ProjectPath(), licenseRegexp) {
103+
return checkresult.Pass, ""
104+
}
105+
106+
return checkresult.Fail, ""
107+
}
108+
109+
// pathContainsRegexpMatch checks if the provided path contains a file name matching the given regular expression.
110+
func pathContainsRegexpMatch(path *paths.Path, pathRegexp *regexp.Regexp) bool {
91111
listing, err := path.ReadDir()
92112
if err != nil {
93113
panic(err)
94114
}
95115
listing.FilterOutDirs()
96116

97117
for _, file := range listing {
98-
if readmeRegexp.MatchString(file.Base()) {
118+
if pathRegexp.MatchString(file.Base()) {
99119
return true
100120
}
101121
}

check/checkfunctions/checkfunctions_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,14 @@ func TestMissingReadme(t *testing.T) {
7171

7272
checkCheckFunction(MissingReadme, testTables, t)
7373
}
74+
75+
func TestMissingLicenseFile(t *testing.T) {
76+
testTables := []checkFunctionTestTable{
77+
{"Subproject", "no-license-file", projecttype.Sketch, projecttype.Library, checkresult.NotRun, ""},
78+
{"Has license", "license-file", projecttype.Sketch, projecttype.Sketch, checkresult.Pass, ""},
79+
{"Has license in subfolder", "license-file-in-subfolder", projecttype.Sketch, projecttype.Sketch, checkresult.Fail, ""},
80+
{"No license", "no-license-file", projecttype.Sketch, projecttype.Sketch, checkresult.Fail, ""},
81+
}
82+
83+
checkCheckFunction(MissingLicenseFile, testTables, t)
84+
}

check/checkfunctions/testdata/general/license-file-in-subfolder/subfolder/LICENSE

Whitespace-only changes.

check/checkfunctions/testdata/general/license-file/LICENSE

Whitespace-only changes.

check/checkfunctions/testdata/general/no-license-file/.gitkeep

Whitespace-only changes.

0 commit comments

Comments
 (0)