1
Fork 0

[gitea] week 2025-02 cherry pick (gitea/main -> forgejo) (#6471)

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6471
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2025-01-07 06:17:49 +00:00
commit 4261301dfb
34 changed files with 341 additions and 127 deletions

View file

@ -10,6 +10,7 @@ import (
"regexp"
"sort"
"strings"
"unicode"
packages_model "code.gitea.io/gitea/models/packages"
packages_module "code.gitea.io/gitea/modules/packages"
@ -139,9 +140,30 @@ func UploadPackageFile(ctx *context.Context) {
return
}
projectURL := ctx.Req.FormValue("home_page")
if !validation.IsValidURL(projectURL) {
projectURL = ""
// Ensure ctx.Req.Form exists.
_ = ctx.Req.ParseForm()
var homepageURL string
projectURLs := ctx.Req.Form["project_urls"]
for _, purl := range projectURLs {
label, url, found := strings.Cut(purl, ",")
if !found {
continue
}
if normalizeLabel(label) != "homepage" {
continue
}
homepageURL = strings.TrimSpace(url)
break
}
if len(homepageURL) == 0 {
// TODO: Home-page is a deprecated metadata field. Remove this branch once it's no longer apart of the spec.
homepageURL = ctx.Req.FormValue("home_page")
}
if !validation.IsValidURL(homepageURL) {
homepageURL = ""
}
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
@ -160,7 +182,7 @@ func UploadPackageFile(ctx *context.Context) {
Description: ctx.Req.FormValue("description"),
LongDescription: ctx.Req.FormValue("long_description"),
Summary: ctx.Req.FormValue("summary"),
ProjectURL: projectURL,
ProjectURL: homepageURL,
License: ctx.Req.FormValue("license"),
RequiresPython: ctx.Req.FormValue("requires_python"),
},
@ -189,6 +211,23 @@ func UploadPackageFile(ctx *context.Context) {
ctx.Status(http.StatusCreated)
}
// Normalizes a Project-URL label.
// See https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
func normalizeLabel(label string) string {
var builder strings.Builder
// "A label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result
// to lowercase."
for _, r := range label {
if unicode.IsPunct(r) || unicode.IsSpace(r) {
continue
}
builder.WriteRune(unicode.ToLower(r))
}
return builder.String()
}
func isValidNameAndVersion(packageName, packageVersion string) bool {
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
}

View file

@ -36,3 +36,13 @@ func TestIsValidNameAndVersion(t *testing.T) {
assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa"))
assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta"))
}
func TestNormalizeLabel(t *testing.T) {
// Cases fetched from https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
assert.Equal(t, "homepage", normalizeLabel("Homepage"))
assert.Equal(t, "homepage", normalizeLabel("Home-page"))
assert.Equal(t, "homepage", normalizeLabel("Home page"))
assert.Equal(t, "changelog", normalizeLabel("Change_Log"))
assert.Equal(t, "whatsnew", normalizeLabel("What's New?"))
assert.Equal(t, "github", normalizeLabel("github"))
}

View file

@ -11,7 +11,6 @@ import (
"fmt"
"io"
"net/http"
"path"
"strings"
"time"
@ -247,19 +246,14 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
return nil, nil, nil
}
info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommitsInfo", err)
ctx.Error(http.StatusInternalServerError, "GetTreePathLatestCommit", err)
return nil, nil, nil
}
when := &latestCommit.Committer.When
if len(info) == 1 {
// Not Modified
lastModified = &info[0].Commit.Committer.When
}
blob = entry.Blob()
return blob, entry, lastModified
return entry.Blob(), entry, when
}
// GetArchive get archive of a repository

View file

@ -209,7 +209,7 @@ func ChangeProjectStatus(ctx *context.Context) {
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id))
ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.ContextUser, id))
}
// DeleteProject delete a project
@ -259,7 +259,7 @@ func RenderEditProject(ctx *context.Context) {
ctx.Data["redirect"] = ctx.FormString("redirect")
ctx.Data["HomeLink"] = ctx.ContextUser.HomeLink()
ctx.Data["card_type"] = p.CardType
ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), p.ID)
ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, p.ID)
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@ -273,7 +273,7 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["PageIsViewProjects"] = true
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.Data["CancelLink"] = fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), projectID)
ctx.Data["CancelLink"] = project_model.ProjectLinkForOrg(ctx.ContextUser, projectID)
shared_user.RenderUserHeader(ctx)

View file

@ -5,7 +5,6 @@
package repo
import (
"path"
"time"
git_model "code.gitea.io/gitea/models/git"
@ -82,7 +81,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
}
func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.Time) {
func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil {
if git.IsErrNotExist(err) {
@ -98,19 +97,14 @@ func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.T
return nil, nil
}
info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil {
ctx.ServerError("GetCommitsInfo", err)
ctx.ServerError("GetTreePathLatestCommit", err)
return nil, nil
}
lastModified := &latestCommit.Committer.When
if len(info) == 1 {
// Not Modified
lastModified = &info[0].Commit.Committer.When
}
blob = entry.Blob()
return blob, lastModified
return entry.Blob(), lastModified
}
// SingleDownload download a file by repos path

View file

@ -1291,10 +1291,17 @@ func NewIssuePost(ctx *context.Context) {
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
if ctx.FormString("redirect_after_creation") == "project" && projectID > 0 {
ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(projectID, 10))
} else {
ctx.JSONRedirect(issue.Link())
project, err := project_model.GetProjectByID(ctx, projectID)
if err == nil {
if project.Type == project_model.TypeOrganization {
ctx.JSONRedirect(project_model.ProjectLinkForOrg(ctx.Repo.Owner, project.ID))
} else {
ctx.JSONRedirect(project_model.ProjectLinkForRepo(repo, project.ID))
}
return
}
}
ctx.JSONRedirect(issue.Link())
}
// roleDescriptor returns the role descriptor for a comment in/with the given repo, poster and issue

View file

@ -183,7 +183,7 @@ func ChangeProjectStatus(ctx *context.Context) {
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
return
}
ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id))
ctx.JSONRedirect(project_model.ProjectLinkForRepo(ctx.Repo.Repository, id))
}
// DeleteProject delete a project
@ -237,7 +237,7 @@ func RenderEditProject(ctx *context.Context) {
ctx.Data["content"] = p.Description
ctx.Data["card_type"] = p.CardType
ctx.Data["redirect"] = ctx.FormString("redirect")
ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), p.ID)
ctx.Data["CancelLink"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, p.ID)
ctx.HTML(http.StatusOK, tplProjectsNew)
}
@ -251,7 +251,7 @@ func EditProjectPost(ctx *context.Context) {
ctx.Data["PageIsEditProjects"] = true
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["CardTypes"] = project_model.GetCardConfig()
ctx.Data["CancelLink"] = fmt.Sprintf("%s/projects/%d", ctx.Repo.Repository.Link(), projectID)
ctx.Data["CancelLink"] = project_model.ProjectLinkForRepo(ctx.Repo.Repository, projectID)
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplProjectsNew)