From 3f032759ed40c8815e301cf5cc9f64f04864f731 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 16 Oct 2022 16:56:58 +0000 Subject: [PATCH 01/54] Return 404 when user is not found on avatar (#21476) (#21477) - Backport #21476 - Instead of returning a 500 Internal Server when the user wasn't found, return 404 Not found. --- routers/web/user/avatar.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/routers/web/user/avatar.go b/routers/web/user/avatar.go index 53a603fab094d..05896299d25ef 100644 --- a/routers/web/user/avatar.go +++ b/routers/web/user/avatar.go @@ -31,6 +31,10 @@ func AvatarByUserName(ctx *context.Context) { if strings.ToLower(userName) != "ghost" { var err error if user, err = user_model.GetUserByName(ctx, userName); err != nil { + if user_model.IsErrUserNotExist(err) { + ctx.NotFound("GetUserByName", err) + return + } ctx.ServerError("Invalid user: "+userName, err) return } From 46053c092d34bb9951f91bd1d5ac2dac4ed980a1 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 17 Oct 2022 07:07:19 +0200 Subject: [PATCH 02/54] Enforce grouped NuGet search results (#21442) (#21480) Backport of #21442 --- integrations/api_packages_nuget_test.go | 88 ++++++++++++++++++------- routers/api/packages/nuget/api.go | 19 ++---- 2 files changed, 72 insertions(+), 35 deletions(-) diff --git a/integrations/api_packages_nuget_test.go b/integrations/api_packages_nuget_test.go index 1fb7c4728b9c3..0b616bcd21e88 100644 --- a/integrations/api_packages_nuget_test.go +++ b/integrations/api_packages_nuget_test.go @@ -10,6 +10,7 @@ import ( "encoding/base64" "fmt" "io" + "io/ioutil" "net/http" "testing" @@ -43,23 +44,29 @@ func TestPackageNuGet(t *testing.T) { symbolFilename := "test.pdb" symbolID := "d910bb6948bd4c6cb40155bcf52c3c94" - var buf bytes.Buffer - archive := zip.NewWriter(&buf) - w, _ := archive.Create("package.nuspec") - w.Write([]byte(` - - - ` + packageName + ` - ` + packageVersion + ` - ` + packageAuthors + ` - ` + packageDescription + ` - - - - - `)) - archive.Close() - content := buf.Bytes() + createPackage := func(id, version string) io.Reader { + var buf bytes.Buffer + archive := zip.NewWriter(&buf) + w, _ := archive.Create("package.nuspec") + w.Write([]byte(` + + + ` + id + ` + ` + version + ` + ` + packageAuthors + ` + ` + packageDescription + ` + + + + + + + `)) + archive.Close() + return &buf + } + + content, _ := ioutil.ReadAll(createPackage(packageName, packageVersion)) url := fmt.Sprintf("/api/packages/%s/nuget", user.Name) @@ -159,7 +166,7 @@ func TestPackageNuGet(t *testing.T) { t.Run("SymbolPackage", func(t *testing.T) { defer PrintCurrentTest(t)() - createPackage := func(id, packageType string) io.Reader { + createSymbolPackage := func(id, packageType string) io.Reader { var buf bytes.Buffer archive := zip.NewWriter(&buf) @@ -185,15 +192,15 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) return &buf } - req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage("unknown-package", "SymbolsPackage")) + req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage("unknown-package", "SymbolsPackage")) req = AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusNotFound) - req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "DummyPackage")) + req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "DummyPackage")) req = AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusBadRequest) - req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage")) + req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage")) req = AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusCreated) @@ -237,7 +244,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) } } - req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage")) + req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createSymbolPackage(packageName, "SymbolsPackage")) req = AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusConflict) }) @@ -315,6 +322,43 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) assert.Equal(t, c.ExpectedTotal, result.TotalHits, "case %d: unexpected total hits", i) assert.Len(t, result.Data, c.ExpectedResults, "case %d: unexpected result count", i) } + + t.Run("EnforceGrouped", func(t *testing.T) { + defer PrintCurrentTest(t)() + + req := NewRequestWithBody(t, "PUT", url, createPackage(packageName+".dummy", "1.0.0")) + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusCreated) + + req = NewRequestWithBody(t, "PUT", url, createPackage(packageName, "1.0.99")) + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusCreated) + + req = NewRequest(t, "GET", fmt.Sprintf("%s/query?q=%s", url, packageName)) + req = AddBasicAuthHeader(req, user.Name) + resp := MakeRequest(t, req, http.StatusOK) + + var result nuget.SearchResultResponse + DecodeJSON(t, resp, &result) + + assert.EqualValues(t, 3, result.TotalHits) + assert.Len(t, result.Data, 2) + for _, sr := range result.Data { + if sr.ID == packageName { + assert.Len(t, sr.Versions, 2) + } else { + assert.Len(t, sr.Versions, 1) + } + } + + req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName+".dummy", "1.0.0")) + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusNoContent) + + req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, "1.0.99")) + req = AddBasicAuthHeader(req, user.Name) + MakeRequest(t, req, http.StatusNoContent) + }) }) t.Run("RegistrationService", func(t *testing.T) { diff --git a/routers/api/packages/nuget/api.go b/routers/api/packages/nuget/api.go index b449cfc5bb3ae..c5cb75bb34daa 100644 --- a/routers/api/packages/nuget/api.go +++ b/routers/api/packages/nuget/api.go @@ -224,20 +224,13 @@ type SearchResultVersion struct { } func createSearchResultResponse(l *linkBuilder, totalHits int64, pds []*packages_model.PackageDescriptor) *SearchResultResponse { - data := make([]*SearchResult, 0, len(pds)) + grouped := make(map[string][]*packages_model.PackageDescriptor) + for _, pd := range pds { + grouped[pd.Package.Name] = append(grouped[pd.Package.Name], pd) + } - if len(pds) > 0 { - groupID := pds[0].Package.Name - group := make([]*packages_model.PackageDescriptor, 0, 10) - - for i := 0; i < len(pds); i++ { - if groupID != pds[i].Package.Name { - data = append(data, createSearchResult(l, group)) - groupID = pds[i].Package.Name - group = group[:0] - } - group = append(group, pds[i]) - } + data := make([]*SearchResult, 0, len(pds)) + for _, group := range grouped { data = append(data, createSearchResult(l, group)) } From 5a84558e7cbf87594a4076d5a785c5bdc0445f95 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 17 Oct 2022 16:37:44 +0200 Subject: [PATCH 03/54] Display total commit count in hook message (#21400) (#21481) Backport of #21400 Co-authored-by: wxiaoguang Co-authored-by: Lunny Xiao --- modules/notification/webhook/webhook.go | 38 +++++++++++++------------ modules/structs/hook.go | 19 +++++++------ routers/api/v1/repo/hook.go | 19 +++++++------ routers/web/repo/webhook.go | 19 +++++++------ services/webhook/dingtalk.go | 8 +++--- services/webhook/dingtalk_test.go | 2 +- services/webhook/discord.go | 4 +-- services/webhook/general_test.go | 13 +++++---- services/webhook/matrix.go | 4 +-- services/webhook/msteams.go | 6 ++-- services/webhook/slack.go | 4 +-- services/webhook/telegram.go | 4 +-- services/webhook/wechatwork.go | 2 +- 13 files changed, 74 insertions(+), 68 deletions(-) diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go index be71d18fdad3d..e61929b4cf277 100644 --- a/modules/notification/webhook/webhook.go +++ b/modules/notification/webhook/webhook.go @@ -608,15 +608,16 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_ } if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{ - Ref: opts.RefFullName, - Before: opts.OldCommitID, - After: opts.NewCommitID, - CompareURL: setting.AppURL + commits.CompareURL, - Commits: apiCommits, - HeadCommit: apiHeadCommit, - Repo: convert.ToRepo(repo, perm.AccessModeOwner), - Pusher: apiPusher, - Sender: apiPusher, + Ref: opts.RefFullName, + Before: opts.OldCommitID, + After: opts.NewCommitID, + CompareURL: setting.AppURL + commits.CompareURL, + Commits: apiCommits, + TotalCommits: commits.Len, + HeadCommit: apiHeadCommit, + Repo: convert.ToRepo(repo, perm.AccessModeOwner), + Pusher: apiPusher, + Sender: apiPusher, }); err != nil { log.Error("PrepareWebhooks: %v", err) } @@ -838,15 +839,16 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *r } if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{ - Ref: opts.RefFullName, - Before: opts.OldCommitID, - After: opts.NewCommitID, - CompareURL: setting.AppURL + commits.CompareURL, - Commits: apiCommits, - HeadCommit: apiHeadCommit, - Repo: convert.ToRepo(repo, perm.AccessModeOwner), - Pusher: apiPusher, - Sender: apiPusher, + Ref: opts.RefFullName, + Before: opts.OldCommitID, + After: opts.NewCommitID, + CompareURL: setting.AppURL + commits.CompareURL, + Commits: apiCommits, + TotalCommits: commits.Len, + HeadCommit: apiHeadCommit, + Repo: convert.ToRepo(repo, perm.AccessModeOwner), + Pusher: apiPusher, + Sender: apiPusher, }); err != nil { log.Error("PrepareWebhooks: %v", err) } diff --git a/modules/structs/hook.go b/modules/structs/hook.go index 07d51915dea9f..4d186c2ca2ca0 100644 --- a/modules/structs/hook.go +++ b/modules/structs/hook.go @@ -267,15 +267,16 @@ func (p *ReleasePayload) JSONPayload() ([]byte, error) { // PushPayload represents a payload information of push event. type PushPayload struct { - Ref string `json:"ref"` - Before string `json:"before"` - After string `json:"after"` - CompareURL string `json:"compare_url"` - Commits []*PayloadCommit `json:"commits"` - HeadCommit *PayloadCommit `json:"head_commit"` - Repo *Repository `json:"repository"` - Pusher *User `json:"pusher"` - Sender *User `json:"sender"` + Ref string `json:"ref"` + Before string `json:"before"` + After string `json:"after"` + CompareURL string `json:"compare_url"` + Commits []*PayloadCommit `json:"commits"` + TotalCommits int `json:"total_commits"` + HeadCommit *PayloadCommit `json:"head_commit"` + Repo *Repository `json:"repository"` + Pusher *User `json:"pusher"` + Sender *User `json:"sender"` } // JSONPayload FIXME diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go index b17142c0f647f..86361817cb464 100644 --- a/routers/api/v1/repo/hook.go +++ b/routers/api/v1/repo/hook.go @@ -169,15 +169,16 @@ func TestHook(ctx *context.APIContext) { commitID := ctx.Repo.Commit.ID.String() if err := webhook_service.PrepareWebhook(hook, ctx.Repo.Repository, webhook.HookEventPush, &api.PushPayload{ - Ref: ref, - Before: commitID, - After: commitID, - CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID), - Commits: []*api.PayloadCommit{commit}, - HeadCommit: commit, - Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone), - Pusher: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone), - Sender: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone), + Ref: ref, + Before: commitID, + After: commitID, + CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID), + Commits: []*api.PayloadCommit{commit}, + TotalCommits: 1, + HeadCommit: commit, + Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone), + Pusher: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone), + Sender: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone), }); err != nil { ctx.Error(http.StatusInternalServerError, "PrepareWebhook: ", err) return diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go index 2b9f78ab018e0..78e2b65a96de9 100644 --- a/routers/web/repo/webhook.go +++ b/routers/web/repo/webhook.go @@ -1273,15 +1273,16 @@ func TestWebhook(ctx *context.Context) { commitID := commit.ID.String() p := &api.PushPayload{ - Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch, - Before: commitID, - After: commitID, - CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID), - Commits: []*api.PayloadCommit{apiCommit}, - HeadCommit: apiCommit, - Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone), - Pusher: apiUser, - Sender: apiUser, + Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch, + Before: commitID, + After: commitID, + CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID), + Commits: []*api.PayloadCommit{apiCommit}, + TotalCommits: 1, + HeadCommit: apiCommit, + Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone), + Pusher: apiUser, + Sender: apiUser, } if err := webhook_service.PrepareWebhook(w, ctx.Repo.Repository, webhook.HookEventPush, p); err != nil { ctx.Flash.Error("PrepareWebhook: " + err.Error()) diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go index 642cf6f2fda16..0fc61ce8c1394 100644 --- a/services/webhook/dingtalk.go +++ b/services/webhook/dingtalk.go @@ -67,14 +67,14 @@ func (d *DingtalkPayload) Push(p *api.PushPayload) (api.Payloader, error) { ) var titleLink, linkText string - if len(p.Commits) == 1 { + if p.TotalCommits == 1 { commitDesc = "1 new commit" titleLink = p.Commits[0].URL - linkText = fmt.Sprintf("view commit %s", p.Commits[0].ID[:7]) + linkText = "view commit" } else { - commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) + commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits) titleLink = p.CompareURL - linkText = fmt.Sprintf("view commit %s...%s", p.Commits[0].ID[:7], p.Commits[len(p.Commits)-1].ID[:7]) + linkText = "view commits" } if titleLink == "" { titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName) diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go index b66b5e43a824d..e411043364ede 100644 --- a/services/webhook/dingtalk_test.go +++ b/services/webhook/dingtalk_test.go @@ -82,7 +82,7 @@ func TestDingTalkPayload(t *testing.T) { assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*DingtalkPayload).ActionCard.Text) assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*DingtalkPayload).ActionCard.Title) - assert.Equal(t, "view commit 2020558...2020558", pl.(*DingtalkPayload).ActionCard.SingleTitle) + assert.Equal(t, "view commits", pl.(*DingtalkPayload).ActionCard.SingleTitle) assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL)) }) diff --git a/services/webhook/discord.go b/services/webhook/discord.go index ae5460b9a7a1b..f65a8648c435a 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -141,11 +141,11 @@ func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) { ) var titleLink string - if len(p.Commits) == 1 { + if p.TotalCommits == 1 { commitDesc = "1 new commit" titleLink = p.Commits[0].URL } else { - commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) + commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits) titleLink = p.CompareURL } if titleLink == "" { diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go index 4d73afe060a68..a2606e4aca734 100644 --- a/services/webhook/general_test.go +++ b/services/webhook/general_test.go @@ -82,12 +82,13 @@ func pushTestPayload() *api.PushPayload { } return &api.PushPayload{ - Ref: "refs/heads/test", - Before: "2020558fe2e34debb818a514715839cabd25e777", - After: "2020558fe2e34debb818a514715839cabd25e778", - CompareURL: "", - HeadCommit: commit, - Commits: []*api.PayloadCommit{commit, commit}, + Ref: "refs/heads/test", + Before: "2020558fe2e34debb818a514715839cabd25e777", + After: "2020558fe2e34debb818a514715839cabd25e778", + CompareURL: "", + HeadCommit: commit, + Commits: []*api.PayloadCommit{commit, commit}, + TotalCommits: 2, Repo: &api.Repository{ HTMLURL: "http://localhost:3000/test/repo", Name: "repo", diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index a42ab2a93e063..37608f22bef2d 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -154,10 +154,10 @@ func (m *MatrixPayloadUnsafe) Release(p *api.ReleasePayload) (api.Payloader, err func (m *MatrixPayloadUnsafe) Push(p *api.PushPayload) (api.Payloader, error) { var commitDesc string - if len(p.Commits) == 1 { + if p.TotalCommits == 1 { commitDesc = "1 commit" } else { - commitDesc = fmt.Sprintf("%d commits", len(p.Commits)) + commitDesc = fmt.Sprintf("%d commits", p.TotalCommits) } repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName) diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go index 59e2e93493982..856e31b4195bb 100644 --- a/services/webhook/msteams.go +++ b/services/webhook/msteams.go @@ -124,11 +124,11 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) { ) var titleLink string - if len(p.Commits) == 1 { + if p.TotalCommits == 1 { commitDesc = "1 new commit" titleLink = p.Commits[0].URL } else { - commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) + commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits) titleLink = p.CompareURL } if titleLink == "" { @@ -155,7 +155,7 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) { text, titleLink, greenColor, - &MSTeamsFact{"Commit count:", fmt.Sprintf("%d", len(p.Commits))}, + &MSTeamsFact{"Commit count:", fmt.Sprintf("%d", p.TotalCommits)}, ), nil } diff --git a/services/webhook/slack.go b/services/webhook/slack.go index 11e1d3c081c28..0975bba1edcc8 100644 --- a/services/webhook/slack.go +++ b/services/webhook/slack.go @@ -171,10 +171,10 @@ func (s *SlackPayload) Push(p *api.PushPayload) (api.Payloader, error) { commitString string ) - if len(p.Commits) == 1 { + if p.TotalCommits == 1 { commitDesc = "1 new commit" } else { - commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) + commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits) } if len(p.CompareURL) > 0 { commitString = SlackLinkFormatter(p.CompareURL, commitDesc) diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go index 64211493ec62c..97b8a4636e56e 100644 --- a/services/webhook/telegram.go +++ b/services/webhook/telegram.go @@ -89,11 +89,11 @@ func (t *TelegramPayload) Push(p *api.PushPayload) (api.Payloader, error) { ) var titleLink string - if len(p.Commits) == 1 { + if p.TotalCommits == 1 { commitDesc = "1 new commit" titleLink = p.Commits[0].URL } else { - commitDesc = fmt.Sprintf("%d new commits", len(p.Commits)) + commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits) titleLink = p.CompareURL } if titleLink == "" { diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go index de8b777066576..29d866695a4fb 100644 --- a/services/webhook/wechatwork.go +++ b/services/webhook/wechatwork.go @@ -93,7 +93,7 @@ func (f *WechatworkPayload) Push(p *api.PushPayload) (api.Payloader, error) { for i, commit := range p.Commits { var authorName string if commit.Author != nil { - authorName = "Author:" + commit.Author.Name + authorName = "Author: " + commit.Author.Name } message := strings.ReplaceAll(commit.Message, "\n\n", "\r\n") From 19df07f0219e63ee0328397667a85121020b07ee Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Tue, 18 Oct 2022 15:46:13 +0800 Subject: [PATCH 04/54] Fix incorrect notification commit url (#21479) (#21483) Backport #21479 For normal commits the notification url was wrong because oldCommitID is received from the shrinked commits list. This PR moves the commits list shrinking after the oldCommitID assignment. --- services/repository/push.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/repository/push.go b/services/repository/push.go index 65ac7b660c5f0..561f6acdd8332 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -220,10 +220,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { log.Error("updateIssuesCommit: %v", err) } - if len(commits.Commits) > setting.UI.FeedMaxCommitNum { - commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum] - } - oldCommitID := opts.OldCommitID if oldCommitID == git.EmptySHA && len(commits.Commits) > 0 { oldCommit, err := gitRepo.GetCommit(commits.Commits[len(commits.Commits)-1].Sha1) @@ -251,6 +247,10 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { commits.CompareURL = "" } + if len(commits.Commits) > setting.UI.FeedMaxCommitNum { + commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum] + } + notification.NotifyPushCommits(pusher, repo, opts, commits) if err = git_model.RemoveDeletedBranchByName(repo.ID, branch); err != nil { From 4b4adb1cc924ba086a89b309869690903b776d6c Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 19 Oct 2022 22:12:37 +0200 Subject: [PATCH 05/54] Enable Monaco automaticLayout (#21516) Enable [`automaticLayout`](https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IDiffEditorOptions.html#automaticLayout) for monaco so it can reflow itself. Fixes: https://github.com/go-gitea/gitea/issues/21508 --- web_src/js/features/codeeditor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web_src/js/features/codeeditor.js b/web_src/js/features/codeeditor.js index a22043c9d4825..e848fb53c75fb 100644 --- a/web_src/js/features/codeeditor.js +++ b/web_src/js/features/codeeditor.js @@ -17,6 +17,7 @@ const baseOptions = { rulers: false, scrollbar: {horizontalScrollbarSize: 6, verticalScrollbarSize: 6}, scrollBeyondLastLine: false, + automaticLayout: true, }; function getEditorconfig(input) { From 675c14aba660fcda7912519035a785e10ea61431 Mon Sep 17 00:00:00 2001 From: delvh Date: Thu, 20 Oct 2022 17:25:54 +0200 Subject: [PATCH 06/54] Ignore error when retrieving changed PR review files (#21487) (#21524) When a PR reviewer reviewed a file on a commit that was later gc'ed, they would always get a `500` response from then on when loading the PR. This PR simply ignores that error and instead marks all files as unchanged. This approach was chosen as the only feasible option without diving into **a lot** of error handling. Fixes #21392 Backport of #21487 Co-authored-by: Lunny Xiao Co-authored-by: wxiaoguang --- services/gitdiff/gitdiff.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index ec4cbd52d56a9..7439f03f107f0 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1505,8 +1505,13 @@ func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *issues_ } changedFiles, err := gitRepo.GetFilesChangedBetween(review.CommitSHA, latestCommit) + // There are way too many possible errors. + // Examples are various git errors such as the commit the review was based on was gc'ed and hence doesn't exist anymore as well as unrecoverable errors where we should serve a 500 response + // Due to the current architecture and physical limitation of needing to compare explicit error messages, we can only choose one approach without the code getting ugly + // For SOME of the errors such as the gc'ed commit, it would be best to mark all files as changed + // But as that does not work for all potential errors, we simply mark all files as unchanged and drop the error which always works, even if not as good as possible if err != nil { - return diff, err + log.Error("Could not get changed files between %s and %s for pull request %d in repo with path %s. Assuming no changes. Error: %w", review.CommitSHA, latestCommit, pull.Index, gitRepo.Path, err) } filesChangedSinceLastDiff := make(map[string]pull_model.ViewedState) From 556e2d5506a4831f14e2b1c4508d9ec778fba628 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 21 Oct 2022 20:59:27 +0800 Subject: [PATCH 07/54] Fix generating compare link (#21519) (#21530) Fix #6318, backport #21519 Co-authored-by: zeripath Co-authored-by: zeripath --- modules/templates/helper.go | 13 +++++++++++++ templates/repo/home.tmpl | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 2ea7bdacc6955..61f1f6adad0ca 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -458,6 +458,19 @@ func NewFuncMap() []template.FuncMap { return items }, "HasPrefix": strings.HasPrefix, + "CompareLink": func(baseRepo, repo *repo_model.Repository, branchName string) string { + var curBranch string + if repo.ID != baseRepo.ID { + curBranch += fmt.Sprintf("%s/%s:", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name)) + } + curBranch += util.PathEscapeSegments(branchName) + + return fmt.Sprintf("%s/compare/%s...%s", + baseRepo.Link(), + util.PathEscapeSegments(baseRepo.DefaultBranch), + curBranch, + ) + }, }} } diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 18c02aa9ecac5..a24aa52247e5c 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -68,7 +68,7 @@ {{if eq $n 0}} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} - + {{end}} From 8043fbce09e32d9e56a26fe2d9e9be7d8c280aca Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 22 Oct 2022 11:22:11 +0200 Subject: [PATCH 08/54] Check for valid user token in integration tests (#21520) (#21529) Backport #21520 Added checks for logged user token. Some builds fail at unrelated tests, due to missing token. Co-authored-by: Vladimir Yakovlev Co-authored-by: wxiaoguang --- integrations/integration_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integrations/integration_test.go b/integrations/integration_test.go index b0004927f7b57..3117587f4285e 100644 --- a/integrations/integration_test.go +++ b/integrations/integration_test.go @@ -21,6 +21,7 @@ import ( "path/filepath" "runtime" "strings" + "sync/atomic" "testing" "time" @@ -430,19 +431,19 @@ var tokenCounter int64 func getTokenForLoggedInUser(t testing.TB, session *TestSession) string { t.Helper() - tokenCounter++ req := NewRequest(t, "GET", "/user/settings/applications") resp := session.MakeRequest(t, req, http.StatusOK) doc := NewHTMLParser(t, resp.Body) req = NewRequestWithValues(t, "POST", "/user/settings/applications", map[string]string{ "_csrf": doc.GetCSRF(), - "name": fmt.Sprintf("api-testing-token-%d", tokenCounter), + "name": fmt.Sprintf("api-testing-token-%d", atomic.AddInt64(&tokenCounter, 1)), }) resp = session.MakeRequest(t, req, http.StatusSeeOther) req = NewRequest(t, "GET", "/user/settings/applications") resp = session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) token := htmlDoc.doc.Find(".ui.info p").Text() + assert.NotEmpty(t, token) return token } From 92b5f48c400ba7913def5bf724ecc66ad614e4ac Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 24 Oct 2022 09:17:13 +0800 Subject: [PATCH 09/54] Update binding to fix bugs (#21560) backport #21556, Fix #19698 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b3b46fbaa983b..9a000fec80ae5 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b code.gitea.io/sdk/gitea v0.15.1 - gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb + gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 diff --git a/go.sum b/go.sum index b5fcf15d7133c..3a6e5588bc362 100644 --- a/go.sum +++ b/go.sum @@ -69,8 +69,8 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EU contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE= contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb h1:Yy0Bxzc8R2wxiwXoG/rECGplJUSpXqCsog9PuJFgiHs= -gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc= +gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681 h1:MMSPgnVULVwV9kEBgvyEUhC9v/uviZ55hPJEMjpbNR4= +gitea.com/go-chi/binding v0.0.0-20221013104517-b29891619681/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc= gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0= gitea.com/go-chi/cache v0.2.0 h1:E0npuTfDW6CT1yD8NMDVc1SK6IeRjfmRL2zlEsCEd7w= gitea.com/go-chi/cache v0.2.0/go.mod h1:iQlVK2aKTZ/rE9UcHyz9pQWGvdP9i1eI2spOpzgCrtE= From 6b7ce726c21f9bc666e55a6eeccd9244010419c2 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 24 Oct 2022 05:18:31 +0200 Subject: [PATCH 10/54] Prevent Authorization header for presigned LFS urls (#21531) (#21569) Backport of #21531 Co-authored-by: Lunny Xiao --- services/lfs/server.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/services/lfs/server.go b/services/lfs/server.go index b868db39dbece..830112fac6e35 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -438,14 +438,21 @@ func buildObjectResponse(rc *requestContext, pointer lfs_module.Pointer, downloa } if download { - rep.Actions["download"] = &lfs_module.Link{Href: rc.DownloadLink(pointer), Header: header} + var link *lfs_module.Link if setting.LFS.ServeDirect { // If we have a signed url (S3, object storage), redirect to this directly. u, err := storage.LFS.URL(pointer.RelativePath(), pointer.Oid) if u != nil && err == nil { - rep.Actions["download"] = &lfs_module.Link{Href: u.String(), Header: header} + // Presigned url does not need the Authorization header + // https://github.com/go-gitea/gitea/issues/21525 + delete(header, "Authorization") + link = &lfs_module.Link{Href: u.String(), Header: header} } } + if link == nil { + link = &lfs_module.Link{Href: rc.DownloadLink(pointer), Header: header} + } + rep.Actions["download"] = link } if upload { rep.Actions["upload"] = &lfs_module.Link{Href: rc.UploadLink(pointer), Header: header} From 0571ddc368efda40479a3b817fba5e3cb704e47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Wawrzy=C5=84czyk?= <107928848+fitithw@users.noreply.github.com> Date: Mon, 24 Oct 2022 13:57:19 +0200 Subject: [PATCH 11/54] Case-insensitive NuGet symbol file GUID (#21409) (#21575) Backport of #21409 Co-authored-by: techknowlogick Co-authored-by: wxiaoguang --- integrations/api_packages_nuget_test.go | 2 +- routers/api/packages/api.go | 2 +- routers/api/packages/nuget/nuget.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integrations/api_packages_nuget_test.go b/integrations/api_packages_nuget_test.go index 0b616bcd21e88..38d678596d2c5 100644 --- a/integrations/api_packages_nuget_test.go +++ b/integrations/api_packages_nuget_test.go @@ -286,7 +286,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) req = AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusNotFound) - req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/%s", url, symbolFilename, symbolID, symbolFilename)) + req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFffff/%s", url, symbolFilename, symbolID, symbolFilename)) req = AddBasicAuthHeader(req, user.Name) MakeRequest(t, req, http.StatusOK) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index dd5cf4a4c0ed8..96924698a3c10 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -190,7 +190,7 @@ func Routes() *web.Route { r.Put("/symbolpackage", nuget.UploadSymbolPackage) r.Delete("/{id}/{version}", nuget.DeletePackage) }, reqPackageAccess(perm.AccessModeWrite)) - r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile) + r.Get("/symbols/{filename}/{guid:[0-9a-fA-F]{32}[fF]{8}}/{filename2}", nuget.DownloadSymbolFile) }, reqPackageAccess(perm.AccessModeRead)) }) r.Group("/npm", func() { diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go index 6bdd0ed5e01ed..d420bfbf050d3 100644 --- a/routers/api/packages/nuget/nuget.go +++ b/routers/api/packages/nuget/nuget.go @@ -351,7 +351,7 @@ func processUploadedFile(ctx *context.Context, expectedType nuget_module.Package // DownloadSymbolFile https://github.com/dotnet/symstore/blob/main/docs/specs/Simple_Symbol_Query_Protocol.md#request func DownloadSymbolFile(ctx *context.Context) { filename := ctx.Params("filename") - guid := ctx.Params("guid") + guid := ctx.Params("guid")[:32] filename2 := ctx.Params("filename2") if filename != filename2 { From d5856fece7d9cc70d5888bbee9fd0eeddf91c8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bogus=C5=82awski?= Date: Mon, 24 Oct 2022 21:05:35 +0200 Subject: [PATCH 12/54] SessionUser protection against nil pointer dereference (#21581) Backport #21358 `SessionUser` should be protected against passing `sess` = `nil` to avoid ``` PANIC: runtime error: invalid memory address or nil pointer dereference ``` in https://github.com/go-gitea/gitea/pull/18452/files#diff-a215b82aadeb8b4c4632fcf31215dd421f804eb1c0137ec6721b980136e4442aR69 after upgrade from gitea v1.16 to v1.17. Related: https://github.com/go-gitea/gitea/pull/18452 --- services/auth/session.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/services/auth/session.go b/services/auth/session.go index 6a23a176651ff..1ec94aa0af718 100644 --- a/services/auth/session.go +++ b/services/auth/session.go @@ -39,6 +39,10 @@ func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataSto // SessionUser returns the user object corresponding to the "uid" session variable. func SessionUser(sess SessionStore) *user_model.User { + if sess == nil { + return nil + } + // Get user ID uid := sess.Get("uid") if uid == nil { From 169c08e20a8fd96b973da03793608e9a580668fb Mon Sep 17 00:00:00 2001 From: eleith Date: Mon, 24 Oct 2022 23:13:27 -0700 Subject: [PATCH 13/54] support binary deploy in npm packages (#21589) backport of #21372 for v1.17.4 ------------------- npm package.json supports binary packaging: https://docs.npmjs.com/cli/v8/configuring-npm/package-json#bin the npm registry documents that the binary references will be attached to the abbreviated version object: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object unfortunately their api documentation leaves this out: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-objectdoc which is likely to be the reason this was left out in gitea's initial implementation this response is critical for npm to install the binary in the .bin folder so as to be included on the users default bin path, resulting in immediate access to any binaries provided by the package i have tested upload and installing through npm and can confirm the npm registry now responds with bin in the version metadata and results in the binary being available after install. this fixes https://github.com/go-gitea/gitea/issues/21303 Co-authored-by: eleith Co-authored-by: Lunny Xiao --- integrations/api_packages_npm_test.go | 6 ++++++ modules/packages/npm/creator.go | 5 ++++- modules/packages/npm/creator_test.go | 5 +++++ modules/packages/npm/metadata.go | 1 + routers/api/packages/npm/api.go | 1 + 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/integrations/api_packages_npm_test.go b/integrations/api_packages_npm_test.go index ad88ac5da6764..bd65a2586714c 100644 --- a/integrations/api_packages_npm_test.go +++ b/integrations/api_packages_npm_test.go @@ -34,6 +34,8 @@ func TestPackageNpm(t *testing.T) { packageTag2 := "release" packageAuthor := "KN4CK3R" packageDescription := "Test Description" + packageBinName := "cli" + packageBinPath := "./cli.sh" data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA" upload := `{ @@ -51,6 +53,9 @@ func TestPackageNpm(t *testing.T) { "author": { "name": "` + packageAuthor + `" }, + "bin": { + "` + packageBinName + `": "` + packageBinPath + `" + }, "dist": { "integrity": "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==", "shasum": "aaa7eaf852a948b0aa05afeda35b1badca155d90" @@ -150,6 +155,7 @@ func TestPackageNpm(t *testing.T) { assert.Equal(t, packageName, pmv.Name) assert.Equal(t, packageDescription, pmv.Description) assert.Equal(t, packageAuthor, pmv.Author.Name) + assert.Equal(t, packageBinPath, pmv.Bin[packageBinName]) assert.Equal(t, "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==", pmv.Dist.Integrity) assert.Equal(t, "aaa7eaf852a948b0aa05afeda35b1badca155d90", pmv.Dist.Shasum) assert.Equal(t, fmt.Sprintf("%s%s/-/%s/%s", setting.AppURL, root[1:], packageVersion, filename), pmv.Dist.Tarball) diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 88ce55ecdbef8..40ac806527531 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -66,7 +66,8 @@ type PackageMetadata struct { License string `json:"license,omitempty"` } -// PackageMetadataVersion https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version +// PackageMetadataVersion documentation: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version +// PackageMetadataVersion response: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object type PackageMetadataVersion struct { ID string `json:"_id"` Name string `json:"name"` @@ -80,6 +81,7 @@ type PackageMetadataVersion struct { Dependencies map[string]string `json:"dependencies,omitempty"` DevDependencies map[string]string `json:"devDependencies,omitempty"` PeerDependencies map[string]string `json:"peerDependencies,omitempty"` + Bin map[string]string `json:"bin,omitempty"` OptionalDependencies map[string]string `json:"optionalDependencies,omitempty"` Readme string `json:"readme,omitempty"` Dist PackageDistribution `json:"dist"` @@ -192,6 +194,7 @@ func ParsePackage(r io.Reader) (*Package, error) { DevelopmentDependencies: meta.DevDependencies, PeerDependencies: meta.PeerDependencies, OptionalDependencies: meta.OptionalDependencies, + Bin: meta.Bin, Readme: meta.Readme, }, } diff --git a/modules/packages/npm/creator_test.go b/modules/packages/npm/creator_test.go index 64ae6238f3baf..2b844f4b0e5f1 100644 --- a/modules/packages/npm/creator_test.go +++ b/modules/packages/npm/creator_test.go @@ -23,6 +23,7 @@ func TestParsePackage(t *testing.T) { packageVersion := "1.0.1-pre" packageTag := "latest" packageAuthor := "KN4CK3R" + packageBin := "gitea" packageDescription := "Test Description" data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA" integrity := "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg==" @@ -236,6 +237,9 @@ func TestParsePackage(t *testing.T) { Dependencies: map[string]string{ "package": "1.2.0", }, + Bin: map[string]string{ + "bin": packageBin, + }, Dist: PackageDistribution{ Integrity: integrity, }, @@ -264,6 +268,7 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, packageDescription, p.Metadata.Description) assert.Equal(t, packageDescription, p.Metadata.Readme) assert.Equal(t, packageAuthor, p.Metadata.Author) + assert.Equal(t, packageBin, p.Metadata.Bin["bin"]) assert.Equal(t, "MIT", p.Metadata.License) assert.Equal(t, "https://gitea.io/", p.Metadata.ProjectURL) assert.Contains(t, p.Metadata.Dependencies, "package") diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go index 643a4d344b7de..44714cd6ea5d6 100644 --- a/modules/packages/npm/metadata.go +++ b/modules/packages/npm/metadata.go @@ -20,5 +20,6 @@ type Metadata struct { DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"` PeerDependencies map[string]string `json:"peer_dependencies,omitempty"` OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"` + Bin map[string]string `json:"bin,omitempty"` Readme string `json:"readme,omitempty"` } diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go index 763c595152ec1..d6fa5417b12e7 100644 --- a/routers/api/packages/npm/api.go +++ b/routers/api/packages/npm/api.go @@ -67,6 +67,7 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package PeerDependencies: metadata.PeerDependencies, OptionalDependencies: metadata.OptionalDependencies, Readme: metadata.Readme, + Bin: metadata.Bin, Dist: npm_module.PackageDistribution{ Shasum: pd.Files[0].Blob.HashSHA1, Integrity: "sha512-" + base64.StdEncoding.EncodeToString(hashBytes), From 2ccf9404647b9bea98892f03901de776b423254f Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Tue, 25 Oct 2022 18:08:05 +0200 Subject: [PATCH 14/54] Suppress `ExternalLoginUserNotExist` error (#21504) (#21572) Backport of #21504 Co-authored-by: Lunny Xiao --- routers/web/auth/auth.go | 4 +++- routers/web/auth/oauth.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index ea5c7232a8aec..bda6789d8d674 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -613,7 +613,9 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. // update external user information if gothUser != nil { if err := externalaccount.UpdateExternalUser(u, *gothUser); err != nil { - log.Error("UpdateExternalUser failed: %v", err) + if !user_model.IsErrExternalLoginUserNotExist(err) { + log.Error("UpdateExternalUser failed: %v", err) + } } } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index a9fc39d0195c1..0492d30656201 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -1061,7 +1061,9 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model // update external user information if err := externalaccount.UpdateExternalUser(u, gothUser); err != nil { - log.Error("UpdateExternalUser failed: %v", err) + if !user_model.IsErrExternalLoginUserNotExist(err) { + log.Error("UpdateExternalUser failed: %v", err) + } } if err := resetLocale(ctx, u); err != nil { From e5044107082c32f538ffbdea524f836a9249cf51 Mon Sep 17 00:00:00 2001 From: Ashley Nelson Date: Wed, 26 Oct 2022 02:44:05 -0500 Subject: [PATCH 15/54] Update milestone counters when issue is deleted (#21459) (#21586) Backports #21459 When actions besides "delete" are performed on issues, the milestone counter is updated. However, since deleting issues goes through a different code path, the associated milestone's count wasn't being updated, resulting in inaccurate counts until another issue in the same milestone had a non-delete action performed on it. I verified this change fixes the inaccurate counts using a local docker build. Co-authored-by: 6543 <6543@obermui.de> --- services/issue/issue.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/issue/issue.go b/services/issue/issue.go index 467bc14b84594..7c94582b82ae4 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -224,6 +224,11 @@ func deleteIssue(issue *issues_model.Issue) error { return err } + if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil { + return fmt.Errorf("error updating counters for milestone id %d: %w", + issue.MilestoneID, err) + } + if err := models.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil { return err } From 291787a5efaa2352241a25bcd75ff69eac23c4f0 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 26 Oct 2022 20:42:45 +0800 Subject: [PATCH 16/54] Fix issues count bug (#21600) backport #21557 --- models/issues/comment.go | 2 +- models/issues/issue.go | 12 ++------- models/repo.go | 13 +++------- models/repo/repo.go | 55 +++++++++++++++++----------------------- 4 files changed, 30 insertions(+), 52 deletions(-) diff --git a/models/issues/comment.go b/models/issues/comment.go index a4e69e7118f34..258ef0cb5ccaf 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -881,7 +881,7 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment } } case CommentTypeReopen, CommentTypeClose: - if err = updateIssueClosedNum(ctx, opts.Issue); err != nil { + if err = repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.Issue.IsPull, true); err != nil { return err } } diff --git a/models/issues/issue.go b/models/issues/issue.go index 76a0ea7d0cb55..814f3d934c838 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -722,7 +722,8 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use } } - if err := updateIssueClosedNum(ctx, issue); err != nil { + // update repository's issue closed number + if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil { return nil, err } @@ -2104,15 +2105,6 @@ func (issue *Issue) BlockingDependencies(ctx context.Context) (issueDeps []*Depe return issueDeps, err } -func updateIssueClosedNum(ctx context.Context, issue *Issue) (err error) { - if issue.IsPull { - err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") - } else { - err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues") - } - return -} - // FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database. func FindAndUpdateIssueMentions(ctx context.Context, issue *Issue, doer *user_model.User, content string) (mentions []*user_model.User, err error) { rawMentions := references.FindAllMentionsMarkdown(content) diff --git a/models/repo.go b/models/repo.go index e304abf960b71..bc2601c9a37ce 100644 --- a/models/repo.go +++ b/models/repo.go @@ -562,24 +562,19 @@ func repoStatsCorrectIssueNumComments(ctx context.Context, id int64) error { } func repoStatsCorrectNumIssues(ctx context.Context, id int64) error { - return repoStatsCorrectNum(ctx, id, false, "num_issues") + return repo_model.UpdateRepoIssueNumbers(ctx, id, false, false) } func repoStatsCorrectNumPulls(ctx context.Context, id int64) error { - return repoStatsCorrectNum(ctx, id, true, "num_pulls") -} - -func repoStatsCorrectNum(ctx context.Context, id int64, isPull bool, field string) error { - _, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_pull=?) WHERE id=?", id, isPull, id) - return err + return repo_model.UpdateRepoIssueNumbers(ctx, id, true, false) } func repoStatsCorrectNumClosedIssues(ctx context.Context, id int64) error { - return repo_model.StatsCorrectNumClosed(ctx, id, false, "num_closed_issues") + return repo_model.UpdateRepoIssueNumbers(ctx, id, false, true) } func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error { - return repo_model.StatsCorrectNumClosed(ctx, id, true, "num_closed_pulls") + return repo_model.UpdateRepoIssueNumbers(ctx, id, true, true) } func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) { diff --git a/models/repo/repo.go b/models/repo/repo.go index f6097d2d6a428..e22e146b56ebc 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -23,6 +23,8 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) // ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo. @@ -319,13 +321,7 @@ func (repo *Repository) LoadUnits(ctx context.Context) (err error) { // UnitEnabled if this repository has the given unit enabled func (repo *Repository) UnitEnabled(tp unit.Type) (result bool) { - if err := db.WithContext(func(ctx *db.Context) error { - result = repo.UnitEnabledCtx(ctx, tp) - return nil - }); err != nil { - log.Error("repo.UnitEnabled: %v", err) - } - return + return repo.UnitEnabledCtx(db.DefaultContext, tp) } // UnitEnabled if this repository has the given unit enabled @@ -760,33 +756,28 @@ func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64, return count, nil } -// StatsCorrectNumClosed update repository's issue related numbers -func StatsCorrectNumClosed(ctx context.Context, id int64, isPull bool, field string) error { - _, err := db.Exec(ctx, "UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, isPull, id) - return err -} - -// UpdateRepoIssueNumbers update repository issue numbers +// UpdateRepoIssueNumbers updates one of a repositories amount of (open|closed) (issues|PRs) with the current count func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed bool) error { - e := db.GetEngine(ctx) + field := "num_" + if isClosed { + field += "closed_" + } if isPull { - if _, err := e.ID(repoID).Decr("num_pulls").Update(new(Repository)); err != nil { - return err - } - if isClosed { - if _, err := e.ID(repoID).Decr("num_closed_pulls").Update(new(Repository)); err != nil { - return err - } - } + field += "pulls" } else { - if _, err := e.ID(repoID).Decr("num_issues").Update(new(Repository)); err != nil { - return err - } - if isClosed { - if _, err := e.ID(repoID).Decr("num_closed_issues").Update(new(Repository)); err != nil { - return err - } - } + field += "issues" } - return nil + + subQuery := builder.Select("count(*)"). + From("issue").Where(builder.Eq{ + "repo_id": repoID, + "is_pull": isPull, + }.And(builder.If(isClosed, builder.Eq{"is_closed": isClosed}))) + + // builder.Update(cond) will generate SQL like UPDATE ... SET cond + query := builder.Update(builder.Eq{field: subQuery}). + From("repository"). + Where(builder.Eq{"id": repoID}) + _, err := db.Exec(ctx, query) + return err } From 43a8547df6e3a186b3b8430cd9856f612047a50a Mon Sep 17 00:00:00 2001 From: Xinyu Zhou Date: Thu, 27 Oct 2022 12:34:32 +0800 Subject: [PATCH 17/54] Added check for disabled Packages (#21540) (#21614) Backport #21540 At the moment, If admin disable Packages, still show the Packages on the admin dashboard. This patch added a check to hide the Packages entry. --- routers/web/web.go | 2 ++ templates/admin/navbar.tmpl | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/routers/web/web.go b/routers/web/web.go index b604337715476..e4fea38244a13 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -566,6 +566,8 @@ func RegisterRoutes(m *web.Route) { m.Post("/delete", admin.DeleteNotices) m.Post("/empty", admin.EmptyNotices) }) + }, func(ctx *context.Context) { + ctx.Data["EnablePackages"] = setting.Packages.Enabled }, adminReq) // ***** END: Admin ***** diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl index 24a0a093a6b28..e523da7e7a9f8 100644 --- a/templates/admin/navbar.tmpl +++ b/templates/admin/navbar.tmpl @@ -12,9 +12,11 @@ {{.i18n.Tr "admin.repositories"}} - - {{.i18n.Tr "packages.title"}} - + {{if .EnablePackages}} + + {{.i18n.Tr "packages.title"}} + + {{end}} {{if not DisableWebhooks}} {{.i18n.Tr "admin.hooks"}} From b0a057f1c05c539eed39fbc53f1578fc159259e0 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Thu, 27 Oct 2022 10:47:47 +0200 Subject: [PATCH 18/54] Fix `Timestamp.IsZero` (#21593) (#21604) Backport of #21593 Co-authored-by: Lunny Xiao --- modules/timeutil/timestamp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/timeutil/timestamp.go b/modules/timeutil/timestamp.go index 9c421914cb584..d2a5fec507f46 100644 --- a/modules/timeutil/timestamp.go +++ b/modules/timeutil/timestamp.go @@ -103,5 +103,5 @@ func (ts TimeStamp) FormatDate() string { // IsZero is zero time func (ts TimeStamp) IsZero() bool { - return ts.AsTimeInLocation(time.Local).IsZero() + return int64(ts) == 0 } From 5bc3fbd51173dc983b15e77291fda8111c4040d6 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Fri, 28 Oct 2022 03:38:59 +0200 Subject: [PATCH 19/54] Fix package access for admins and inactive users (#21580) (#21592) Backport of #21580 Co-authored-by: Lauris BH Co-authored-by: Lunny Xiao --- integrations/api_packages_container_test.go | 4 ++++ integrations/api_packages_test.go | 22 +++++++++++++++++++++ modules/context/package.go | 9 ++++++--- routers/api/packages/api.go | 2 ++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/integrations/api_packages_container_test.go b/integrations/api_packages_container_test.go index af659363d595a..ad987a662e979 100644 --- a/integrations/api_packages_container_test.go +++ b/integrations/api_packages_container_test.go @@ -433,6 +433,10 @@ func TestPackageContainer(t *testing.T) { assert.Equal(t, fmt.Sprintf("%d", len(blobContent)), resp.Header().Get("Content-Length")) assert.Equal(t, blobDigest, resp.Header().Get("Docker-Content-Digest")) + + req = NewRequest(t, "HEAD", fmt.Sprintf("%s/blobs/%s", url, blobDigest)) + addTokenAuthHeader(req, anonymousToken) + MakeRequest(t, req, http.StatusOK) }) t.Run("GetBlob", func(t *testing.T) { diff --git a/integrations/api_packages_test.go b/integrations/api_packages_test.go index 1f24807060df7..5b871cd476ec0 100644 --- a/integrations/api_packages_test.go +++ b/integrations/api_packages_test.go @@ -24,6 +24,7 @@ import ( func TestPackageAPI(t *testing.T) { defer prepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) session := loginUser(t, user.Name) token := getTokenForLoggedInUser(t, session) @@ -143,6 +144,27 @@ func TestPackageAPI(t *testing.T) { }) } +func TestPackageAccess(t *testing.T) { + defer prepareTestEnv(t)() + + admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User) + inactive := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 9}).(*user_model.User) + + uploadPackage := func(doer, owner *user_model.User, expectedStatus int) { + url := fmt.Sprintf("/api/packages/%s/generic/test-package/1.0/file.bin", owner.Name) + req := NewRequestWithBody(t, "PUT", url, bytes.NewReader([]byte{1})) + AddBasicAuthHeader(req, doer.Name) + MakeRequest(t, req, expectedStatus) + } + + uploadPackage(user, inactive, http.StatusUnauthorized) + uploadPackage(inactive, inactive, http.StatusUnauthorized) + uploadPackage(inactive, user, http.StatusUnauthorized) + uploadPackage(admin, inactive, http.StatusCreated) + uploadPackage(admin, user, http.StatusCreated) +} + func TestPackageCleanup(t *testing.T) { defer prepareTestEnv(t)() diff --git a/modules/context/package.go b/modules/context/package.go index 89a8c4466c154..5ccea9ee9b7ef 100644 --- a/modules/context/package.go +++ b/modules/context/package.go @@ -83,12 +83,15 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) { } func determineAccessMode(ctx *Context) (perm.AccessMode, error) { - accessMode := perm.AccessModeNone - if setting.Service.RequireSignInView && ctx.Doer == nil { - return accessMode, nil + return perm.AccessModeNone, nil } + if ctx.Doer != nil && !ctx.Doer.IsGhost() && (!ctx.Doer.IsActive || ctx.Doer.ProhibitLogin) { + return perm.AccessModeNone, nil + } + + accessMode := perm.AccessModeNone if ctx.Package.Owner.IsOrganization() { org := organization.OrgFromUser(ctx.Package.Owner) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 96924698a3c10..0df6012b566b7 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -55,6 +55,7 @@ func Routes() *web.Route { authGroup := auth.NewGroup(authMethods...) r.Use(func(ctx *context.Context) { ctx.Doer = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session) + ctx.IsSigned = ctx.Doer != nil }) r.Group("/{username}", func() { @@ -256,6 +257,7 @@ func ContainerRoutes() *web.Route { authGroup := auth.NewGroup(authMethods...) r.Use(func(ctx *context.Context) { ctx.Doer = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session) + ctx.IsSigned = ctx.Doer != nil }) r.Get("", container.ReqContainerAccess, container.DetermineSupport) From 7a2daae7c3a203a3716d653bfcdcdbb2fe48cb2d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Sun, 30 Oct 2022 11:16:09 +0800 Subject: [PATCH 20/54] Sync git hooks when config file path changed (#21619) (#21625) Backport #21619 . A patch to #17335. Just like AppPath, Gitea writes its own CustomConf into git hook scripts too. If Gitea's CustomConf changes, then the git push may fail. Co-authored-by: techknowlogick Co-authored-by: techknowlogick --- modules/appstate/item_runtime.go | 3 ++- routers/init.go | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/modules/appstate/item_runtime.go b/modules/appstate/item_runtime.go index 7fdc53f64247a..fd2ecb80ac8af 100644 --- a/modules/appstate/item_runtime.go +++ b/modules/appstate/item_runtime.go @@ -6,7 +6,8 @@ package appstate // RuntimeState contains app state for runtime, and we can save remote version for update checker here in future type RuntimeState struct { - LastAppPath string `json:"last_app_path"` + LastAppPath string `json:"last_app_path"` + LastCustomConf string `json:"last_custom_conf"` } // Name returns the item name diff --git a/routers/init.go b/routers/init.go index e640ca48453bc..a127e00e5a119 100644 --- a/routers/init.go +++ b/routers/init.go @@ -74,21 +74,31 @@ func InitGitServices() { mustInit(repo_service.Init) } -func syncAppPathForGit(ctx context.Context) error { +func syncAppConfForGit(ctx context.Context) error { runtimeState := new(appstate.RuntimeState) if err := appstate.AppState.Get(runtimeState); err != nil { return err } + + updated := false if runtimeState.LastAppPath != setting.AppPath { log.Info("AppPath changed from '%s' to '%s'", runtimeState.LastAppPath, setting.AppPath) + runtimeState.LastAppPath = setting.AppPath + updated = true + } + if runtimeState.LastCustomConf != setting.CustomConf { + log.Info("CustomConf changed from '%s' to '%s'", runtimeState.LastCustomConf, setting.CustomConf) + runtimeState.LastCustomConf = setting.CustomConf + updated = true + } + if updated { log.Info("re-sync repository hooks ...") mustInitCtx(ctx, repo_service.SyncRepositoryHooks) log.Info("re-write ssh public keys ...") mustInit(asymkey_model.RewriteAllPublicKeys) - runtimeState.LastAppPath = setting.AppPath return appstate.AppState.Set(runtimeState) } return nil @@ -153,7 +163,7 @@ func GlobalInitInstalled(ctx context.Context) { mustInit(repo_migrations.Init) eventsource.GetManager().Init() - mustInitCtx(ctx, syncAppPathForGit) + mustInitCtx(ctx, syncAppConfForGit) mustInit(ssh.Init) From d6d62c071f0c6f8799062274c19e87ad2572927d Mon Sep 17 00:00:00 2001 From: zeripath Date: Tue, 1 Nov 2022 19:24:37 +0000 Subject: [PATCH 21/54] Fix repository adoption on Windows (#21646) (#21651) Backport #21646 A bug was introduced in #17865 where filepath.Join is used to join putative unadopted repository owner and names together. This is incorrect as these names are then used as repository names - which shoud have the '/' separator. This means that adoption will not work on Windows servers. Fix #21632 Signed-off-by: Andrew Thornton --- services/repository/adopt.go | 11 ++++++----- services/repository/adopt_test.go | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 48f049cd28117..0df0aa049b136 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "os" + "path" "path/filepath" "strings" @@ -220,21 +221,21 @@ func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error return util.RemoveAll(repoPath) } -type unadoptedRrepositories struct { +type unadoptedRepositories struct { repositories []string index int start int end int } -func (unadopted *unadoptedRrepositories) add(repository string) { +func (unadopted *unadoptedRepositories) add(repository string) { if unadopted.index >= unadopted.start && unadopted.index < unadopted.end { unadopted.repositories = append(unadopted.repositories, repository) } unadopted.index++ } -func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unadopted *unadoptedRrepositories) error { +func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unadopted *unadoptedRepositories) error { if len(repoNamesToCheck) == 0 { return nil } @@ -266,7 +267,7 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad } for _, repoName := range repoNamesToCheck { if _, ok := repoNames[repoName]; !ok { - unadopted.add(filepath.Join(userName, repoName)) + unadopted.add(path.Join(userName, repoName)) // These are not used as filepaths - but as reponames - therefore use path.Join not filepath.Join } } return nil @@ -294,7 +295,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in var repoNamesToCheck []string start := (opts.Page - 1) * opts.PageSize - unadopted := &unadoptedRrepositories{ + unadopted := &unadoptedRepositories{ repositories: make([]string, 0, opts.PageSize), start: start, end: start + opts.PageSize, diff --git a/services/repository/adopt_test.go b/services/repository/adopt_test.go index 685bfe9bc4824..b450005f344bc 100644 --- a/services/repository/adopt_test.go +++ b/services/repository/adopt_test.go @@ -19,7 +19,7 @@ import ( func TestCheckUnadoptedRepositories_Add(t *testing.T) { start := 10 end := 20 - unadopted := &unadoptedRrepositories{ + unadopted := &unadoptedRepositories{ start: start, end: end, index: 0, @@ -39,7 +39,7 @@ func TestCheckUnadoptedRepositories(t *testing.T) { // // Non existent user // - unadopted := &unadoptedRrepositories{start: 0, end: 100} + unadopted := &unadoptedRepositories{start: 0, end: 100} err := checkUnadoptedRepositories("notauser", []string{"repo"}, unadopted) assert.NoError(t, err) assert.Equal(t, 0, len(unadopted.repositories)) @@ -50,14 +50,14 @@ func TestCheckUnadoptedRepositories(t *testing.T) { userName := "user2" repoName := "repo2" unadoptedRepoName := "unadopted" - unadopted = &unadoptedRrepositories{start: 0, end: 100} + unadopted = &unadoptedRepositories{start: 0, end: 100} err = checkUnadoptedRepositories(userName, []string{repoName, unadoptedRepoName}, unadopted) assert.NoError(t, err) assert.Equal(t, []string{path.Join(userName, unadoptedRepoName)}, unadopted.repositories) // // Existing (adopted) repository is not returned // - unadopted = &unadoptedRrepositories{start: 0, end: 100} + unadopted = &unadoptedRepositories{start: 0, end: 100} err = checkUnadoptedRepositories(userName, []string{repoName}, unadopted) assert.NoError(t, err) assert.Equal(t, 0, len(unadopted.repositories)) From 14342047add0292efba985c19fdc6da998a2e27c Mon Sep 17 00:00:00 2001 From: Wayne Starr Date: Tue, 8 Nov 2022 19:10:25 -0600 Subject: [PATCH 22/54] Allow local package identifiers for PyPI packages (#21690) (#21726) Backport (#21690) Fixes #21683 Co-authored-by: Lunny Xiao Co-authored-by: KN4CK3R --- integrations/api_packages_pypi_test.go | 4 ++-- routers/api/packages/pypi/pypi.go | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/integrations/api_packages_pypi_test.go b/integrations/api_packages_pypi_test.go index 5d610df39da92..cbb42020737a7 100644 --- a/integrations/api_packages_pypi_test.go +++ b/integrations/api_packages_pypi_test.go @@ -28,7 +28,7 @@ func TestPackagePyPI(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) packageName := "test-package" - packageVersion := "1.0.1" + packageVersion := "1.0.1+r1234" packageAuthor := "KN4CK3R" packageDescription := "Test Description" @@ -163,7 +163,7 @@ func TestPackagePyPI(t *testing.T) { nodes := htmlDoc.doc.Find("a").Nodes assert.Len(t, nodes, 2) - hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, packageName, packageVersion, hashSHA256)) + hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256)) for _, a := range nodes { for _, att := range a.Attr { diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go index bb2208e01e868..558ddd06f01ef 100644 --- a/routers/api/packages/pypi/pypi.go +++ b/routers/api/packages/pypi/pypi.go @@ -26,8 +26,15 @@ import ( var normalizer = strings.NewReplacer(".", "-", "_", "-") var nameMatcher = regexp.MustCompile(`\A[a-zA-Z0-9\.\-_]+\z`) -// https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions -var versionMatcher = regexp.MustCompile(`^([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$`) +// https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions +var versionMatcher = regexp.MustCompile(`\Av?` + + `(?:[0-9]+!)?` + // epoch + `[0-9]+(?:\.[0-9]+)*` + // release segment + `(?:[-_\.]?(?:a|b|c|rc|alpha|beta|pre|preview)[-_\.]?[0-9]*)?` + // pre-release + `(?:-[0-9]+|[-_\.]?(?:post|rev|r)[-_\.]?[0-9]*)?` + // post release + `(?:[-_\.]?dev[-_\.]?[0-9]*)?` + // dev release + `(?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)?` + // local version + `\z`) func apiError(ctx *context.Context, status int, obj interface{}) { helper.LogAndProcessError(ctx, status, obj, func(message string) { From 995ae06a6e02b7fb1938bb7ac2a1d5fe10be55b1 Mon Sep 17 00:00:00 2001 From: Wayne Starr Date: Wed, 9 Nov 2022 00:00:09 -0600 Subject: [PATCH 23/54] Allow for resolution of NPM registry paths that match upstream (#21568) (#21723) Backport (#21568) This PR fixes issue #21567 allowing for package tarball URLs to match the upstream registry (and GitLab/JFrog Artifactory URLs). It uses a regex to parse the filename (which contains the NPM version) and does a fuzzy search to pull it out. The regex was built/expanded from http://json.schemastore.org/package, https://github.com/Masterminds/semver, and https://docs.npmjs.com/cli/v6/using-npm/semver and is testable here: https://regex101.com/r/OydBJq/5 Co-authored-by: Lunny Xiao --- integrations/api_packages_npm_test.go | 8 ++++- routers/api/packages/api.go | 2 ++ routers/api/packages/npm/npm.go | 43 +++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/integrations/api_packages_npm_test.go b/integrations/api_packages_npm_test.go index bd65a2586714c..02e5138503afb 100644 --- a/integrations/api_packages_npm_test.go +++ b/integrations/api_packages_npm_test.go @@ -123,10 +123,16 @@ func TestPackageNpm(t *testing.T) { b, _ := base64.StdEncoding.DecodeString(data) assert.Equal(t, b, resp.Body.Bytes()) + req = NewRequest(t, "GET", fmt.Sprintf("%s/-/%s", root, filename)) + req = addTokenAuthHeader(req, token) + resp = MakeRequest(t, req, http.StatusOK) + + assert.Equal(t, b, resp.Body.Bytes()) + pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm) assert.NoError(t, err) assert.Len(t, pvs, 1) - assert.Equal(t, int64(1), pvs[0].DownloadCount) + assert.Equal(t, int64(2), pvs[0].DownloadCount) }) t.Run("PackageMetadata", func(t *testing.T) { diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 0df6012b566b7..35ac3540b69fe 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -199,11 +199,13 @@ func Routes() *web.Route { r.Get("", npm.PackageMetadata) r.Put("", reqPackageAccess(perm.AccessModeWrite), npm.UploadPackage) r.Get("/-/{version}/{filename}", npm.DownloadPackageFile) + r.Get("/-/{filename}", npm.DownloadPackageFileByName) }) r.Group("/{id}", func() { r.Get("", npm.PackageMetadata) r.Put("", reqPackageAccess(perm.AccessModeWrite), npm.UploadPackage) r.Get("/-/{version}/{filename}", npm.DownloadPackageFile) + r.Get("/-/{filename}", npm.DownloadPackageFileByName) }) r.Group("/-/package/@{scope}/{id}/dist-tags", func() { r.Get("", npm.ListPackageTags) diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index d127134d44558..57b24e3a8de87 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -105,6 +105,49 @@ func DownloadPackageFile(ctx *context.Context) { ctx.ServeStream(s, pf.Name) } +// DownloadPackageFileByName finds the version and serves the contents of a package +func DownloadPackageFileByName(ctx *context.Context) { + filename := ctx.Params("filename") + + pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{ + OwnerID: ctx.Package.Owner.ID, + Type: packages_model.TypeNpm, + Name: packages_model.SearchValue{ + ExactMatch: true, + Value: packageNameFromParams(ctx), + }, + HasFileWithName: filename, + IsInternal: false, + }) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + if len(pvs) != 1 { + apiError(ctx, http.StatusNotFound, nil) + return + } + + s, pf, err := packages_service.GetFileStreamByPackageVersion( + ctx, + pvs[0], + &packages_service.PackageFileInfo{ + Filename: filename, + }, + ) + if err != nil { + if err == packages_model.ErrPackageFileNotExist { + apiError(ctx, http.StatusNotFound, err) + return + } + apiError(ctx, http.StatusInternalServerError, err) + return + } + defer s.Close() + + ctx.ServeStream(s, pf.Name) +} + // UploadPackage creates a new package func UploadPackage(ctx *context.Context) { npmPackage, err := npm_module.ParsePackage(ctx.Req.Body) From 3c07ed091174f4258ae930b5516b2cf773ac313d Mon Sep 17 00:00:00 2001 From: Wayne Starr Date: Wed, 9 Nov 2022 09:02:21 -0600 Subject: [PATCH 24/54] Remove semver compatible flag and change pypi to an array of test cases (#21708) (#21729) Backport (#21708) This addresses #21707 and adds a second package test case for a non-semver compatible version (this might be overkill though since you could also edit the old package version to have an epoch in front and see the error, this just seemed more flexible for the future). Co-authored-by: KN4CK3R --- integrations/api_packages_pypi_test.go | 6 ++-- routers/api/packages/pypi/pypi.go | 12 +++++--- routers/api/packages/pypi/pypi_test.go | 39 ++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 routers/api/packages/pypi/pypi_test.go diff --git a/integrations/api_packages_pypi_test.go b/integrations/api_packages_pypi_test.go index cbb42020737a7..a04ee127d89c8 100644 --- a/integrations/api_packages_pypi_test.go +++ b/integrations/api_packages_pypi_test.go @@ -28,7 +28,7 @@ func TestPackagePyPI(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) packageName := "test-package" - packageVersion := "1.0.1+r1234" + packageVersion := "1!1.0.1+r1234" packageAuthor := "KN4CK3R" packageDescription := "Test Description" @@ -71,7 +71,7 @@ func TestPackagePyPI(t *testing.T) { pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) assert.NoError(t, err) - assert.NotNil(t, pd.SemVer) + assert.Nil(t, pd.SemVer) assert.IsType(t, &pypi.Metadata{}, pd.Metadata) assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageVersion, pd.Version.Version) @@ -99,7 +99,7 @@ func TestPackagePyPI(t *testing.T) { pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) assert.NoError(t, err) - assert.NotNil(t, pd.SemVer) + assert.Nil(t, pd.SemVer) assert.IsType(t, &pypi.Metadata{}, pd.Metadata) assert.Equal(t, packageName, pd.Package.Name) assert.Equal(t, packageVersion, pd.Version.Version) diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go index 558ddd06f01ef..9fdba1172c4cb 100644 --- a/routers/api/packages/pypi/pypi.go +++ b/routers/api/packages/pypi/pypi.go @@ -22,9 +22,9 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -// https://www.python.org/dev/peps/pep-0503/#normalized-names +// https://peps.python.org/pep-0426/#name var normalizer = strings.NewReplacer(".", "-", "_", "-") -var nameMatcher = regexp.MustCompile(`\A[a-zA-Z0-9\.\-_]+\z`) +var nameMatcher = regexp.MustCompile(`\A(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\.\-_]*[a-zA-Z0-9])\z`) // https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions var versionMatcher = regexp.MustCompile(`\Av?` + @@ -130,7 +130,7 @@ func UploadPackageFile(ctx *context.Context) { packageName := normalizer.Replace(ctx.Req.FormValue("name")) packageVersion := ctx.Req.FormValue("version") - if !nameMatcher.MatchString(packageName) || !versionMatcher.MatchString(packageVersion) { + if !isValidNameAndVersion(packageName, packageVersion) { apiError(ctx, http.StatusBadRequest, "invalid name or version") return } @@ -148,7 +148,7 @@ func UploadPackageFile(ctx *context.Context) { Name: packageName, Version: packageVersion, }, - SemverCompatible: true, + SemverCompatible: false, Creator: ctx.Doer, Metadata: &pypi_module.Metadata{ Author: ctx.Req.FormValue("author"), @@ -179,3 +179,7 @@ func UploadPackageFile(ctx *context.Context) { ctx.Status(http.StatusCreated) } + +func isValidNameAndVersion(packageName, packageVersion string) bool { + return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion) +} diff --git a/routers/api/packages/pypi/pypi_test.go b/routers/api/packages/pypi/pypi_test.go new file mode 100644 index 0000000000000..56e327a3472a2 --- /dev/null +++ b/routers/api/packages/pypi/pypi_test.go @@ -0,0 +1,39 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package pypi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsValidNameAndVersion(t *testing.T) { + // The test cases below were created from the following Python PEPs: + // https://peps.python.org/pep-0426/#name + // https://peps.python.org/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions + + // Valid Cases + assert.True(t, isValidNameAndVersion("A", "1.0.1")) + assert.True(t, isValidNameAndVersion("Test.Name.1234", "1.0.1")) + assert.True(t, isValidNameAndVersion("test_name", "1.0.1")) + assert.True(t, isValidNameAndVersion("test-name", "1.0.1")) + assert.True(t, isValidNameAndVersion("test-name", "v1.0.1")) + assert.True(t, isValidNameAndVersion("test-name", "2012.4")) + assert.True(t, isValidNameAndVersion("test-name", "1.0.1-alpha")) + assert.True(t, isValidNameAndVersion("test-name", "1.0.1a1")) + assert.True(t, isValidNameAndVersion("test-name", "1.0b2.r345.dev456")) + assert.True(t, isValidNameAndVersion("test-name", "1!1.0.1")) + assert.True(t, isValidNameAndVersion("test-name", "1.0.1+local.1")) + + // Invalid Cases + assert.False(t, isValidNameAndVersion(".test-name", "1.0.1")) + assert.False(t, isValidNameAndVersion("test!name", "1.0.1")) + assert.False(t, isValidNameAndVersion("-test-name", "1.0.1")) + assert.False(t, isValidNameAndVersion("test-name-", "1.0.1")) + assert.False(t, isValidNameAndVersion("test-name", "a1.0.1")) + assert.False(t, isValidNameAndVersion("test-name", "1.0.1aa")) + assert.False(t, isValidNameAndVersion("test-name", "1.0.0-alpha.beta")) +} From 9414260d67b972e5e35327189b9c53fa3dd574ac Mon Sep 17 00:00:00 2001 From: Xinyu Zhou Date: Thu, 10 Nov 2022 10:13:36 +0800 Subject: [PATCH 25/54] Fix UI language switching bug (#21597) (#21748) Backport #21597 Related: * https://github.com/go-gitea/gitea/pull/21596#issuecomment-1291450224 There was a bug when switching language by AJAX: the irrelevant POST requests were processed by the target page's handler. Now, use GET instead of POST. The GET requests should be harmless. Co-authored-by: delvh Co-authored-by: Jason Song Co-authored-by: Lunny Xiao Co-authored-by: wxiaoguang --- web_src/js/features/common-global.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index bada838d834dd..574cd9fa9ea2d 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -35,7 +35,7 @@ export function initHeadNavbarContentToggle() { export function initFootLanguageMenu() { function linkLanguageAction() { const $this = $(this); - $.post($this.data('url')).always(() => { + $.get($this.data('url')).always(() => { window.location.reload(); }); } From 43bddc1405fc533b887a2d6223b084f5e14ef49a Mon Sep 17 00:00:00 2001 From: Jason Song Date: Thu, 10 Nov 2022 11:15:28 +0800 Subject: [PATCH 26/54] Set last login when activating account (#21731) (#21754) Backport #21731. Fix #21698. Set the last login time to the current time when activating the user successfully. Co-authored-by: Lunny Xiao --- routers/web/auth/auth.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index bda6789d8d674..bb249c1eca12e 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -775,6 +775,13 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) { return } + // Register last login + user.SetLastLogin() + if err := user_model.UpdateUserCols(ctx, user, "last_login_unix"); err != nil { + ctx.ServerError("UpdateUserCols", err) + return + } + ctx.Flash.Success(ctx.Tr("auth.account_activated")) ctx.Redirect(setting.AppSubURL + "/") } From f24120148496e4d8f34c2d77b96e9968867c41ef Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 10 Nov 2022 22:22:45 +0800 Subject: [PATCH 27/54] Init git module before database migration (#21764) (#21766) Backport #21764 Some database migrations depend on the git module. --- models/migrations/migrations.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 2719f45efbbbc..6592c1c9f4ba9 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -14,6 +14,7 @@ import ( "regexp" "strings" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -496,6 +497,13 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t return nil } + // Some migration tasks depend on the git command + if git.DefaultContext == nil { + if err = git.InitSimple(context.Background()); err != nil { + return err + } + } + // Migrate for i, m := range migrations[v-minDBVersion:] { log.Info("Migration[%d]: %s", v+int64(i), m.Description()) From f321cdced72227da102a61f84acf5b50ffc1575a Mon Sep 17 00:00:00 2001 From: Xinyu Zhou Date: Fri, 11 Nov 2022 14:38:52 +0800 Subject: [PATCH 28/54] Add HEAD fix to gitea doctor (#21352) (#21751) Backport #21352 Due to a bug in presumably an older version of Gitea, multiple of my repositories still have their HEADs pointing to a `master` branch while the default branch on the UI is listed as `main`. This adds a `gitea doctor` command that will fix all of the HEAD references for repos when they're not synchronized with the default branch in the DB. This will help with cloning to ensure that git automatically checks out the right branch, instead of a nonexistent one. Note: I'm not sure if I actually need to do more other than add a file here. Will try testing this out on my server soon. Co-authored-by: Clar Fon <15850505+clarfonthey@users.noreply.github.com> Co-authored-by: zeripath Co-authored-by: wxiaoguang Co-authored-by: Lunny Xiao --- modules/doctor/heads.go | 91 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 modules/doctor/heads.go diff --git a/modules/doctor/heads.go b/modules/doctor/heads.go new file mode 100644 index 0000000000000..ec14aa4eadb1b --- /dev/null +++ b/modules/doctor/heads.go @@ -0,0 +1,91 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package doctor + +import ( + "context" + + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" +) + +func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) error { + numRepos := 0 + numHeadsBroken := 0 + numDefaultBranchesBroken := 0 + numReposUpdated := 0 + err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { + numRepos++ + runOpts := &git.RunOpts{Dir: repo.RepoPath()} + + _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(runOpts) + + head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(runOpts) + + // what we expect: default branch is valid, and HEAD points to it + if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch { + return nil + } + + if headErr != nil { + numHeadsBroken++ + } + if defaultBranchErr != nil { + numDefaultBranchesBroken++ + } + + // if default branch is broken, let the user fix that in the UI + if defaultBranchErr != nil { + logger.Warn("Default branch for %s/%s doesn't point to a valid commit", repo.OwnerName, repo.Name) + return nil + } + + // if we're not autofixing, that's all we can do + if !autofix { + return nil + } + + // otherwise, let's try fixing HEAD + err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", repo.DefaultBranch).Run(runOpts) + if err != nil { + logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) + return nil + } + numReposUpdated++ + return nil + }) + if err != nil { + logger.Critical("Error when fixing repo HEADs: %v", err) + } + + if autofix { + logger.Info("Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken", numRepos, numReposUpdated, numDefaultBranchesBroken+numHeadsBroken-numReposUpdated) + } else { + if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 { + logger.Info("All %d repos have their HEADs in the correct state") + } else { + if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 { + logger.Critical("Default branches are broken for %d/%d repos", numDefaultBranchesBroken, numRepos) + } else if numHeadsBroken != 0 && numDefaultBranchesBroken == 0 { + logger.Warn("HEADs are broken for %d/%d repos", numHeadsBroken, numRepos) + } else { + logger.Critical("Out of %d repos, HEADS are broken for %d and default branches are broken for %d", numRepos, numHeadsBroken, numDefaultBranchesBroken) + } + } + } + + return err +} + +func init() { + Register(&Check{ + Title: "Synchronize repo HEADs", + Name: "synchronize-repo-heads", + IsDefault: true, + Run: synchronizeRepoHeads, + Priority: 7, + }) +} From e609ef9585565986ebc2c0dfb4c896c111886233 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 11 Nov 2022 17:45:40 +0100 Subject: [PATCH 29/54] Ignore line anchor links with leading zeroes (#21728) (#21777) --- web_src/js/features/repo-code.js | 37 ++++++++++++++++----------- web_src/js/features/repo-code.test.js | 17 ++++++++++++ 2 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 web_src/js/features/repo-code.test.js diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js index 8562ba00723bc..04cd7b48424f9 100644 --- a/web_src/js/features/repo-code.js +++ b/web_src/js/features/repo-code.js @@ -2,6 +2,9 @@ import $ from 'jquery'; import {svg} from '../svg.js'; import {invertFileFolding} from './file-fold.js'; +export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/; +export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/; + function changeHash(hash) { if (window.history.pushState) { window.history.pushState(null, null, hash); @@ -114,7 +117,7 @@ export function initRepoCodeView() { }); $(window).on('hashchange', () => { - let m = window.location.hash.match(/^#(L\d+)-(L\d+)$/); + let m = window.location.hash.match(rangeAnchorRegex); let $list; if ($('div.blame').length) { $list = $('.code-view td.lines-code.blame-code'); @@ -124,27 +127,31 @@ export function initRepoCodeView() { let $first; if (m) { $first = $list.filter(`[rel=${m[1]}]`); - selectRange($list, $first, $list.filter(`[rel=${m[2]}]`)); + if ($first.length) { + selectRange($list, $first, $list.filter(`[rel=${m[2]}]`)); - // show code view menu marker (don't show in blame page) - if ($('div.blame').length === 0) { - showLineButton(); - } + // show code view menu marker (don't show in blame page) + if ($('div.blame').length === 0) { + showLineButton(); + } - $('html, body').scrollTop($first.offset().top - 200); - return; + $('html, body').scrollTop($first.offset().top - 200); + return; + } } - m = window.location.hash.match(/^#(L|n)(\d+)$/); + m = window.location.hash.match(singleAnchorRegex); if (m) { $first = $list.filter(`[rel=L${m[2]}]`); - selectRange($list, $first); + if ($first.length) { + selectRange($list, $first); - // show code view menu marker (don't show in blame page) - if ($('div.blame').length === 0) { - showLineButton(); - } + // show code view menu marker (don't show in blame page) + if ($('div.blame').length === 0) { + showLineButton(); + } - $('html, body').scrollTop($first.offset().top - 200); + $('html, body').scrollTop($first.offset().top - 200); + } } }).trigger('hashchange'); } diff --git a/web_src/js/features/repo-code.test.js b/web_src/js/features/repo-code.test.js new file mode 100644 index 0000000000000..0e0062a787f5d --- /dev/null +++ b/web_src/js/features/repo-code.test.js @@ -0,0 +1,17 @@ +import {singleAnchorRegex, rangeAnchorRegex} from './repo-code.js'; + +test('singleAnchorRegex', () => { + expect(singleAnchorRegex.test('#L0')).toEqual(false); + expect(singleAnchorRegex.test('#L1')).toEqual(true); + expect(singleAnchorRegex.test('#L01')).toEqual(false); + expect(singleAnchorRegex.test('#n0')).toEqual(false); + expect(singleAnchorRegex.test('#n1')).toEqual(true); + expect(singleAnchorRegex.test('#n01')).toEqual(false); +}); + +test('rangeAnchorRegex', () => { + expect(rangeAnchorRegex.test('#L0-L10')).toEqual(false); + expect(rangeAnchorRegex.test('#L1-L10')).toEqual(true); + expect(rangeAnchorRegex.test('#L01-L10')).toEqual(false); + expect(rangeAnchorRegex.test('#L1-L01')).toEqual(false); +}); From 795913e3c7f37c55a81e64517c740f0402b6230d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Sun, 13 Nov 2022 12:13:31 +0800 Subject: [PATCH 30/54] Load GitRepo in API before deleting issue (#21720) (#21795) Backport #21720. Fix #20921. The `ctx.Repo.GitRepo` has been used in deleting issues when the issue is a PR. Co-authored-by: Lunny Xiao Co-authored-by: Lauris BH --- routers/api/v1/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 7efaeaab86647..6fcbaefc93c60 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -890,7 +890,7 @@ func Routes() *web.Route { m.Group("/{index}", func() { m.Combo("").Get(repo.GetIssue). Patch(reqToken(), bind(api.EditIssueOption{}), repo.EditIssue). - Delete(reqToken(), reqAdmin(), repo.DeleteIssue) + Delete(reqToken(), reqAdmin(), context.ReferencesGitRepo(), repo.DeleteIssue) m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) From d25c74f3536909ef526b1dbf6a2eff328de83fab Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 13 Nov 2022 05:43:43 +0100 Subject: [PATCH 31/54] Upgrade golang.org/x/crypto (#21792) (#21794) - Backport #21792 - Update the crypto dependency to include https://github.com/golang/crypto/commit/6fad3dfc18918c2ac9c112e46b32473bd2e5e2f9 - Resolves #17798 Co-authored-by: John Olheiser --- go.mod | 8 ++++---- go.sum | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 9a000fec80ae5..a038eba3d7fcd 100644 --- a/go.mod +++ b/go.mod @@ -90,11 +90,11 @@ require ( github.com/yuin/goldmark-meta v1.1.0 go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/pwn v0.0.3 - golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 - golang.org/x/net v0.0.0-20220927171203-f486391704dc + golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891 + golang.org/x/net v0.2.0 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 - golang.org/x/text v0.3.8 + golang.org/x/sys v0.2.0 + golang.org/x/text v0.4.0 golang.org/x/tools v0.1.12 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df gopkg.in/ini.v1 v1.66.4 diff --git a/go.sum b/go.sum index 3a6e5588bc362..af1dc77f8a9bd 100644 --- a/go.sum +++ b/go.sum @@ -1676,8 +1676,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= -golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891 h1:WhEPFM1Ck5gaKybeSWvzI7Y/cd8K9K5tJGRxXMACOBA= +golang.org/x/crypto v0.2.1-0.20221112162523-6fad3dfc1891/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1788,8 +1788,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ= -golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1936,12 +1936,12 @@ golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1951,8 +1951,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 9dc53ba65f3a6b87acdd34d65dc4704cb3cf9e43 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sun, 13 Nov 2022 22:43:40 +0000 Subject: [PATCH 32/54] Prevent panic in doctor command when running default checks (#21791) (#21808) Backport #21791 There was a bug introduced in #21352 due to a change of behaviour caused by #19280. This causes a panic on running the default doctor checks because the panic introduced by #19280 assumes that the only way opts.StdOut and opts.Stderr can be set in RunOpts is deliberately. Unfortunately, when running a git.Command the provided RunOpts can be set, therefore if you share a common set of RunOpts these two values can be set by the previous commands. This PR stops using common RunOpts for the commands in that doctor check but secondly stops RunCommand variants from changing the provided RunOpts. Signed-off-by: Andrew Thornton --- modules/doctor/heads.go | 10 ++++------ modules/git/command.go | 26 ++++++++++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/modules/doctor/heads.go b/modules/doctor/heads.go index ec14aa4eadb1b..3ae7c6e15a9a9 100644 --- a/modules/doctor/heads.go +++ b/modules/doctor/heads.go @@ -19,11 +19,9 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) numReposUpdated := 0 err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { numRepos++ - runOpts := &git.RunOpts{Dir: repo.RepoPath()} + _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) - _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(runOpts) - - head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(runOpts) + head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) // what we expect: default branch is valid, and HEAD points to it if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch { @@ -49,7 +47,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) } // otherwise, let's try fixing HEAD - err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", repo.DefaultBranch).Run(runOpts) + err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", git.BranchPrefix+repo.DefaultBranch).Run(&git.RunOpts{Dir: repo.RepoPath()}) if err != nil { logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err) return nil @@ -65,7 +63,7 @@ func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) logger.Info("Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken", numRepos, numReposUpdated, numDefaultBranchesBroken+numHeadsBroken-numReposUpdated) } else { if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 { - logger.Info("All %d repos have their HEADs in the correct state") + logger.Info("All %d repos have their HEADs in the correct state", numRepos) } else { if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 { logger.Critical("Default branches are broken for %d/%d repos", numDefaultBranchesBroken, numRepos) diff --git a/modules/git/command.go b/modules/git/command.go index ed06dd9b08de9..17114ab473a84 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -169,8 +169,11 @@ func (c *Command) Run(opts *RunOpts) error { if opts == nil { opts = &RunOpts{} } - if opts.Timeout <= 0 { - opts.Timeout = defaultCommandExecutionTimeout + + // We must not change the provided options + timeout := opts.Timeout + if timeout <= 0 { + timeout = defaultCommandExecutionTimeout } if len(opts.Dir) == 0 { @@ -205,7 +208,7 @@ func (c *Command) Run(opts *RunOpts) error { if opts.UseContextTimeout { ctx, cancel, finished = process.GetManager().AddContext(c.parentContext, desc) } else { - ctx, cancel, finished = process.GetManager().AddContextTimeout(c.parentContext, opts.Timeout, desc) + ctx, cancel, finished = process.GetManager().AddContextTimeout(c.parentContext, timeout, desc) } defer finished() @@ -306,9 +309,20 @@ func (c *Command) RunStdBytes(opts *RunOpts) (stdout, stderr []byte, runErr RunS } stdoutBuf := &bytes.Buffer{} stderrBuf := &bytes.Buffer{} - opts.Stdout = stdoutBuf - opts.Stderr = stderrBuf - err := c.Run(opts) + + // We must not change the provided options as it could break future calls - therefore make a copy. + newOpts := &RunOpts{ + Env: opts.Env, + Timeout: opts.Timeout, + UseContextTimeout: opts.UseContextTimeout, + Dir: opts.Dir, + Stdout: stdoutBuf, + Stderr: stderrBuf, + Stdin: opts.Stdin, + PipelineFunc: opts.PipelineFunc, + } + + err := c.Run(newOpts) stderr = stderrBuf.Bytes() if err != nil { return nil, stderr, &runStdError{err: err, stderr: bytesToString(stderr)} From 65b5c8e5325c58fa0be77015adf0778bc1c74117 Mon Sep 17 00:00:00 2001 From: zeripath Date: Mon, 14 Nov 2022 07:58:11 +0000 Subject: [PATCH 33/54] Fix enabling partial clones on 1.17 (#21809) When backporting #20902 in #21058 there was a slight misbackport. It was missed that we needed to remove the global command option before setting the settings. Fix #21805 Signed-off-by: Andrew Thornton --- modules/git/git.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/git/git.go b/modules/git/git.go index 606a73b23097e..0fcc205b009a2 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -190,11 +190,6 @@ func InitOnceWithSync(ctx context.Context) (err error) { globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2") } - // By default partial clones are disabled, enable them from git v2.22 - if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil { - globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true") - } - // Explicitly disable credential helper, otherwise Git credentials might leak if CheckGitVersionAtLeast("2.9") == nil { globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") From 56716f5834d8fb7f77667d4ea7dce035d397d268 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 18 Nov 2022 15:24:49 +0100 Subject: [PATCH 34/54] Prevent dangling user redirects (#21856) (#21859) - Backport #21856 - It's possible that the `user_redirect` table contains a user id that no longer exists. - Delete a user redirect upon deleting the user. - Add a check for these dangling user redirects to check-db-consistency. --- models/user.go | 1 + modules/doctor/dbconsistency.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/models/user.go b/models/user.go index fb9246070d5aa..4f804b8232e28 100644 --- a/models/user.go +++ b/models/user.go @@ -87,6 +87,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { &user_model.Setting{UserID: u.ID}, &pull_model.AutoMerge{DoerID: u.ID}, &pull_model.ReviewState{UserID: u.ID}, + &user_model.Redirect{RedirectUserID: u.ID}, ); err != nil { return fmt.Errorf("deleteBeans: %v", err) } diff --git a/modules/doctor/dbconsistency.go b/modules/doctor/dbconsistency.go index f8b62e898a4bd..9518a1fb26e2f 100644 --- a/modules/doctor/dbconsistency.go +++ b/modules/doctor/dbconsistency.go @@ -205,6 +205,9 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er // find stopwatches without existing issue genericOrphanCheck("Orphaned Stopwatches without existing Issue", "stopwatch", "issue", "stopwatch.issue_id=`issue`.id"), + // find redirects without existing user. + genericOrphanCheck("Orphaned Redirects without existing redirect user", + "user_redirect", "user", "user_redirect.redirect_user_id=`user`.id"), ) for _, c := range consistencyChecks { From 87630a6583a0bc6d9b33aae1aa5a08eea765af24 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sun, 20 Nov 2022 13:35:26 +0100 Subject: [PATCH 35/54] Do not allow Ghost access to limited visible user/org (#21849) (#21875) Backport of #21849 Co-authored-by: Lauris BH --- models/organization/org.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/models/organization/org.go b/models/organization/org.go index 0d4a5e337b626..6e70233bc38c2 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -448,8 +448,9 @@ func CountOrgs(opts FindOrgOptions) (int64, error) { // HasOrgOrUserVisible tells if the given user can see the given org or user func HasOrgOrUserVisible(ctx context.Context, orgOrUser, user *user_model.User) bool { - // Not SignedUser - if user == nil { + // If user is nil, it's an anonymous user/request. + // The Ghost user is handled like an anonymous user. + if user == nil || user.IsGhost() { return orgOrUser.Visibility == structs.VisibleTypePublic } From ba16df8da3905e92639dce3cd0132ac08cee8b61 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sun, 20 Nov 2022 21:14:27 +0100 Subject: [PATCH 36/54] Fix setting HTTP headers after write (#21833) (#21874) Backport #21833 --- modules/context/context.go | 62 ++++++++++++++++++----- routers/api/packages/rubygems/rubygems.go | 8 ++- routers/common/repo.go | 41 ++++++--------- routers/web/feed/profile.go | 2 - 4 files changed, 70 insertions(+), 43 deletions(-) diff --git a/modules/context/context.go b/modules/context/context.go index d34dbb5e64834..bfb6f9338588a 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -34,6 +34,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/translation" + "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/services/auth" @@ -322,9 +323,9 @@ func (ctx *Context) plainTextInternal(skip, status int, bs []byte) { if statusPrefix == 4 || statusPrefix == 5 { log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs)) } - ctx.Resp.WriteHeader(status) ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8") ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") + ctx.Resp.WriteHeader(status) if _, err := ctx.Resp.Write(bs); err != nil { log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err) } @@ -345,16 +346,45 @@ func (ctx *Context) RespHeader() http.Header { return ctx.Resp.Header() } +type ServeHeaderOptions struct { + ContentType string // defaults to "application/octet-stream" + ContentTypeCharset string + Disposition string // defaults to "attachment" + Filename string + CacheDuration time.Duration // defaults to 5 minutes +} + // SetServeHeaders sets necessary content serve headers -func (ctx *Context) SetServeHeaders(filename string) { - ctx.Resp.Header().Set("Content-Description", "File Transfer") - ctx.Resp.Header().Set("Content-Type", "application/octet-stream") - ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+filename) - ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") - ctx.Resp.Header().Set("Expires", "0") - ctx.Resp.Header().Set("Cache-Control", "must-revalidate") - ctx.Resp.Header().Set("Pragma", "public") - ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") +func (ctx *Context) SetServeHeaders(opts *ServeHeaderOptions) { + header := ctx.Resp.Header() + + contentType := typesniffer.ApplicationOctetStream + if opts.ContentType != "" { + if opts.ContentTypeCharset != "" { + contentType = opts.ContentType + "; charset=" + strings.ToLower(opts.ContentTypeCharset) + } else { + contentType = opts.ContentType + } + } + header.Set("Content-Type", contentType) + header.Set("X-Content-Type-Options", "nosniff") + + if opts.Filename != "" { + disposition := opts.Disposition + if disposition == "" { + disposition = "attachment" + } + + backslashEscapedName := strings.ReplaceAll(strings.ReplaceAll(opts.Filename, `\`, `\\`), `"`, `\"`) // \ -> \\, " -> \" + header.Set("Content-Disposition", fmt.Sprintf(`%s; filename="%s"; filename*=UTF-8''%s`, disposition, backslashEscapedName, url.PathEscape(opts.Filename))) + header.Set("Access-Control-Expose-Headers", "Content-Disposition") + } + + duration := opts.CacheDuration + if duration == 0 { + duration = 5 * time.Minute + } + httpcache.AddCacheControlToHeader(header, duration) } // ServeContent serves content to http request @@ -366,7 +396,9 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa modTime = v } } - ctx.SetServeHeaders(name) + ctx.SetServeHeaders(&ServeHeaderOptions{ + Filename: name, + }) http.ServeContent(ctx.Resp, ctx.Req, name, modTime, r) } @@ -378,13 +410,17 @@ func (ctx *Context) ServeFile(file string, names ...string) { } else { name = path.Base(file) } - ctx.SetServeHeaders(name) + ctx.SetServeHeaders(&ServeHeaderOptions{ + Filename: name, + }) http.ServeFile(ctx.Resp, ctx.Req, file) } // ServeStream serves file via io stream func (ctx *Context) ServeStream(rd io.Reader, name string) { - ctx.SetServeHeaders(name) + ctx.SetServeHeaders(&ServeHeaderOptions{ + Filename: name, + }) _, err := io.Copy(ctx.Resp, rd) if err != nil { ctx.ServerError("Download file failed", err) diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go index 6fdd03e8ea704..eff6178f43ad2 100644 --- a/routers/api/packages/rubygems/rubygems.go +++ b/routers/api/packages/rubygems/rubygems.go @@ -75,7 +75,9 @@ func enumeratePackages(ctx *context.Context, filename string, pvs []*packages_mo }) } - ctx.SetServeHeaders(filename + ".gz") + ctx.SetServeHeaders(&context.ServeHeaderOptions{ + Filename: filename + ".gz", + }) zw := gzip.NewWriter(ctx.Resp) defer zw.Close() @@ -113,7 +115,9 @@ func ServePackageSpecification(ctx *context.Context) { return } - ctx.SetServeHeaders(filename) + ctx.SetServeHeaders(&context.ServeHeaderOptions{ + Filename: filename, + }) zw := zlib.NewWriter(ctx.Resp) defer zw.Close() diff --git a/routers/common/repo.go b/routers/common/repo.go index a9e80fad48c8d..f4b813d6b490b 100644 --- a/routers/common/repo.go +++ b/routers/common/repo.go @@ -7,7 +7,6 @@ package common import ( "fmt" "io" - "net/url" "path" "path/filepath" "strings" @@ -53,50 +52,44 @@ func ServeData(ctx *context.Context, filePath string, size int64, reader io.Read buf = buf[:n] } - httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 5*time.Minute) - if size >= 0 { ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size)) } else { log.Error("ServeData called to serve data: %s with size < 0: %d", filePath, size) } - fileName := path.Base(filePath) + opts := &context.ServeHeaderOptions{ + Filename: path.Base(filePath), + } + sniffedType := typesniffer.DetectContentType(buf) isPlain := sniffedType.IsText() || ctx.FormBool("render") - mimeType := "" - charset := "" if setting.MimeTypeMap.Enabled { - fileExtension := strings.ToLower(filepath.Ext(fileName)) - mimeType = setting.MimeTypeMap.Map[fileExtension] + fileExtension := strings.ToLower(filepath.Ext(filePath)) + opts.ContentType = setting.MimeTypeMap.Map[fileExtension] } - if mimeType == "" { + if opts.ContentType == "" { if sniffedType.IsBrowsableBinaryType() { - mimeType = sniffedType.GetMimeType() + opts.ContentType = sniffedType.GetMimeType() } else if isPlain { - mimeType = "text/plain" + opts.ContentType = "text/plain" } else { - mimeType = typesniffer.ApplicationOctetStream + opts.ContentType = typesniffer.ApplicationOctetStream } } if isPlain { + var charset string charset, err = charsetModule.DetectEncoding(buf) if err != nil { log.Error("Detect raw file %s charset failed: %v, using by default utf-8", filePath, err) charset = "utf-8" } + opts.ContentTypeCharset = strings.ToLower(charset) } - if charset != "" { - ctx.Resp.Header().Set("Content-Type", mimeType+"; charset="+strings.ToLower(charset)) - } else { - ctx.Resp.Header().Set("Content-Type", mimeType) - } - ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") - isSVG := sniffedType.IsSvgImage() // serve types that can present a security risk with CSP @@ -109,16 +102,12 @@ func ServeData(ctx *context.Context, filePath string, size int64, reader io.Read ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'") } - disposition := "inline" + opts.Disposition = "inline" if isSVG && !setting.UI.SVG.Enabled { - disposition = "attachment" + opts.Disposition = "attachment" } - // encode filename per https://datatracker.ietf.org/doc/html/rfc5987 - encodedFileName := `filename*=UTF-8''` + url.PathEscape(fileName) - - ctx.Resp.Header().Set("Content-Disposition", disposition+"; "+encodedFileName) - ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") + ctx.SetServeHeaders(opts) _, err = ctx.Resp.Write(buf) if err != nil { diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go index 61a39755f5020..6444222ba0356 100644 --- a/routers/web/feed/profile.go +++ b/routers/web/feed/profile.go @@ -5,7 +5,6 @@ package feed import ( - "net/http" "time" "code.gitea.io/gitea/models" @@ -57,7 +56,6 @@ func showUserFeed(ctx *context.Context, formatType string) { // writeFeed write a feeds.Feed as atom or rss to ctx.Resp func writeFeed(ctx *context.Context, feed *feeds.Feed, formatType string) { - ctx.Resp.WriteHeader(http.StatusOK) if formatType == "atom" { ctx.Resp.Header().Set("Content-Type", "application/atom+xml;charset=utf-8") if err := feed.WriteAtom(ctx.Resp); err != nil { From 6117c8b15a2ea8da84b02f9b2c7735ae2ebc1491 Mon Sep 17 00:00:00 2001 From: Xinyu Zhou Date: Thu, 24 Nov 2022 12:00:43 +0800 Subject: [PATCH 37/54] Fix vertical align of committer avatar rendered by email address (#21884) (#21919) Backport #21884 Committer avatar rendered by `func AvatarByEmail` are not vertical align as `func Avatar` does. - Replace literals `ui avatar` and `ui avatar vm` with the constant `DefaultAvatarClass` Signed-off-by: Xinyu Zhou --- models/avatars/avatar.go | 8 ++++++-- modules/templates/helper.go | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go index 9f7b0c474ff05..907210b781a40 100644 --- a/models/avatars/avatar.go +++ b/models/avatars/avatar.go @@ -19,8 +19,12 @@ import ( "code.gitea.io/gitea/modules/setting" ) -// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar -const DefaultAvatarPixelSize = 28 +const ( + // DefaultAvatarClass is the default class of a rendered avatar + DefaultAvatarClass = "ui avatar vm" + // DefaultAvatarPixelSize is the default size in pixels of a rendered avatar + DefaultAvatarPixelSize = 28 +) // EmailHash represents a pre-generated hash map (mainly used by LibravatarURL, it queries email server's DNS records) type EmailHash struct { diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 61f1f6adad0ca..94e50200f5fb4 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -647,7 +647,7 @@ func SVG(icon string, others ...interface{}) template.HTML { // Avatar renders user avatars. args: user, size (int), class (string) func Avatar(item interface{}, others ...interface{}) template.HTML { - size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...) + size, class := parseOthers(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) switch t := item.(type) { case *user_model.User: @@ -678,7 +678,7 @@ func AvatarByAction(action *models.Action, others ...interface{}) template.HTML // RepoAvatar renders repo avatars. args: repo, size(int), class (string) func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML { - size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...) + size, class := parseOthers(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) src := repo.RelAvatarLink() if src != "" { @@ -689,7 +689,7 @@ func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTM // AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string) func AvatarByEmail(email, name string, others ...interface{}) template.HTML { - size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...) + size, class := parseOthers(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) src := avatars.GenerateEmailAvatarFastLink(email, size*setting.Avatar.RenderedSizeFactor) if src != "" { From 82d50af72179df95698ebb00b060eeac4ac131ce Mon Sep 17 00:00:00 2001 From: Xinyu Zhou Date: Fri, 25 Nov 2022 00:02:42 +0800 Subject: [PATCH 38/54] Fix button in branch list, avoid unexpected page jump before restore branch actually done (#21562) (#21927) Backport #21562 Signed-off-by: Xinyu Zhou Co-authored-by: Lunny Xiao Co-authored-by: Lauris BH --- templates/repo/branch/list.tmpl | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index 4ae58ad3192fb..391ea43d3835d 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -22,18 +22,18 @@ {{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}} -
+
+ {{end}} {{if not $.DisableDownloadSourceArchives}} -
+ {{end}} @@ -108,26 +108,30 @@ {{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}} -
+
+ {{end}} {{if and (not .IsDeleted) (not $.DisableDownloadSourceArchives)}} - + {{end}} {{if and $.IsWriter (not $.IsMirror) (not $.Repository.IsArchived) (not .IsProtected)}} {{if .IsDeleted}} - {{svg "octicon-reply"}} + {{else}} - + {{end}} {{end}} From 8188cdfcd2073001a544feea9120673c3787f20a Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 5 Dec 2022 05:57:33 +0800 Subject: [PATCH 39/54] Fix ListBranches to handle empty case (#21921) (#22025) Fix #21910 Backport #21921 Co-authored-by: KN4CK3R Co-authored-by: KN4CK3R --- routers/api/v1/repo/branch.go | 60 ++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 84a172e92bf00..b0ef5bc96e48d 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -252,42 +252,50 @@ func ListBranches(ctx *context.APIContext) { // "200": // "$ref": "#/responses/BranchList" + var totalNumOfBranches int + var apiBranches []*api.Branch + listOptions := utils.GetListOptions(ctx) - skip, _ := listOptions.GetStartEnd() - branches, totalNumOfBranches, err := ctx.Repo.GitRepo.GetBranches(skip, listOptions.PageSize) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetBranches", err) - return - } - apiBranches := make([]*api.Branch, 0, len(branches)) - for i := range branches { - c, err := branches[i].GetCommit() + if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil { + skip, _ := listOptions.GetStartEnd() + branches, total, err := ctx.Repo.GitRepo.GetBranches(skip, listOptions.PageSize) if err != nil { - // Skip if this branch doesn't exist anymore. - if git.IsErrNotExist(err) { - totalNumOfBranches-- - continue - } - ctx.Error(http.StatusInternalServerError, "GetCommit", err) + ctx.Error(http.StatusInternalServerError, "GetBranches", err) return } - branchProtection, err := git_model.GetProtectedBranchBy(ctx, ctx.Repo.Repository.ID, branches[i].Name) - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err) - return - } - apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) - if err != nil { - ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err) - return + + apiBranches = make([]*api.Branch, 0, len(branches)) + for i := range branches { + c, err := branches[i].GetCommit() + if err != nil { + // Skip if this branch doesn't exist anymore. + if git.IsErrNotExist(err) { + total-- + continue + } + ctx.Error(http.StatusInternalServerError, "GetCommit", err) + return + } + branchProtection, err := git_model.GetProtectedBranchBy(ctx, ctx.Repo.Repository.ID, branches[i].Name) + if err != nil { + ctx.Error(http.StatusInternalServerError, "GetProtectedBranchBy", err) + return + } + apiBranch, err := convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.Doer, ctx.Repo.IsAdmin()) + if err != nil { + ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err) + return + } + apiBranches = append(apiBranches, apiBranch) } - apiBranches = append(apiBranches, apiBranch) + + totalNumOfBranches = total } ctx.SetLinkHeader(totalNumOfBranches, listOptions.PageSize) ctx.SetTotalCountHeader(int64(totalNumOfBranches)) - ctx.JSON(http.StatusOK, &apiBranches) + ctx.JSON(http.StatusOK, apiBranches) } // GetBranchProtection gets a branch protection From ee6d5124bd09a3ed0a2353b418d9b9e61c79d776 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Dec 2022 11:20:37 +0100 Subject: [PATCH 40/54] On Tag/Branch Exist Check, dont panic if repo is nil (#21787) (#21789) Backport #21787 Co-authored-by: Lunny Xiao Co-authored-by: Lauris BH --- modules/git/repo_branch_nogogit.go | 2 +- modules/git/repo_tag_nogogit.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go index bc58991085b78..2054df56cd4bf 100644 --- a/modules/git/repo_branch_nogogit.go +++ b/modules/git/repo_branch_nogogit.go @@ -53,7 +53,7 @@ func (repo *Repository) IsReferenceExist(name string) bool { // IsBranchExist returns true if given branch exists in current repository. func (repo *Repository) IsBranchExist(name string) bool { - if name == "" { + if repo == nil || name == "" { return false } diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go index 8d44db0a2e114..1617209896a6a 100644 --- a/modules/git/repo_tag_nogogit.go +++ b/modules/git/repo_tag_nogogit.go @@ -16,7 +16,7 @@ import ( // IsTagExist returns true if given tag exists in the repository. func (repo *Repository) IsTagExist(name string) bool { - if name == "" { + if repo == nil || name == "" { return false } From 601766d1fa39f981dcc72576af6ada18771bf54d Mon Sep 17 00:00:00 2001 From: zeripath Date: Tue, 6 Dec 2022 03:49:28 +0000 Subject: [PATCH 41/54] Handle empty author names (#21902) (#22028) Backport #21902 Although git does expect that author names should be of the form: `NAME ` some users have been able to create commits with: `` Fix #21900 Signed-off-by: Andrew Thornton Co-authored-by: Lauris BH --- modules/git/signature_gogit.go | 5 ++++- modules/git/signature_nogogit.go | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/git/signature_gogit.go b/modules/git/signature_gogit.go index 6f1c98420d05a..5ab38cd85252a 100644 --- a/modules/git/signature_gogit.go +++ b/modules/git/signature_gogit.go @@ -10,6 +10,7 @@ package git import ( "bytes" "strconv" + "strings" "time" "github.com/go-git/go-git/v5/plumbing/object" @@ -30,7 +31,9 @@ type Signature = object.Signature func newSignatureFromCommitline(line []byte) (_ *Signature, err error) { sig := new(Signature) emailStart := bytes.IndexByte(line, '<') - sig.Name = string(line[:emailStart-1]) + if emailStart > 0 { // Empty name has already occurred, even if it shouldn't + sig.Name = strings.TrimSpace(string(line[:emailStart-1])) + } emailEnd := bytes.IndexByte(line, '>') sig.Email = string(line[emailStart+1 : emailEnd]) diff --git a/modules/git/signature_nogogit.go b/modules/git/signature_nogogit.go index 9d5638b80456d..21d8d55feb247 100644 --- a/modules/git/signature_nogogit.go +++ b/modules/git/signature_nogogit.go @@ -11,6 +11,7 @@ import ( "bytes" "fmt" "strconv" + "strings" "time" ) @@ -51,7 +52,9 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { return } - sig.Name = string(line[:emailStart-1]) + if emailStart > 0 { // Empty name has already occurred, even if it shouldn't + sig.Name = strings.TrimSpace(string(line[:emailStart-1])) + } sig.Email = string(line[emailStart+1 : emailEnd]) hasTime := emailEnd+2 < len(line) From e93a4a0174206c96d9ac07a28e4f82d2ffce06c4 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 6 Dec 2022 22:15:38 +0800 Subject: [PATCH 42/54] Fix issue/PR numbers (#22037) (#22045) Backport #22037. When deleting a closed issue, we should update both `NumIssues`and `NumClosedIssues`, or `NumOpenIssues`(`= NumIssues -NumClosedIssues`) will be wrong. It's the same for pull requests. Releated to #21557. Alse fixed two harmless problems: - The SQL to check issue/PR total numbers is wrong, that means it will update the numbers even if they are correct. - Replace legacy `num_issues = num_issues + 1` operations with `UpdateRepoIssueNumbers`. --- models/issues/issue.go | 7 +------ models/repo.go | 4 ++-- services/issue/issue.go | 9 ++++++++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/models/issues/issue.go b/models/issues/issue.go index 814f3d934c838..ae43a020736e7 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -1007,12 +1007,7 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue } } - if opts.IsPull { - _, err = e.Exec("UPDATE `repository` SET num_pulls = num_pulls + 1 WHERE id = ?", opts.Issue.RepoID) - } else { - _, err = e.Exec("UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?", opts.Issue.RepoID) - } - if err != nil { + if err := repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.IsPull, false); err != nil { return err } diff --git a/models/repo.go b/models/repo.go index bc2601c9a37ce..cd42ab5e34622 100644 --- a/models/repo.go +++ b/models/repo.go @@ -602,7 +602,7 @@ func CheckRepoStats(ctx context.Context) error { }, // Repository.NumIssues { - statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", false, false), + statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=?)", false), repoStatsCorrectNumIssues, "repository count 'num_issues'", }, @@ -614,7 +614,7 @@ func CheckRepoStats(ctx context.Context) error { }, // Repository.NumPulls { - statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", false, true), + statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=?)", true), repoStatsCorrectNumPulls, "repository count 'num_pulls'", }, diff --git a/services/issue/issue.go b/services/issue/issue.go index 7c94582b82ae4..6745eab99ae05 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -220,9 +220,16 @@ func deleteIssue(issue *issues_model.Issue) error { return err } - if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, issue.IsClosed); err != nil { + // update the total issue numbers + if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, false); err != nil { return err } + // if the issue is closed, update the closed issue numbers + if issue.IsClosed { + if err := repo_model.UpdateRepoIssueNumbers(ctx, issue.RepoID, issue.IsPull, true); err != nil { + return err + } + } if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil { return fmt.Errorf("error updating counters for milestone id %d: %w", From e23ad87b550f2a6b4250a7e99f04fec16532c347 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sat, 10 Dec 2022 15:22:41 +0100 Subject: [PATCH 43/54] Workaround for container registry push/pull errors (#21862) (#22069) Backport of #21862 --- integrations/api_packages_container_test.go | 28 +++++++++++++++++++ modules/packages/content_store.go | 7 +++++ routers/api/packages/container/blob.go | 31 ++++++++++++++++++++- routers/api/packages/container/container.go | 27 ++++++++++++++++-- routers/api/packages/container/manifest.go | 11 ++++++++ 5 files changed, 100 insertions(+), 4 deletions(-) diff --git a/integrations/api_packages_container_test.go b/integrations/api_packages_container_test.go index ad987a662e979..08ca49cd6697f 100644 --- a/integrations/api_packages_container_test.go +++ b/integrations/api_packages_container_test.go @@ -6,10 +6,12 @@ package integrations import ( "bytes" + "crypto/sha256" "encoding/base64" "fmt" "net/http" "strings" + "sync" "testing" "code.gitea.io/gitea/models/db" @@ -549,6 +551,32 @@ func TestPackageContainer(t *testing.T) { }) } + // https://github.com/go-gitea/gitea/issues/19586 + t.Run("ParallelUpload", func(t *testing.T) { + defer PrintCurrentTest(t)() + + url := fmt.Sprintf("%sv2/%s/parallel", setting.AppURL, user.Name) + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + + content := []byte{byte(i)} + digest := fmt.Sprintf("sha256:%x", sha256.Sum256(content)) + + go func() { + defer wg.Done() + + req := NewRequestWithBody(t, "POST", fmt.Sprintf("%s/blobs/uploads?digest=%s", url, digest), bytes.NewReader(content)) + addTokenAuthHeader(req, userToken) + resp := MakeRequest(t, req, http.StatusCreated) + + assert.Equal(t, digest, resp.Header().Get("Docker-Content-Digest")) + }() + } + wg.Wait() + }) + t.Run("OwnerNameChange", func(t *testing.T) { defer PrintCurrentTest(t)() diff --git a/modules/packages/content_store.go b/modules/packages/content_store.go index a3a5d1a6663c8..b0e653a102018 100644 --- a/modules/packages/content_store.go +++ b/modules/packages/content_store.go @@ -30,6 +30,13 @@ func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) { return s.store.Open(KeyToRelativePath(key)) } +// FIXME: Workaround to be removed in v1.20 +// https://github.com/go-gitea/gitea/issues/19586 +func (s *ContentStore) Has(key BlobHash256Key) error { + _, err := s.store.Stat(KeyToRelativePath(key)) + return err +} + // Save stores a package blob func (s *ContentStore) Save(key BlobHash256Key, r io.Reader, size int64) error { _, err := s.store.Save(KeyToRelativePath(key), r, size) diff --git a/routers/api/packages/container/blob.go b/routers/api/packages/container/blob.go index 8a9cbd4a15fbf..33710472ceb47 100644 --- a/routers/api/packages/container/blob.go +++ b/routers/api/packages/container/blob.go @@ -7,8 +7,11 @@ package container import ( "context" "encoding/hex" + "errors" "fmt" + "os" "strings" + "sync" "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" @@ -19,6 +22,8 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) +var uploadVersionMutex sync.Mutex + // saveAsPackageBlob creates a package blob from an upload // The uploaded blob gets stored in a special upload version to link them to the package/image func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_service.PackageInfo) (*packages_model.PackageBlob, error) { @@ -28,6 +33,11 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic contentStore := packages_module.NewContentStore() + var uploadVersion *packages_model.PackageVersion + + // FIXME: Replace usage of mutex with database transaction + // https://github.com/go-gitea/gitea/pull/21862 + uploadVersionMutex.Lock() err := db.WithTx(func(ctx context.Context) error { created := true p := &packages_model.Package{ @@ -68,11 +78,30 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic } } + uploadVersion = pv + + return nil + }) + uploadVersionMutex.Unlock() + if err != nil { + return nil, err + } + + err = db.WithTx(func(ctx context.Context) error { pb, exists, err = packages_model.GetOrInsertBlob(ctx, pb) if err != nil { log.Error("Error inserting package blob: %v", err) return err } + // FIXME: Workaround to be removed in v1.20 + // https://github.com/go-gitea/gitea/issues/19586 + if exists { + err = contentStore.Has(packages_module.BlobHash256Key(pb.HashSHA256)) + if err != nil && errors.Is(err, os.ErrNotExist) { + log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256) + exists = false + } + } if !exists { if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), hsr, hsr.Size()); err != nil { log.Error("Error saving package blob in content store: %v", err) @@ -83,7 +112,7 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic filename := strings.ToLower(fmt.Sprintf("sha256_%s", pb.HashSHA256)) pf := &packages_model.PackageFile{ - VersionID: pv.ID, + VersionID: uploadVersion.ID, BlobID: pb.ID, Name: filename, LowerName: filename, diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index b961cd4afb393..acbe9670004e6 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -10,6 +10,7 @@ import ( "io" "net/http" "net/url" + "os" "regexp" "strconv" "strings" @@ -193,7 +194,7 @@ func InitiateUploadBlob(ctx *context.Context) { mount := ctx.FormTrim("mount") from := ctx.FormTrim("from") if mount != "" { - blob, _ := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{ + blob, _ := workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{ Image: from, Digest: mount, }) @@ -361,7 +362,7 @@ func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescri return nil, container_model.ErrContainerBlobNotExist } - return container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{ + return workaroundGetContainerBlob(ctx, &container_model.BlobSearchOptions{ OwnerID: ctx.Package.Owner.ID, Image: ctx.Params("image"), Digest: digest, @@ -503,7 +504,7 @@ func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDe return nil, container_model.ErrContainerBlobNotExist } - return container_model.GetContainerBlob(ctx, opts) + return workaroundGetContainerBlob(ctx, opts) } // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#checking-if-content-exists-in-the-registry @@ -643,3 +644,23 @@ func GetTagList(ctx *context.Context) { Tags: tags, }) } + +// FIXME: Workaround to be removed in v1.20 +// https://github.com/go-gitea/gitea/issues/19586 +func workaroundGetContainerBlob(ctx *context.Context, opts *container_model.BlobSearchOptions) (*packages_model.PackageFileDescriptor, error) { + blob, err := container_model.GetContainerBlob(ctx, opts) + if err != nil { + return nil, err + } + + err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(blob.Blob.HashSHA256)) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + log.Debug("Package registry inconsistent: blob %s does not exist on file system", blob.Blob.HashSHA256) + return nil, container_model.ErrContainerBlobNotExist + } + return nil, err + } + + return blob, nil +} diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go index 8beed3dbb7296..c63d9363dfe28 100644 --- a/routers/api/packages/container/manifest.go +++ b/routers/api/packages/container/manifest.go @@ -6,8 +6,10 @@ package container import ( "context" + "errors" "fmt" "io" + "os" "strings" "code.gitea.io/gitea/models/db" @@ -403,6 +405,15 @@ func createManifestBlob(ctx context.Context, mci *manifestCreationInfo, pv *pack log.Error("Error inserting package blob: %v", err) return nil, false, "", err } + // FIXME: Workaround to be removed in v1.20 + // https://github.com/go-gitea/gitea/issues/19586 + if exists { + err = packages_module.NewContentStore().Has(packages_module.BlobHash256Key(pb.HashSHA256)) + if err != nil && errors.Is(err, os.ErrNotExist) { + log.Debug("Package registry inconsistent: blob %s does not exist on file system", pb.HashSHA256) + exists = false + } + } if !exists { contentStore := packages_module.NewContentStore() if err := contentStore.Save(packages_module.BlobHash256Key(pb.HashSHA256), buf, buf.Size()); err != nil { From a8534ac4a4ff5d27479ed1f1d079d8fa54986711 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 13 Dec 2022 03:59:28 +0800 Subject: [PATCH 44/54] Fix permission check on issue/pull lock (#22114) Fix #22110 --- routers/web/web.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/routers/web/web.go b/routers/web/web.go index e4fea38244a13..3bcbc6d7c04dc 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -599,7 +599,6 @@ func RegisterRoutes(m *web.Route) { reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases) reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases) reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki) - reqRepoIssueWriter := context.RequireRepoWriter(unit.TypeIssues) reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues) reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests) reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(unit.TypeIssues, unit.TypePullRequests) @@ -893,8 +892,8 @@ func RegisterRoutes(m *web.Route) { }) }) m.Post("/reactions/{action}", bindIgnErr(forms.ReactionForm{}), repo.ChangeIssueReaction) - m.Post("/lock", reqRepoIssueWriter, bindIgnErr(forms.IssueLockForm{}), repo.LockIssue) - m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue) + m.Post("/lock", reqRepoIssuesOrPullsWriter, bindIgnErr(forms.IssueLockForm{}), repo.LockIssue) + m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue) m.Post("/delete", reqRepoAdmin, repo.DeleteIssue) }, context.RepoMustNotBeArchived()) m.Group("/{index}", func() { From c057590a3aaafada6d8a17a7943a04e03a9cd6c4 Mon Sep 17 00:00:00 2001 From: aceArt-GmbH <33117017+aceArt-GmbH@users.noreply.github.com> Date: Tue, 13 Dec 2022 02:18:20 +0100 Subject: [PATCH 45/54] Fix sorting admin user list by last login (#22081) (#22106) Backport of #22081 Co-authored-by: KN4CK3R --- routers/web/explore/user.go | 4 ++++ templates/admin/user/list.tmpl | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index ea0d7d5f9d8c3..64c84e01183e9 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -58,6 +58,10 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, orderBy = "`user`.updated_unix ASC" case "reversealphabetically": orderBy = "`user`.name DESC" + case "lastlogin": + orderBy = "`user`.last_login_unix ASC" + case "reverselastlogin": + orderBy = "`user`.last_login_unix DESC" case UserSearchDefaultSortType: // "alphabetically" default: orderBy = "`user`.name ASC" diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl index 963a440e299e9..edc58b7855cfd 100644 --- a/templates/admin/user/list.tmpl +++ b/templates/admin/user/list.tmpl @@ -76,9 +76,9 @@ {{.i18n.Tr "admin.users.2fa"}} {{.i18n.Tr "admin.users.repos"}} {{.i18n.Tr "admin.users.created"}} - + {{.i18n.Tr "admin.users.last_login"}} - {{SortArrow "leastupdate" "recentupdate" $.SortType false}} + {{SortArrow "lastlogin" "reverselastlogin" $.SortType false}} {{.i18n.Tr "admin.users.edit"}} From 0e95e7460e46fd353d0a9d07bb4e65d96a2a0177 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 13 Dec 2022 22:03:14 +0800 Subject: [PATCH 46/54] Fix warn in database structs sync (#22111) Fix #21880 --- models/repo/pushmirror.go | 1 + 1 file changed, 1 insertion(+) diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go index 42dbaef3fd6a5..a7727370d6b7a 100644 --- a/models/repo/pushmirror.go +++ b/models/repo/pushmirror.go @@ -23,6 +23,7 @@ type PushMirror struct { Repo *Repository `xorm:"-"` RemoteName string + SyncOnCommit bool `xorm:"NOT NULL DEFAULT true"` Interval time.Duration CreatedUnix timeutil.TimeStamp `xorm:"created"` LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"` From 6f323d13dd0e978a4e3795e93600cb115dbd80a6 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Wed, 14 Dec 2022 18:49:44 +0100 Subject: [PATCH 47/54] Fix condition for is_internal (#22095) (#22131) Backport of #22095 I changed it to a static condition because it needs a new version of xorm which is only available in 1.19. This change is valid because `SearchLatestVersions` is never called to list internal versions and there will no change to this behaviour in <1.19. Co-authored-by: Lunny Xiao Co-authored-by: techknowlogick --- models/packages/package_version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/packages/package_version.go b/models/packages/package_version.go index 78e76c50545ee..261a0285d99dd 100644 --- a/models/packages/package_version.go +++ b/models/packages/package_version.go @@ -289,7 +289,7 @@ func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*P sess := db.GetEngine(ctx). Table("package_version"). - Join("LEFT", "package_version pv2", "package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))"). + Join("LEFT", "package_version pv2", "package_version.package_id = pv2.package_id AND pv2.is_internal = ? AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))", false). Join("INNER", "package", "package.id = package_version.package_id"). Where(cond) From 721e422fa7a299d0dcfe9fe000c18f5e99380e37 Mon Sep 17 00:00:00 2001 From: zeripath Date: Thu, 15 Dec 2022 15:28:05 +0000 Subject: [PATCH 48/54] Correctly handle moved files in apply patch (#22118) (#22136) Backport #22118 Moved files in a patch will result in git apply returning: ``` error: {filename}: No such file or directory ``` This wasn't handled by the git apply patch code. This PR adds handling for this. Fix #22083 Signed-off-by: Andrew Thornton Signed-off-by: Andrew Thornton Co-authored-by: KN4CK3R --- services/pull/patch.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/pull/patch.go b/services/pull/patch.go index 32895b2e784fc..73ee7ba7eda6f 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -52,6 +52,8 @@ var patchErrorSuffices = []string{ ": patch does not apply", ": already exists in working directory", "unrecognized input", + ": No such file or directory", + ": does not exist in index", } // TestPatch will test whether a simple patch will apply @@ -415,6 +417,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * scanner := bufio.NewScanner(stderrReader) for scanner.Scan() { line := scanner.Text() + log.Trace("PullRequest[%d].testPatch: stderr: %s", pr.ID, line) if strings.HasPrefix(line, prefix) { conflict = true filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0]) From 068e96fbd25e2ea683d1f7d51cb43ab9b5f82356 Mon Sep 17 00:00:00 2001 From: Christian Ullrich Date: Mon, 19 Dec 2022 13:48:57 +0100 Subject: [PATCH 49/54] Do not list active repositories as unadopted (#22034) (#22167) Backport #22034 This fixes a bug where, when searching unadopted repositories, active repositories will be listed as well. This is because the size of the array of repository names to check is larger by one than the `IterateBufferSize`. For an `IterateBufferSize` of 50, the original code will pass 51 repository names but set the query to `LIMIT 50`. If all repositories in the query are active (i.e. not unadopted) one of them will be omitted from the result. Due to the `ORDER BY` clause it will be the oldest (or least recently modified) one. Co-authored-by: Christian Ullrich --- services/repository/adopt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 0df0aa049b136..46050cc5e41d7 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -340,7 +340,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in } repoNamesToCheck = append(repoNamesToCheck, name) - if len(repoNamesToCheck) > setting.Database.IterateBufferSize { + if len(repoNamesToCheck) >= setting.Database.IterateBufferSize { if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil { return err } From f51a19c537cbe63a9a670204c2c1c826af2506ed Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 20 Dec 2022 03:07:41 +0100 Subject: [PATCH 50/54] Check for zero time instant in TimeStamp.IsZero() (#22171) (#22173) - Backport of #22171 - Currently, the 'IsZero' function for 'TimeStamp' just checks if the unix time is zero, which is not the behavior of 'Time.IsZero()', but Gitea is using this method in accordance with the behavior of 'Time.IsZero()'. - Adds a new condition to check for the zero time instant. - Fixes a bug where non-expiring GPG keys where shown as they expired on Jan 01, 0001. - Related https://codeberg.org/Codeberg/Community/issues/791 --- modules/timeutil/timestamp.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/timeutil/timestamp.go b/modules/timeutil/timestamp.go index d2a5fec507f46..e45039b96e432 100644 --- a/modules/timeutil/timestamp.go +++ b/modules/timeutil/timestamp.go @@ -13,8 +13,13 @@ import ( // TimeStamp defines a timestamp type TimeStamp int64 -// mock is NOT concurrency-safe!! -var mock time.Time +var ( + // mock is NOT concurrency-safe!! + mock time.Time + + // Used for IsZero, to check if timestamp is the zero time instant. + timeZeroUnix = time.Time{}.Unix() +) // Set sets the time to a mocked time.Time func Set(now time.Time) { @@ -103,5 +108,5 @@ func (ts TimeStamp) FormatDate() string { // IsZero is zero time func (ts TimeStamp) IsZero() bool { - return int64(ts) == 0 + return int64(ts) == 0 || int64(ts) == timeZeroUnix } From 3d34cdabb9468f6f10826d87150ead8a9b438c9d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 21 Dec 2022 02:14:07 +0800 Subject: [PATCH 51/54] Disable auto tag to prepare next 1.17 release (#22180) Disable auto tag so that 1.17 release will not tag `:1`. --- .drone.yml | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/.drone.yml b/.drone.yml index 928e75d1ac75e..3618a399817f0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -902,8 +902,11 @@ steps: image: techknowlogick/drone-docker:latest pull: always settings: - auto_tag: true + auto_tag: false auto_tag_suffix: linux-amd64 + tags: + - ${DRONE_TAG##v}-linux-amd64 + - ${DRONE_TAG:1:4}-linux-amd64 repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io @@ -920,8 +923,11 @@ steps: image: techknowlogick/drone-docker:latest settings: dockerfile: Dockerfile.rootless - auto_tag: true + auto_tag: false auto_tag_suffix: linux-amd64-rootless + tags: + - ${DRONE_TAG##v}-linux-amd64-rootless + - ${DRONE_TAG:1:4}-linux-amd64-rootless repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io @@ -1126,8 +1132,11 @@ steps: image: techknowlogick/drone-docker:latest pull: always settings: - auto_tag: true + auto_tag: false auto_tag_suffix: linux-arm64 + tags: + - ${DRONE_TAG##v}-linux-arm64 + - ${DRONE_TAG:1:4}-linux-arm64 repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io @@ -1144,8 +1153,11 @@ steps: image: techknowlogick/drone-docker:latest settings: dockerfile: Dockerfile.rootless - auto_tag: true + auto_tag: false auto_tag_suffix: linux-arm64-rootless + tags: + - ${DRONE_TAG##v}-linux-arm64-rootless + - ${DRONE_TAG:1:4}-linux-arm64-rootless repo: gitea/gitea build_args: - GOPROXY=https://goproxy.io @@ -1299,7 +1311,7 @@ steps: image: plugins/manifest pull: always settings: - auto_tag: true + auto_tag: false ignore_missing: true spec: docker/manifest.rootless.tmpl password: @@ -1310,7 +1322,7 @@ steps: - name: manifest image: plugins/manifest settings: - auto_tag: true + auto_tag: false ignore_missing: true spec: docker/manifest.tmpl password: From 7e26f2b62601085ac7b422322f56675df5bcb268 Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Wed, 21 Dec 2022 18:01:11 +0100 Subject: [PATCH 52/54] Normalize NuGet package version on upload (#22186) (#22201) Backport of #22186 Co-authored-by: Lunny Xiao --- modules/packages/nuget/metadata.go | 20 +++++++++++++++++++- modules/packages/nuget/metadata_test.go | 13 +++++++++++++ routers/api/packages/nuget/api.go | 22 +++------------------- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go index 797bff45ac632..72cc4ddeeea94 100644 --- a/modules/packages/nuget/metadata.go +++ b/modules/packages/nuget/metadata.go @@ -6,8 +6,10 @@ package nuget import ( "archive/zip" + "bytes" "encoding/xml" "errors" + "fmt" "io" "path/filepath" "regexp" @@ -181,7 +183,23 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) { return &Package{ PackageType: packageType, ID: p.Metadata.ID, - Version: v.String(), + Version: toNormalizedVersion(v), Metadata: m, }, nil } + +// https://learn.microsoft.com/en-us/nuget/concepts/package-versioning#normalized-version-numbers +// https://github.com/NuGet/NuGet.Client/blob/dccbd304b11103e08b97abf4cf4bcc1499d9235a/src/NuGet.Core/NuGet.Versioning/VersionFormatter.cs#L121 +func toNormalizedVersion(v *version.Version) string { + var buf bytes.Buffer + segments := v.Segments64() + fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2]) + if len(segments) > 3 && segments[3] > 0 { + fmt.Fprintf(&buf, ".%d", segments[3]) + } + pre := v.Prerelease() + if pre != "" { + fmt.Fprint(&buf, "-", pre) + } + return buf.String() +} diff --git a/modules/packages/nuget/metadata_test.go b/modules/packages/nuget/metadata_test.go index e8c7773e97069..2fb517504cfa9 100644 --- a/modules/packages/nuget/metadata_test.go +++ b/modules/packages/nuget/metadata_test.go @@ -147,6 +147,19 @@ func TestParseNuspecMetaData(t *testing.T) { assert.Len(t, deps, 1) assert.Equal(t, dependencyID, deps[0].ID) assert.Equal(t, dependencyVersion, deps[0].Version) + + t.Run("NormalizedVersion", func(t *testing.T) { + np, err := ParseNuspecMetaData(strings.NewReader(` + + + test + 1.04.5.2.5-rc.1+metadata + +`)) + assert.NoError(t, err) + assert.NotNil(t, np) + assert.Equal(t, "1.4.5.2-rc.1", np.Version) + }) }) t.Run("Symbols Package", func(t *testing.T) { diff --git a/routers/api/packages/nuget/api.go b/routers/api/packages/nuget/api.go index c5cb75bb34daa..0bfdce779c982 100644 --- a/routers/api/packages/nuget/api.go +++ b/routers/api/packages/nuget/api.go @@ -5,15 +5,11 @@ package nuget import ( - "bytes" - "fmt" "sort" "time" packages_model "code.gitea.io/gitea/models/packages" nuget_module "code.gitea.io/gitea/modules/packages/nuget" - - "github.com/hashicorp/go-version" ) // ServiceIndexResponse https://docs.microsoft.com/en-us/nuget/api/service-index#resources @@ -113,8 +109,8 @@ func createRegistrationIndexResponse(l *linkBuilder, pds []*packages_model.Packa { RegistrationPageURL: l.GetRegistrationIndexURL(pds[0].Package.Name), Count: len(pds), - Lower: normalizeVersion(pds[0].SemVer), - Upper: normalizeVersion(pds[len(pds)-1].SemVer), + Lower: pds[0].Version.Version, + Upper: pds[len(pds)-1].Version.Version, Items: items, }, }, @@ -191,7 +187,7 @@ type PackageVersionsResponse struct { func createPackageVersionsResponse(pds []*packages_model.PackageDescriptor) *PackageVersionsResponse { versions := make([]string, 0, len(pds)) for _, pd := range pds { - versions = append(versions, normalizeVersion(pd.SemVer)) + versions = append(versions, pd.Version.Version) } return &PackageVersionsResponse{ @@ -266,15 +262,3 @@ func createSearchResult(l *linkBuilder, pds []*packages_model.PackageDescriptor) RegistrationIndexURL: l.GetRegistrationIndexURL(latest.Package.Name), } } - -// normalizeVersion removes the metadata -func normalizeVersion(v *version.Version) string { - var buf bytes.Buffer - segments := v.Segments64() - fmt.Fprintf(&buf, "%d.%d.%d", segments[0], segments[1], segments[2]) - pre := v.Prerelease() - if pre != "" { - fmt.Fprintf(&buf, "-%s", pre) - } - return buf.String() -} From 92f72d678c9ea42dacbc1b431df501dfe71605bd Mon Sep 17 00:00:00 2001 From: John Olheiser Date: Wed, 21 Dec 2022 14:09:55 -0600 Subject: [PATCH 53/54] fix: update libcurl in docs pipeline (#22205) Backport https://github.com/go-gitea/gitea/pull/22203 Signed-off-by: jolheiser --- .drone.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index 3618a399817f0..5c15637e997e0 100644 --- a/.drone.yml +++ b/.drone.yml @@ -851,7 +851,8 @@ steps: image: plugins/hugo:latest pull: always commands: - - apk add --no-cache make bash curl + # https://github.com/drone-plugins/drone-hugo/issues/36 + - apk upgrade --no-cache libcurl && apk add --no-cache make bash curl - cd docs - make trans-copy clean build From 73189f0a19cc215f8eb599414d3b97dabbbc43ad Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 22 Dec 2022 05:36:07 +0800 Subject: [PATCH 54/54] Update changelog for 1.17.4 (#22198) Co-authored-by: techknowlogick Co-authored-by: John Olheiser Co-authored-by: Lauris BH --- CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50766cbb09926..1b0d38d4cad37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,60 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.17.4](https://github.com/go-gitea/gitea/releases/tag/1.17.4) - 2022-12-21 + +* SECURITY + * Do not allow Ghost access to limited visible user/org (#21849) (#21875) + * Fix package access for admins and inactive users (#21580) (#21592) +* ENHANCEMENTS + * Fix button in branch list, avoid unexpected page jump before restore branch actually done (#21562) (#21927) + * Fix vertical align of committer avatar rendered by email address (#21884) (#21919) + * Fix setting HTTP headers after write (#21833) (#21874) + * Ignore line anchor links with leading zeroes (#21728) (#21777) + * Enable Monaco automaticLayout (#21516) +* BUGFIXES + * Do not list active repositories as unadopted (#22034) (#22167) + * Correctly handle moved files in apply patch (#22118) (#22136) + * Fix condition for is_internal (#22095) (#22131) + * Fix permission check on issue/pull lock (#22114) + * Fix sorting admin user list by last login (#22081) (#22106) + * Workaround for container registry push/pull errors (#21862) (#22069) + * Fix issue/PR numbers (#22037) (#22045) + * Handle empty author names (#21902) (#22028) + * Fix ListBranches to handle empty case (#21921) (#22025) + * Fix enabling partial clones on 1.17 (#21809) + * Prevent panic in doctor command when running default checks (#21791) (#21808) + * Upgrade golang.org/x/crypto (#21792) (#21794) + * Init git module before database migration (#21764) (#21766) + * Set last login when activating account (#21731) (#21754) + * Add HEAD fix to gitea doctor (#21352) (#21751) + * Fix UI language switching bug (#21597) (#21748) + * Remove semver compatible flag and change pypi to an array of test cases (#21708) (#21729) + * Allow local package identifiers for PyPI packages (#21690) (#21726) + * Fix repository adoption on Windows (#21646) (#21651) + * Sync git hooks when config file path changed (#21619) (#21625) + * Added check for disabled Packages (#21540) (#21614) + * Fix `Timestamp.IsZero` (#21593) (#21604) + * Fix issues count bug (#21600) + * Support binary deploy in npm packages (#21589) + * Update milestone counters when issue is deleted (#21459) (#21586) + * SessionUser protection against nil pointer dereference (#21581) + * Case-insensitive NuGet symbol file GUID (#21409) (#21575) + * Suppress `ExternalLoginUserNotExist` error (#21504) (#21572) + * Prevent Authorization header for presigned LFS urls (#21531) (#21569) + * Update binding to fix bugs (#21560) + * Fix generating compare link (#21519) (#21530) + * Ignore error when retrieving changed PR review files (#21487) (#21524) + * Fix incorrect notification commit url (#21479) (#21483) + * Display total commit count in hook message (#21400) (#21481) + * Enforce grouped NuGet search results (#21442) (#21480) + * Return 404 when user is not found on avatar (#21476) (#21477) + * Normalize NuGet package version on upload (#22186) (#22201) +* MISC + * Check for zero time instant in TimeStamp.IsZero() (#22171) (#22173) + * Fix warn in database structs sync (#22111) + * Allow for resolution of NPM registry paths that match upstream (#21568) (#21723) + ## [1.17.3](https://github.com/go-gitea/gitea/releases/tag/v1.17.3) - 2022-10-15 * SECURITY