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

Refactor RepoRefByType #32413

Merged
merged 2 commits into from
Nov 5, 2024
Merged
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
8 changes: 4 additions & 4 deletions routers/api/v1/repo/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ func GetRawFile(ctx *context.APIContext) {
// required: true
// - name: filepath
// in: path
// description: filepath of the file to get
// description: path of the file to get, it should be "{ref}/{filepath}". If there is no ref could be inferred, it will be treated as the default branch
// type: string
// required: true
// - name: ref
// in: query
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
// description: "The name of the commit/branch/tag. Default the repository’s default branch"
// type: string
// required: false
// responses:
Expand Down Expand Up @@ -109,12 +109,12 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
// required: true
// - name: filepath
// in: path
// description: filepath of the file to get
// description: path of the file to get, it should be "{ref}/{filepath}". If there is no ref could be inferred, it will be treated as the default branch
// type: string
// required: true
// - name: ref
// in: query
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
// description: "The name of the commit/branch/tag. Default the repository’s default branch"
// type: string
// required: false
// responses:
Expand Down
2 changes: 1 addition & 1 deletion routers/web/goget.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func goGet(ctx *context.Context) {
insecure = "--insecure "
}

goGetImport := context.ComposeGoGetImport(ownerName, trimmedRepoName)
goGetImport := context.ComposeGoGetImport(ctx, ownerName, trimmedRepoName)

var cloneURL string
if setting.Repository.GoGetCloneURLProtocol == "ssh" {
Expand Down
12 changes: 6 additions & 6 deletions routers/web/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ func registerRoutes(m *web.Router) {
m.Get(".rss", feedEnabled, repo.TagsListFeedRSS)
m.Get(".atom", feedEnabled, repo.TagsListFeedAtom)
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, true))
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true}))
m.Post("/tags/delete", repo.DeleteTag, reqSignIn,
repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef())
}, ignSignIn, context.RepoAssignment, reqRepoCodeReader)
Expand All @@ -1337,7 +1337,7 @@ func registerRoutes(m *web.Router) {
m.Get(".rss", feedEnabled, repo.ReleasesFeedRSS)
m.Get(".atom", feedEnabled, repo.ReleasesFeedAtom)
}, ctxDataSet("EnableFeed", setting.Other.EnableFeed),
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, true))
repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefTag, context.RepoRefByTypeOptions{IgnoreNotExistErr: true}))
m.Get("/releases/attachments/{uuid}", repo.MustBeNotEmpty, repo.GetAttachment)
m.Get("/releases/download/{vTag}/{fileName}", repo.MustBeNotEmpty, repo.RedirectDownload)
m.Group("/releases", func() {
Expand Down Expand Up @@ -1535,7 +1535,7 @@ func registerRoutes(m *web.Router) {
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownloadOrLFS)
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByIDOrLFS)
// "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownloadOrLFS)
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.SingleDownloadOrLFS)
}, repo.MustBeNotEmpty)

m.Group("/raw", func() {
Expand All @@ -1544,7 +1544,7 @@ func registerRoutes(m *web.Router) {
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.SingleDownload)
m.Get("/blob/{sha}", context.RepoRefByType(context.RepoRefBlob), repo.DownloadByID)
// "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.SingleDownload)
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.SingleDownload)
}, repo.MustBeNotEmpty)

m.Group("/render", func() {
Expand All @@ -1559,7 +1559,7 @@ func registerRoutes(m *web.Router) {
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.RefCommits)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.RefCommits)
// "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.RefCommits)
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.RefCommits)
}, repo.MustBeNotEmpty)

m.Group("/blame", func() {
Expand All @@ -1582,7 +1582,7 @@ func registerRoutes(m *web.Router) {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.Home)
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.Home)
m.Get("/*", context.RepoRefByType(context.RepoRefLegacy), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
m.Get("/*", context.RepoRefByType(context.RepoRefUnknown), repo.Home) // "/*" route is deprecated, and kept for backward compatibility
}, repo.SetEditorconfigIfExists)

m.Get("/forks", context.RepoRef(), repo.Forks)
Expand Down
21 changes: 2 additions & 19 deletions services/context/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
Expand Down Expand Up @@ -306,24 +305,8 @@ func RepoRefForAPI(next http.Handler) http.Handler {
return
}

if ref := ctx.FormTrim("ref"); len(ref) > 0 {
commit, err := ctx.Repo.GitRepo.GetCommit(ref)
if err != nil {
if git.IsErrNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
}
return
}
ctx.Repo.Commit = commit
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
ctx.Repo.TreePath = ctx.PathParam("*")
next.ServeHTTP(w, req)
return
}

refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
// NOTICE: the "ref" here for internal usage only (e.g. woodpecker)
refName, _ := getRefNameLegacy(ctx.Base, ctx.Repo, ctx.FormTrim("ref"))
var err error

if ctx.Repo.GitRepo.IsBranchExist(refName) {
Expand Down
138 changes: 73 additions & 65 deletions services/context/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/httplib"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/optional"
Expand Down Expand Up @@ -306,11 +307,9 @@ func RetrieveTemplateRepo(ctx *Context, repo *repo_model.Repository) {
}

// ComposeGoGetImport returns go-get-import meta content.
func ComposeGoGetImport(owner, repo string) string {
/// setting.AppUrl is guaranteed to be parse as url
appURL, _ := url.Parse(setting.AppURL)

return path.Join(appURL.Host, setting.AppSubURL, url.PathEscape(owner), url.PathEscape(repo))
func ComposeGoGetImport(ctx context.Context, owner, repo string) string {
curAppURL, _ := url.Parse(httplib.GuessCurrentAppURL(ctx))
return path.Join(curAppURL.Host, setting.AppSubURL, url.PathEscape(owner), url.PathEscape(repo))
}

// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
Expand All @@ -332,7 +331,7 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
} else {
cloneURL = repo_model.ComposeHTTPSCloneURL(username, reponame)
}
goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(username, reponame), cloneURL)
goImportContent := fmt.Sprintf("%s git %s", ComposeGoGetImport(ctx, username, reponame), cloneURL)
htmlMeta := fmt.Sprintf(`<meta name="go-import" content="%s">`, html.EscapeString(goImportContent))
ctx.PlainText(http.StatusOK, htmlMeta)
}
Expand Down Expand Up @@ -744,7 +743,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
}

if ctx.FormString("go-get") == "1" {
ctx.Data["GoGetImport"] = ComposeGoGetImport(owner.Name, repo.Name)
ctx.Data["GoGetImport"] = ComposeGoGetImport(ctx, owner.Name, repo.Name)
fullURLPrefix := repo.HTMLURL() + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
ctx.Data["GoDocDirectory"] = fullURLPrefix + "{/dir}"
ctx.Data["GoDocFile"] = fullURLPrefix + "{/dir}/{file}#L{line}"
Expand All @@ -756,19 +755,11 @@ func RepoAssignment(ctx *Context) context.CancelFunc {
type RepoRefType int

const (
// RepoRefLegacy unknown type, make educated guess and redirect.
// for backward compatibility with previous URL scheme
RepoRefLegacy RepoRefType = iota
// RepoRefAny is for usage where educated guess is needed
// but redirect can not be made
RepoRefAny
// RepoRefBranch branch
// RepoRefUnknown is for legacy support, makes the code to "guess" the ref type
RepoRefUnknown RepoRefType = iota
RepoRefBranch
// RepoRefTag tag
RepoRefTag
// RepoRefCommit commit
RepoRefCommit
// RepoRefBlob blob
RepoRefBlob
)

Expand All @@ -781,22 +772,6 @@ func RepoRef() func(*Context) context.CancelFunc {
return RepoRefByType(RepoRefBranch)
}

// RefTypeIncludesBranches returns true if ref type can be a branch
func (rt RepoRefType) RefTypeIncludesBranches() bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefBranch {
return true
}
return false
}

// RefTypeIncludesTags returns true if ref type can be a tag
func (rt RepoRefType) RefTypeIncludesTags() bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefTag {
return true
}
return false
}

func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool) string {
refName := ""
parts := strings.Split(path, "/")
Expand All @@ -810,28 +785,50 @@ func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool
return ""
}

func isStringLikelyCommitID(objFmt git.ObjectFormat, s string, minLength ...int) bool {
minLen := util.OptionalArg(minLength, objFmt.FullLength())
if len(s) < minLen || len(s) > objFmt.FullLength() {
return false
}
for _, c := range s {
isHex := (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')
if !isHex {
return false
}
}
return true
}

func getRefNameLegacy(ctx *Base, repo *Repository, optionalExtraRef ...string) (string, RepoRefType) {
extraRef := util.OptionalArg(optionalExtraRef)
reqPath := ctx.PathParam("*")
reqPath = path.Join(extraRef, reqPath)

if refName := getRefName(ctx, repo, RepoRefBranch); refName != "" {
return refName, RepoRefBranch
}
if refName := getRefName(ctx, repo, RepoRefTag); refName != "" {
return refName, RepoRefTag
}

// For legacy support only full commit sha
parts := strings.Split(reqPath, "/")
if isStringLikelyCommitID(git.ObjectFormatFromName(repo.Repository.ObjectFormatName), parts[0]) {
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
repo.TreePath = strings.Join(parts[1:], "/")
return parts[0], RepoRefCommit
}

if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
return refName, RepoRefBlob
}
repo.TreePath = reqPath
return repo.Repository.DefaultBranch, RepoRefBranch
}

func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
path := ctx.PathParam("*")
switch pathType {
case RepoRefLegacy, RepoRefAny:
if refName := getRefName(ctx, repo, RepoRefBranch); len(refName) > 0 {
return refName
}
if refName := getRefName(ctx, repo, RepoRefTag); len(refName) > 0 {
return refName
}
// For legacy and API support only full commit sha
parts := strings.Split(path, "/")

if len(parts) > 0 && len(parts[0]) == git.ObjectFormatFromName(repo.Repository.ObjectFormatName).FullLength() {
repo.TreePath = strings.Join(parts[1:], "/")
return parts[0]
}
if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
return refName
}
repo.TreePath = path
return repo.Repository.DefaultBranch
case RepoRefBranch:
ref := getRefNameFromPath(repo, path, repo.GitRepo.IsBranchExist)
if len(ref) == 0 {
Expand Down Expand Up @@ -866,13 +863,13 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
return getRefNameFromPath(repo, path, repo.GitRepo.IsTagExist)
case RepoRefCommit:
parts := strings.Split(path, "/")

if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= repo.GetObjectFormat().FullLength() {
if isStringLikelyCommitID(repo.GetObjectFormat(), parts[0], 7) {
// FIXME: this logic is different from other types. Ideally, it should also try to GetCommit to check if it exists
repo.TreePath = strings.Join(parts[1:], "/")
return parts[0]
}

if len(parts) > 0 && parts[0] == headRefName {
if parts[0] == headRefName {
// HEAD ref points to last default branch commit
commit, err := repo.GitRepo.GetBranchCommit(repo.Repository.DefaultBranch)
if err != nil {
Expand All @@ -888,15 +885,21 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
}
return path
default:
log.Error("Unrecognized path type: %v", path)
panic(fmt.Sprintf("Unrecognized path type: %v", pathType))
}
return ""
}

type RepoRefByTypeOptions struct {
IgnoreNotExistErr bool
}

// RepoRefByType handles repository reference name for a specific type
// of repository reference
func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context) context.CancelFunc {
func RepoRefByType(detectRefType RepoRefType, opts ...RepoRefByTypeOptions) func(*Context) context.CancelFunc {
opt := util.OptionalArg(opts)
return func(ctx *Context) (cancel context.CancelFunc) {
refType := detectRefType
// Empty repository does not have reference information.
if ctx.Repo.Repository.IsEmpty {
// assume the user is viewing the (non-existent) default branch
Expand Down Expand Up @@ -956,7 +959,12 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
}
ctx.Repo.IsViewBranch = true
} else {
refName = getRefName(ctx.Base, ctx.Repo, refType)
guessLegacyPath := refType == RepoRefUnknown
if guessLegacyPath {
refName, refType = getRefNameLegacy(ctx.Base, ctx.Repo)
} else {
refName = getRefName(ctx.Base, ctx.Repo, refType)
}
ctx.Repo.RefName = refName
isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
if isRenamedBranch && has {
Expand All @@ -967,7 +975,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return cancel
}

if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
if refType == RepoRefBranch && ctx.Repo.GitRepo.IsBranchExist(refName) {
ctx.Repo.IsViewBranch = true
ctx.Repo.BranchName = refName

Expand All @@ -977,7 +985,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return cancel
}
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
} else if refType == RepoRefTag && ctx.Repo.GitRepo.IsTagExist(refName) {
ctx.Repo.IsViewTag = true
ctx.Repo.TagName = refName

Expand All @@ -991,7 +999,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return cancel
}
ctx.Repo.CommitID = ctx.Repo.Commit.ID.String()
} else if len(refName) >= 7 && len(refName) <= ctx.Repo.GetObjectFormat().FullLength() {
} else if isStringLikelyCommitID(ctx.Repo.GetObjectFormat(), refName, 7) {
ctx.Repo.IsViewCommit = true
ctx.Repo.CommitID = refName

Expand All @@ -1002,18 +1010,18 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
}
// If short commit ID add canonical link header
if len(refName) < ctx.Repo.GetObjectFormat().FullLength() {
ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"",
util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))))
canonicalURL := util.URLJoin(httplib.GuessCurrentAppURL(ctx), strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1))
ctx.RespHeader().Set("Link", fmt.Sprintf(`<%s>; rel="canonical"`, canonicalURL))
}
} else {
if len(ignoreNotExistErr) > 0 && ignoreNotExistErr[0] {
if opt.IgnoreNotExistErr {
return cancel
}
ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName))
return cancel
}

if refType == RepoRefLegacy {
if guessLegacyPath {
// redirect from old URL scheme to new URL scheme
prefix := strings.TrimPrefix(setting.AppSubURL+strings.ToLower(strings.TrimSuffix(ctx.Req.URL.Path, ctx.PathParam("*"))), strings.ToLower(ctx.Repo.RepoLink))
redirect := path.Join(
Expand Down
Loading
Loading