Skip to content

Commit 35f0800

Browse files
committed
detect license file by const filename
1 parent bd2b334 commit 35f0800

File tree

10 files changed

+160
-221
lines changed

10 files changed

+160
-221
lines changed

models/repo/license.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,10 @@ func CopyLicense(ctx context.Context, originalRepo, destRepo *Repository) error
111111
}
112112
return nil
113113
}
114+
115+
// CleanRepoLicenses will remove all license record of the repo
116+
func CleanRepoLicenses(ctx context.Context, repo *Repository) error {
117+
return db.DeleteBeans(ctx, &RepoLicense{
118+
RepoID: repo.ID,
119+
})
120+
}

modules/util/path.go

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -283,47 +283,38 @@ func CommonSkip(name string) bool {
283283
return false
284284
}
285285

286-
type FileType string
287-
288-
const (
289-
FileTypeReadme FileType = "readme"
290-
FileTypeLicense FileType = "license"
291-
)
292-
293-
// IsFileName reports whether name looks like a target name file
286+
// IsReadmeFileName reports whether name looks like a README file
294287
// based on its name.
295-
func IsFileName(name string, fileType FileType) bool {
288+
func IsReadmeFileName(name string) bool {
296289
name = strings.ToLower(name)
297-
lenFileType := len(fileType)
298-
if len(name) < lenFileType {
290+
if len(name) < 6 {
299291
return false
300-
} else if len(name) == lenFileType {
301-
return name == string(fileType)
292+
} else if len(name) == 6 {
293+
return name == "readme"
302294
}
303-
return name[:lenFileType+1] == string(fileType)+"."
295+
return name[:7] == "readme."
304296
}
305297

306-
// IsFileExtension reports whether name looks like a target name file
298+
// IsReadmeFileExtension reports whether name looks like a README file
307299
// based on its name. It will look through the provided extensions and check if the file matches
308300
// one of the extensions and provide the index in the extension list.
309301
// If the filename is `readme.` with an unmatched extension it will match with the index equaling
310302
// the length of the provided extension list.
311303
// Note that the '.' should be provided in ext, e.g ".md"
312-
func IsFileExtension(name string, fileType FileType, ext ...string) (int, bool) {
304+
func IsReadmeFileExtension(name string, ext ...string) (int, bool) {
313305
name = strings.ToLower(name)
314-
lenFileType := len(fileType)
315-
if len(name) < lenFileType || name[:lenFileType] != string(fileType) {
306+
if len(name) < 6 || name[:6] != "readme" {
316307
return 0, false
317308
}
318309

319310
for i, extension := range ext {
320311
extension = strings.ToLower(extension)
321-
if name[lenFileType:] == extension {
312+
if name[6:] == extension {
322313
return i, true
323314
}
324315
}
325316

326-
if name[lenFileType] == '.' {
317+
if name[6] == '.' {
327318
return len(ext), true
328319
}
329320

modules/util/path_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ func TestMisc_IsReadmeFileName(t *testing.T) {
7676
}
7777

7878
for _, testCase := range trueTestCases {
79-
assert.True(t, IsFileName(testCase, FileTypeReadme))
79+
assert.True(t, IsReadmeFileName(testCase))
8080
}
8181
for _, testCase := range falseTestCases {
82-
assert.False(t, IsFileName(testCase, FileTypeReadme))
82+
assert.False(t, IsReadmeFileName(testCase))
8383
}
8484

8585
type extensionTestcase struct {
@@ -131,7 +131,7 @@ func TestMisc_IsReadmeFileName(t *testing.T) {
131131
}
132132

133133
for _, testCase := range testCasesExtensions {
134-
idx, ok := IsFileExtension(testCase.name, FileTypeReadme, exts...)
134+
idx, ok := IsReadmeFileExtension(testCase.name, exts...)
135135
assert.Equal(t, testCase.expected, ok)
136136
assert.Equal(t, testCase.idx, idx)
137137
}

routers/web/repo/branch.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,7 @@ func Branches(ctx *context.Context) {
8989
pager := context.NewPagination(int(branchesCount), pageSize, page, 5)
9090
pager.SetDefaultParams(ctx)
9191
ctx.Data["Page"] = pager
92-
93-
ctx.Data["DetectedLicenseFileName"], err = repo_service.GetDetectedLicenseFileName(ctx, ctx.Repo.Repository, ctx.Repo.Commit)
94-
if err != nil {
95-
ctx.ServerError("GetDetectedLicenseFileName", err)
96-
return
97-
}
92+
ctx.Data["LicenseFileName"] = repo_service.LicenseFileName
9893
ctx.HTML(http.StatusOK, tplBranch)
9994
}
10095

routers/web/repo/commit.go

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,7 @@ func Commits(ctx *context.Context) {
101101
pager := context.NewPagination(int(commitsCount), pageSize, page, 5)
102102
pager.SetDefaultParams(ctx)
103103
ctx.Data["Page"] = pager
104-
ctx.Data["DetectedLicenseFileName"], err = repo_service.GetDetectedLicenseFileName(ctx, ctx.Repo.Repository, ctx.Repo.Commit)
105-
if err != nil {
106-
ctx.ServerError("GetDetectedLicenseFileName", err)
107-
return
108-
}
104+
ctx.Data["LicenseFileName"] = repo_service.LicenseFileName
109105
ctx.HTML(http.StatusOK, tplCommits)
110106
}
111107

@@ -223,11 +219,7 @@ func SearchCommits(ctx *context.Context) {
223219
ctx.Data["Username"] = ctx.Repo.Owner.Name
224220
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
225221
ctx.Data["RefName"] = ctx.Repo.RefName
226-
ctx.Data["DetectedLicenseFileName"], err = repo_service.GetDetectedLicenseFileName(ctx, ctx.Repo.Repository, ctx.Repo.Commit)
227-
if err != nil {
228-
ctx.ServerError("GetDetectedLicenseFileName", err)
229-
return
230-
}
222+
ctx.Data["LicenseFileName"] = repo_service.LicenseFileName
231223
ctx.HTML(http.StatusOK, tplCommits)
232224
}
233225

@@ -273,12 +265,7 @@ func FileHistory(ctx *context.Context) {
273265
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
274266
pager.SetDefaultParams(ctx)
275267
ctx.Data["Page"] = pager
276-
277-
ctx.Data["DetectedLicenseFileName"], err = repo_service.GetDetectedLicenseFileName(ctx, ctx.Repo.Repository, ctx.Repo.Commit)
278-
if err != nil {
279-
ctx.ServerError("GetDetectedLicenseFileName", err)
280-
return
281-
}
268+
ctx.Data["LicenseFileName"] = repo_service.LicenseFileName
282269
ctx.HTML(http.StatusOK, tplCommits)
283270
}
284271

routers/web/repo/view.go

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,99 @@ const (
6969
tplMigrating base.TplName = "repo/migrate/migrating"
7070
)
7171

72+
// locate a README for a tree in one of the supported paths.
73+
//
74+
// entries is passed to reduce calls to ListEntries(), so
75+
// this has precondition:
76+
//
77+
// entries == ctx.Repo.Commit.SubTree(ctx.Repo.TreePath).ListEntries()
78+
//
79+
// FIXME: There has to be a more efficient way of doing this
80+
func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, tryWellKnownDirs bool) (string, *git.TreeEntry, error) {
81+
// Create a list of extensions in priority order
82+
// 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
83+
// 2. Txt files - e.g. README.txt
84+
// 3. No extension - e.g. README
85+
exts := append(localizedExtensions(".md", ctx.Locale.Language()), ".txt", "") // sorted by priority
86+
extCount := len(exts)
87+
readmeFiles := make([]*git.TreeEntry, extCount+1)
88+
89+
docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/)
90+
for _, entry := range entries {
91+
if tryWellKnownDirs && entry.IsDir() {
92+
// as a special case for the top-level repo introduction README,
93+
// fall back to subfolders, looking for e.g. docs/README.md, .gitea/README.zh-CN.txt, .github/README.txt, ...
94+
// (note that docsEntries is ignored unless we are at the root)
95+
lowerName := strings.ToLower(entry.Name())
96+
switch lowerName {
97+
case "docs":
98+
if entry.Name() == "docs" || docsEntries[0] == nil {
99+
docsEntries[0] = entry
100+
}
101+
case ".gitea":
102+
if entry.Name() == ".gitea" || docsEntries[1] == nil {
103+
docsEntries[1] = entry
104+
}
105+
case ".github":
106+
if entry.Name() == ".github" || docsEntries[2] == nil {
107+
docsEntries[2] = entry
108+
}
109+
}
110+
continue
111+
}
112+
if i, ok := util.IsReadmeFileExtension(entry.Name(), exts...); ok {
113+
log.Debug("Potential readme file: %s", entry.Name())
114+
if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].Name(), entry.Blob().Name()) {
115+
if entry.IsLink() {
116+
target, err := entry.FollowLinks()
117+
if err != nil && !git.IsErrBadLink(err) {
118+
return "", nil, err
119+
} else if target != nil && (target.IsExecutable() || target.IsRegular()) {
120+
readmeFiles[i] = entry
121+
}
122+
} else {
123+
readmeFiles[i] = entry
124+
}
125+
}
126+
}
127+
}
128+
var readmeFile *git.TreeEntry
129+
for _, f := range readmeFiles {
130+
if f != nil {
131+
readmeFile = f
132+
break
133+
}
134+
}
135+
136+
if ctx.Repo.TreePath == "" && readmeFile == nil {
137+
for _, subTreeEntry := range docsEntries {
138+
if subTreeEntry == nil {
139+
continue
140+
}
141+
subTree := subTreeEntry.Tree()
142+
if subTree == nil {
143+
// this should be impossible; if subTreeEntry exists so should this.
144+
continue
145+
}
146+
var err error
147+
childEntries, err := subTree.ListEntries()
148+
if err != nil {
149+
return "", nil, err
150+
}
151+
152+
subfolder, readmeFile, err := findReadmeFileInEntries(ctx, childEntries, false)
153+
if err != nil && !git.IsErrNotExist(err) {
154+
return "", nil, err
155+
}
156+
if readmeFile != nil {
157+
return path.Join(subTreeEntry.Name(), subfolder), readmeFile, nil
158+
}
159+
}
160+
}
161+
162+
return "", readmeFile, nil
163+
}
164+
72165
func renderDirectory(ctx *context.Context) {
73166
entries := renderDirectoryFiles(ctx, 1*time.Second)
74167
if ctx.Written() {
@@ -80,15 +173,37 @@ func renderDirectory(ctx *context.Context) {
80173
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
81174
}
82175

83-
subfolder, readmeFile, err := repo_service.FindFileInEntries(util.FileTypeReadme, entries, ctx.Repo.TreePath, ctx.Locale.Language(), true)
176+
subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)
84177
if err != nil {
85-
ctx.ServerError("findFileInEntries", err)
178+
ctx.ServerError("findReadmeFileInEntries", err)
86179
return
87180
}
88181

89182
renderReadmeFile(ctx, subfolder, readmeFile)
90183
}
91184

185+
// localizedExtensions prepends the provided language code with and without a
186+
// regional identifier to the provided extension.
187+
// Note: the language code will always be lower-cased, if a region is present it must be separated with a `-`
188+
// Note: ext should be prefixed with a `.`
189+
func localizedExtensions(ext, languageCode string) (localizedExts []string) {
190+
if len(languageCode) < 1 {
191+
return []string{ext}
192+
}
193+
194+
lowerLangCode := "." + strings.ToLower(languageCode)
195+
196+
if strings.Contains(lowerLangCode, "-") {
197+
underscoreLangCode := strings.ReplaceAll(lowerLangCode, "-", "_")
198+
indexOfDash := strings.Index(lowerLangCode, "-")
199+
// e.g. [.zh-cn.md, .zh_cn.md, .zh.md, _zh.md, .md]
200+
return []string{lowerLangCode + ext, underscoreLangCode + ext, lowerLangCode[:indexOfDash] + ext, "_" + lowerLangCode[1:indexOfDash] + ext, ext}
201+
}
202+
203+
// e.g. [.en.md, .md]
204+
return []string{lowerLangCode + ext, ext}
205+
}
206+
92207
type fileInfo struct {
93208
isTextFile bool
94209
isLFSFile bool
@@ -384,7 +499,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) {
384499
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
385500

386501
shouldRenderSource := ctx.FormString("display") == "source"
387-
readmeExist := util.IsFileName(blob.Name(), util.FileTypeReadme)
502+
readmeExist := util.IsReadmeFileName(blob.Name())
388503
ctx.Data["ReadmeExist"] = readmeExist
389504

390505
markupType := markup.DetectMarkupTypeByFileName(blob.Name())
@@ -963,11 +1078,7 @@ func renderHomeCode(ctx *context.Context) {
9631078
ctx.Data["TreeLink"] = treeLink
9641079
ctx.Data["TreeNames"] = treeNames
9651080
ctx.Data["BranchLink"] = branchLink
966-
ctx.Data["DetectedLicenseFileName"], err = repo_service.GetDetectedLicenseFileName(ctx, ctx.Repo.Repository, ctx.Repo.Commit)
967-
if err != nil {
968-
ctx.ServerError("GetDetectedLicenseFileName", err)
969-
return
970-
}
1081+
ctx.Data["LicenseFileName"] = repo_service.LicenseFileName
9711082
ctx.HTML(http.StatusOK, tplRepoHome)
9721083
}
9731084

services/repository/file_test.go renamed to routers/web/repo/view_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// Copyright 2023 The Gitea Authors. All rights reserved.
1+
// Copyright 2017 The Gitea Authors. All rights reserved.
2+
// Copyright 2014 The Gogs Authors. All rights reserved.
23
// SPDX-License-Identifier: MIT
34

4-
package repository
5+
package repo
56

67
import (
78
"reflect"

0 commit comments

Comments
 (0)