diff --git a/.deadcode-out b/.deadcode-out index dc4aa0e498..c728ea81a0 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -271,13 +271,19 @@ package "code.gitea.io/gitea/modules/sync" package "code.gitea.io/gitea/modules/testlogger" func (*testLoggerWriterCloser).pushT - func (*testLoggerWriterCloser).Write + func (*testLoggerWriterCloser).Log + func (*testLoggerWriterCloser).recordError + func (*testLoggerWriterCloser).printMsg func (*testLoggerWriterCloser).popT - func (*testLoggerWriterCloser).Close func (*testLoggerWriterCloser).Reset func PrintCurrentTest func Printf func NewTestLoggerWriter + func (*TestLogEventWriter).Base + func (*TestLogEventWriter).GetLevel + func (*TestLogEventWriter).GetWriterName + func (*TestLogEventWriter).GetWriterType + func (*TestLogEventWriter).Run package "code.gitea.io/gitea/modules/timeutil" func GetExecutableModTime diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index af9ce93ba5..b2febb4ed8 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -6,6 +6,7 @@ repo_id: 2 # private is_private: true created_unix: 1603228283 + content: '1|' # issueId 4 - id: 2 diff --git a/models/fixtures/action_run.yml b/models/fixtures/action_run.yml index a42ab77ca5..405de2c548 100644 --- a/models/fixtures/action_run.yml +++ b/models/fixtures/action_run.yml @@ -17,6 +17,195 @@ updated: 1683636626 need_approval: 0 approved_by: 0 + event_payload: | + { + "after": "7a3858dc7f059543a8807a8b551304b7e362a7ef", + "before": "0000000000000000000000000000000000000000", + "commits": [ + { + "added": [ + ".forgejo/workflows/test.yml" + ], + "author": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "committer": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "id": "7a3858dc7f059543a8807a8b551304b7e362a7ef", + "message": "initial commit\n", + "modified": [], + "removed": [], + "timestamp": "2024-01-24T18:59:25Z", + "url": "http://10.201.14.40:3000/root/example-push/commit/7a3858dc7f059543a8807a8b551304b7e362a7ef", + "verification": null + } + ], + "compare_url": "http://10.201.14.40:3000/", + "head_commit": { + "added": [ + ".forgejo/workflows/test.yml" + ], + "author": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "committer": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "id": "7a3858dc7f059543a8807a8b551304b7e362a7ef", + "message": "initial commit\n", + "modified": [], + "removed": [], + "timestamp": "2024-01-24T18:59:25Z", + "url": "http://10.201.14.40:3000/root/example-push/commit/7a3858dc7f059543a8807a8b551304b7e362a7ef", + "verification": null + }, + "pusher": { + "active": false, + "avatar_url": "http://10.201.14.40:3000/avatars/04edfc0ef6c6cf6d6b88fbc69f9f9071", + "created": "2024-01-24T18:57:32Z", + "description": "", + "email": "root@noreply.10.201.14.40", + "followers_count": 0, + "following_count": 0, + "full_name": "", + "id": 1, + "is_admin": false, + "language": "", + "last_login": "0001-01-01T00:00:00Z", + "location": "", + "login": "root", + "login_name": "", + "prohibit_login": false, + "restricted": false, + "starred_repos_count": 0, + "username": "root", + "visibility": "public", + "website": "" + }, + "ref": "refs/heads/main", + "repository": { + "allow_merge_commits": true, + "allow_rebase": true, + "allow_rebase_explicit": true, + "allow_rebase_update": true, + "allow_squash_merge": true, + "archived": false, + "archived_at": "1970-01-01T00:00:00Z", + "avatar_url": "", + "clone_url": "http://10.201.14.40:3000/root/example-push.git", + "created_at": "2024-01-24T18:59:25Z", + "default_allow_maintainer_edit": false, + "default_branch": "main", + "default_delete_branch_after_merge": false, + "default_merge_style": "merge", + "description": "", + "empty": false, + "fork": false, + "forks_count": 0, + "full_name": "root/example-push", + "has_actions": true, + "has_issues": true, + "has_packages": true, + "has_projects": true, + "has_pull_requests": true, + "has_releases": true, + "has_wiki": true, + "html_url": "http://10.201.14.40:3000/root/example-push", + "id": 2, + "ignore_whitespace_conflicts": false, + "internal": false, + "internal_tracker": { + "allow_only_contributors_to_track_time": true, + "enable_issue_dependencies": true, + "enable_time_tracker": true + }, + "language": "", + "languages_url": "http://10.201.14.40:3000/api/v1/repos/root/example-push/languages", + "link": "", + "mirror": false, + "mirror_interval": "", + "mirror_updated": "0001-01-01T00:00:00Z", + "name": "example-push", + "object_format_name": "", + "open_issues_count": 0, + "open_pr_counter": 0, + "original_url": "", + "owner": { + "active": false, + "avatar_url": "http://10.201.14.40:3000/avatars/04edfc0ef6c6cf6d6b88fbc69f9f9071", + "created": "2024-01-24T18:57:32Z", + "description": "", + "email": "root@example.com", + "followers_count": 0, + "following_count": 0, + "full_name": "", + "id": 1, + "is_admin": false, + "language": "", + "last_login": "0001-01-01T00:00:00Z", + "location": "", + "login": "root", + "login_name": "", + "prohibit_login": false, + "restricted": false, + "starred_repos_count": 0, + "username": "root", + "visibility": "public", + "website": "" + }, + "parent": null, + "permissions": { + "admin": true, + "pull": true, + "push": true + }, + "private": false, + "release_counter": 0, + "repo_transfer": null, + "size": 25, + "ssh_url": "forgejo@10.201.14.40:root/example-push.git", + "stars_count": 0, + "template": false, + "updated_at": "2024-01-24T18:59:25Z", + "url": "http://10.201.14.40:3000/api/v1/repos/root/example-push", + "watchers_count": 1, + "website": "" + }, + "sender": { + "active": false, + "avatar_url": "http://10.201.14.40:3000/avatars/04edfc0ef6c6cf6d6b88fbc69f9f9071", + "created": "2024-01-24T18:57:32Z", + "description": "", + "email": "root@noreply.10.201.14.40", + "followers_count": 0, + "following_count": 0, + "full_name": "", + "id": 1, + "is_admin": false, + "language": "", + "last_login": "0001-01-01T00:00:00Z", + "location": "", + "login": "root", + "login_name": "", + "prohibit_login": false, + "restricted": false, + "starred_repos_count": 0, + "username": "root", + "visibility": "public", + "website": "" + }, + "total_commits": 0 + } + - id: 792 title: "update actions" @@ -36,3 +225,191 @@ updated: 1683636626 need_approval: 0 approved_by: 0 + event_payload: | + { + "after": "7a3858dc7f059543a8807a8b551304b7e362a7ef", + "before": "0000000000000000000000000000000000000000", + "commits": [ + { + "added": [ + ".forgejo/workflows/test.yml" + ], + "author": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "committer": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "id": "7a3858dc7f059543a8807a8b551304b7e362a7ef", + "message": "initial commit\n", + "modified": [], + "removed": [], + "timestamp": "2024-01-24T18:59:25Z", + "url": "http://10.201.14.40:3000/root/example-push/commit/7a3858dc7f059543a8807a8b551304b7e362a7ef", + "verification": null + } + ], + "compare_url": "http://10.201.14.40:3000/", + "head_commit": { + "added": [ + ".forgejo/workflows/test.yml" + ], + "author": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "committer": { + "email": "root@example.com", + "name": "username", + "username": "root" + }, + "id": "7a3858dc7f059543a8807a8b551304b7e362a7ef", + "message": "initial commit\n", + "modified": [], + "removed": [], + "timestamp": "2024-01-24T18:59:25Z", + "url": "http://10.201.14.40:3000/root/example-push/commit/7a3858dc7f059543a8807a8b551304b7e362a7ef", + "verification": null + }, + "pusher": { + "active": false, + "avatar_url": "http://10.201.14.40:3000/avatars/04edfc0ef6c6cf6d6b88fbc69f9f9071", + "created": "2024-01-24T18:57:32Z", + "description": "", + "email": "root@noreply.10.201.14.40", + "followers_count": 0, + "following_count": 0, + "full_name": "", + "id": 1, + "is_admin": false, + "language": "", + "last_login": "0001-01-01T00:00:00Z", + "location": "", + "login": "root", + "login_name": "", + "prohibit_login": false, + "restricted": false, + "starred_repos_count": 0, + "username": "root", + "visibility": "public", + "website": "" + }, + "ref": "refs/heads/main", + "repository": { + "allow_merge_commits": true, + "allow_rebase": true, + "allow_rebase_explicit": true, + "allow_rebase_update": true, + "allow_squash_merge": true, + "archived": false, + "archived_at": "1970-01-01T00:00:00Z", + "avatar_url": "", + "clone_url": "http://10.201.14.40:3000/root/example-push.git", + "created_at": "2024-01-24T18:59:25Z", + "default_allow_maintainer_edit": false, + "default_branch": "main", + "default_delete_branch_after_merge": false, + "default_merge_style": "merge", + "description": "", + "empty": false, + "fork": false, + "forks_count": 0, + "full_name": "root/example-push", + "has_actions": true, + "has_issues": true, + "has_packages": true, + "has_projects": true, + "has_pull_requests": true, + "has_releases": true, + "has_wiki": true, + "html_url": "http://10.201.14.40:3000/root/example-push", + "id": 2, + "ignore_whitespace_conflicts": false, + "internal": false, + "internal_tracker": { + "allow_only_contributors_to_track_time": true, + "enable_issue_dependencies": true, + "enable_time_tracker": true + }, + "language": "", + "languages_url": "http://10.201.14.40:3000/api/v1/repos/root/example-push/languages", + "link": "", + "mirror": false, + "mirror_interval": "", + "mirror_updated": "0001-01-01T00:00:00Z", + "name": "example-push", + "object_format_name": "", + "open_issues_count": 0, + "open_pr_counter": 0, + "original_url": "", + "owner": { + "active": false, + "avatar_url": "http://10.201.14.40:3000/avatars/04edfc0ef6c6cf6d6b88fbc69f9f9071", + "created": "2024-01-24T18:57:32Z", + "description": "", + "email": "root@example.com", + "followers_count": 0, + "following_count": 0, + "full_name": "", + "id": 1, + "is_admin": false, + "language": "", + "last_login": "0001-01-01T00:00:00Z", + "location": "", + "login": "root", + "login_name": "", + "prohibit_login": false, + "restricted": false, + "starred_repos_count": 0, + "username": "root", + "visibility": "public", + "website": "" + }, + "parent": null, + "permissions": { + "admin": true, + "pull": true, + "push": true + }, + "private": false, + "release_counter": 0, + "repo_transfer": null, + "size": 25, + "ssh_url": "forgejo@10.201.14.40:root/example-push.git", + "stars_count": 0, + "template": false, + "updated_at": "2024-01-24T18:59:25Z", + "url": "http://10.201.14.40:3000/api/v1/repos/root/example-push", + "watchers_count": 1, + "website": "" + }, + "sender": { + "active": false, + "avatar_url": "http://10.201.14.40:3000/avatars/04edfc0ef6c6cf6d6b88fbc69f9f9071", + "created": "2024-01-24T18:57:32Z", + "description": "", + "email": "root@noreply.10.201.14.40", + "followers_count": 0, + "following_count": 0, + "full_name": "", + "id": 1, + "is_admin": false, + "language": "", + "last_login": "0001-01-01T00:00:00Z", + "location": "", + "login": "root", + "login_name": "", + "prohibit_login": false, + "restricted": false, + "starred_repos_count": 0, + "username": "root", + "visibility": "public", + "website": "" + }, + "total_commits": 0 + } diff --git a/models/fixtures/webhook.yml b/models/fixtures/webhook.yml index f62bae1f31..989bb5657a 100644 --- a/models/fixtures/webhook.yml +++ b/models/fixtures/webhook.yml @@ -1,15 +1,17 @@ - id: 1 repo_id: 1 - url: www.example.com/url1 + url: http://www.example.com/url1 + http_method: POST content_type: 1 # json events: '{"push_only":true,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":false}}' - is_active: true + is_active: false # disable to prevent sending hook task during unrelated tests - id: 2 repo_id: 1 - url: www.example.com/url2 + url: http://www.example.com/url2 + http_method: POST content_type: 1 # json events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}' is_active: false @@ -18,14 +20,16 @@ id: 3 owner_id: 3 repo_id: 3 - url: www.example.com/url3 + url: http://www.example.com/url3 + http_method: POST content_type: 1 # json events: '{"push_only":false,"send_everything":false,"choose_events":false,"events":{"create":false,"push":true,"pull_request":true}}' - is_active: true + is_active: false - id: 4 repo_id: 2 - url: www.example.com/url4 + url: http://www.example.com/url4 + http_method: POST content_type: 1 # json events: '{"push_only":true,"branch_filter":"{master,feature*}"}' - is_active: true + is_active: false diff --git a/models/git/commit_status.go b/models/git/commit_status.go index 2d1d1bcb06..d204d11c28 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -199,22 +199,17 @@ func (status *CommitStatus) LocaleString(lang translation.Locale) string { // CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus { - var lastStatus *CommitStatus - state := api.CommitStatusSuccess - for _, status := range statuses { - if status.State.NoBetterThan(state) { - state = status.State - lastStatus = status + if len(statuses) == 0 { + return nil + } + + latestWorstStatus := statuses[0] + for _, status := range statuses[1:] { + if status.State.NoBetterThan(latestWorstStatus.State) { + latestWorstStatus = status } } - if lastStatus == nil { - if len(statuses) > 0 { - lastStatus = statuses[0] - } else { - lastStatus = &CommitStatus{} - } - } - return lastStatus + return latestWorstStatus } // CommitStatusOptions holds the options for query commit statuses diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go index 74ba4a1006..94c8d3776c 100644 --- a/models/git/commit_status_test.go +++ b/models/git/commit_status_test.go @@ -141,16 +141,20 @@ func Test_CalcCommitStatus(t *testing.T) { statuses: []*git_model.CommitStatus{ { State: structs.CommitStatusSuccess, + ID: 1, }, { State: structs.CommitStatusSuccess, + ID: 2, }, { State: structs.CommitStatusSuccess, + ID: 3, }, }, expected: &git_model.CommitStatus{ State: structs.CommitStatusSuccess, + ID: 3, }, }, { @@ -169,6 +173,10 @@ func Test_CalcCommitStatus(t *testing.T) { State: structs.CommitStatusError, }, }, + { + statuses: []*git_model.CommitStatus{}, + expected: nil, + }, } for _, kase := range kases { diff --git a/models/issues/issue.go b/models/issues/issue.go index baa79b30d1..54ef5cec7a 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -500,7 +500,7 @@ func (issue *Issue) GetLastEventLabelFake() string { // GetIssueByIndex returns raw issue without loading attributes by index in a repository. func GetIssueByIndex(ctx context.Context, repoID, index int64) (*Issue, error) { if index < 1 { - return nil, ErrIssueNotExist{} + return nil, ErrIssueNotExist{0, repoID, index} } issue := &Issue{ RepoID: repoID, diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index 85cafc1ab9..0726211ea8 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -160,6 +160,10 @@ func MainTest(m *testing.M) { exitStatus := m.Run() + if err := testlogger.WriterCloser.Reset(); err != nil && exitStatus == 0 { + fmt.Printf("testlogger.WriterCloser.Reset: %v\n", err) + os.Exit(1) + } if err := removeAllWithRetry(setting.RepoRootPath); err != nil { fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err) } diff --git a/models/migrations/v1_11/v111.go b/models/migrations/v1_11/v111.go index d757acb7d2..c18d7adae1 100644 --- a/models/migrations/v1_11/v111.go +++ b/models/migrations/v1_11/v111.go @@ -23,9 +23,9 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { Type int // Permissions - IsAdmin bool - IsRestricted bool `xorm:"NOT NULL DEFAULT false"` - Visibility int `xorm:"NOT NULL DEFAULT 0"` + IsAdmin bool + // IsRestricted bool `xorm:"NOT NULL DEFAULT false"` glitch: this column was added in v1_12/v121.go + // Visibility int `xorm:"NOT NULL DEFAULT 0"` glitch: this column was added in v1_12/v124.go } type Review struct { @@ -51,9 +51,9 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { ReviewTypeReject int = 3 // VisibleTypePublic Visible for everyone - VisibleTypePublic int = 0 + // VisibleTypePublic int = 0 // VisibleTypePrivate Visible only for organization's members - VisibleTypePrivate int = 2 + // VisibleTypePrivate int = 2 // unit.UnitTypeCode is unit type code UnitTypeCode int = 1 @@ -145,9 +145,9 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { hasOrgVisible := true // Not SignedUser if user == nil { - hasOrgVisible = repoOwner.Visibility == VisibleTypePublic + // hasOrgVisible = repoOwner.Visibility == VisibleTypePublic // VisibleTypePublic is the default } else if !user.IsAdmin { - hasMemberWithUserID, err := sess. + _, err := sess. Where("uid=?", user.ID). And("org_id=?", repoOwner.ID). Table("org_user"). @@ -155,9 +155,10 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { if err != nil { hasOrgVisible = false } - if (repoOwner.Visibility == VisibleTypePrivate || user.IsRestricted) && !hasMemberWithUserID { - hasOrgVisible = false - } + // VisibleTypePublic is the default so the condition below is always false + // if (repoOwner.Visibility == VisibleTypePrivate) && !hasMemberWithUserID { + // hasOrgVisible = false + // } } isCollaborator, err := sess.Get(&Collaboration{RepoID: repo.ID, UserID: user.ID}) @@ -195,7 +196,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { if user != nil { userID = user.ID - restricted = user.IsRestricted + restricted = false } if !restricted && !repo.IsPrivate { @@ -284,7 +285,7 @@ func AddBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { } // for a public repo on an organization, a non-restricted user has read permission on non-team defined units. - if !found && !repo.IsPrivate && !user.IsRestricted { + if !found && !repo.IsPrivate { if _, ok := perm.UnitsMode[u.Type]; !ok { perm.UnitsMode[u.Type] = AccessModeRead } diff --git a/models/migrations/v1_12/v128.go b/models/migrations/v1_12/v128.go index 44d44a26c5..6eea1337ef 100644 --- a/models/migrations/v1_12/v128.go +++ b/models/migrations/v1_12/v128.go @@ -94,7 +94,7 @@ func FixMergeBase(x *xorm.Engine) error { } else { parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath}) if err != nil { - log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) + log.Warn("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue } parents := strings.Split(strings.TrimSpace(parentsString), " ") diff --git a/models/migrations/v1_12/v134.go b/models/migrations/v1_12/v134.go index 3d1c82f09e..23c2916ba8 100644 --- a/models/migrations/v1_12/v134.go +++ b/models/migrations/v1_12/v134.go @@ -81,7 +81,7 @@ func RefixMergeBase(x *xorm.Engine) error { parentsString, _, err := git.NewCommand(git.DefaultContext, "rev-list", "--parents", "-n", "1").AddDynamicArguments(pr.MergedCommitID).RunStdString(&git.RunOpts{Dir: repoPath}) if err != nil { - log.Error("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) + log.Warn("Unable to get parents for merged PR ID %d, Index %d in %s/%s. Error: %v", pr.ID, pr.Index, baseRepo.OwnerName, baseRepo.Name, err) continue } parents := strings.Split(strings.TrimSpace(parentsString), " ") diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index f4403776ce..b4f6ffa189 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -124,6 +124,9 @@ func TestGetWebhookByOwnerID(t *testing.T) { func TestGetActiveWebhooksByRepoID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + + activateWebhook(t, 1) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{RepoID: 1, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { @@ -144,6 +147,9 @@ func TestGetWebhooksByRepoID(t *testing.T) { func TestGetActiveWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + + activateWebhook(t, 3) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3, IsActive: optional.Some(true)}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { @@ -152,8 +158,18 @@ func TestGetActiveWebhooksByOwnerID(t *testing.T) { } } +func activateWebhook(t *testing.T, hookID int64) { + t.Helper() + updated, err := db.GetEngine(db.DefaultContext).ID(hookID).Cols("is_active").Update(Webhook{IsActive: true}) + assert.Equal(t, int64(1), updated) + assert.NoError(t, err) +} + func TestGetWebhooksByOwnerID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) + + activateWebhook(t, 3) + hooks, err := db.Find[Webhook](db.DefaultContext, ListWebhookOptions{OwnerID: 3}) assert.NoError(t, err) if assert.Len(t, hooks, 1) { diff --git a/modules/queue/workergroup.go b/modules/queue/workergroup.go index 8c9dd274f0..ea4c0020c5 100644 --- a/modules/queue/workergroup.go +++ b/modules/queue/workergroup.go @@ -334,7 +334,10 @@ func (q *WorkerPoolQueue[T]) doRun() { // since we are already in a "flush" operation, so the dispatching function shouldn't read the flush chan. q.doDispatchBatchToWorker(wg, skipFlushChan) q.doFlush(wg, flush) - case err := <-wg.popItemErr: + case err, errOk := <-wg.popItemErr: + if !errOk { + return + } if !q.isCtxRunCanceled() { log.Error("Failed to pop item from queue %q (doRun): %v", q.GetName(), err) } diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index 4215567c00..908885554d 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -4,8 +4,11 @@ package testlogger import ( + "bytes" "context" + "errors" "fmt" + "io" "os" "runtime" "strings" @@ -27,53 +30,146 @@ var WriterCloser = &testLoggerWriterCloser{} type testLoggerWriterCloser struct { sync.RWMutex - t []testing.TB + t []testing.TB + errs []error // stack of error, parallel to the stack of testing.TB + err error // fallback if the stack is empty } func (w *testLoggerWriterCloser) pushT(t testing.TB) { w.Lock() w.t = append(w.t, t) + w.errs = append(w.errs, nil) w.Unlock() } -func (w *testLoggerWriterCloser) Write(p []byte) (int, error) { +func (w *testLoggerWriterCloser) Log(level log.Level, msg string) { + if len(msg) > 0 && msg[len(msg)-1] == '\n' { + msg = msg[:len(msg)-1] + } + + w.printMsg(msg) + if level >= log.ERROR { + w.recordError(msg) + } +} + +// list of error message which will not fail the test +// ideally this list should be empty, however ensuring that it does not grow +// is already a good first step. +var ignoredErrorMessageSuffixes = []string{ + // only seen on mysql tests https://codeberg.org/forgejo/forgejo/pulls/2657#issuecomment-1693055 + `table columns using inconsistent collation, they should use "utf8mb4_0900_ai_ci". Please go to admin panel Self Check page`, + + // TestAPIDeleteReleaseByTagName + // action notification were a commit cannot be computed (because the commit got deleted) + `Notify() [E] an error occurred while executing the DeleteRelease actions method: gitRepo.GetCommit: object does not exist [id: refs/tags/release-tag, rel_path: ]`, + `Notify() [E] an error occurred while executing the PushCommits actions method: gitRepo.GetCommit: object does not exist [id: refs/tags/release-tag, rel_path: ]`, + + // TestAPIRepoTags + `Notify() [E] an error occurred while executing the DeleteRelease actions method: gitRepo.GetCommit: object does not exist [id: refs/tags/gitea/22, rel_path: ]`, + `Notify() [E] an error occurred while executing the PushCommits actions method: gitRepo.GetCommit: object does not exist [id: refs/tags/gitea/22, rel_path: ]`, + + // TestAPIDeleteTagByName + `Notify() [E] an error occurred while executing the DeleteRelease actions method: gitRepo.GetCommit: object does not exist [id: refs/tags/delete-tag, rel_path: ]`, + `Notify() [E] an error occurred while executing the PushCommits actions method: gitRepo.GetCommit: object does not exist [id: refs/tags/delete-tag, rel_path: ]`, + + // TestAPIGenerateRepo + `Notify() [E] an error occurred while executing the CreateRepository actions method: gitRepo.GetCommit: object does not exist [id: , rel_path: ]`, + + // TestAPIPullReview + `PullRequestReview() [E] Unsupported review webhook type`, + + // TestAPIPullReviewRequest + `ToAPIPullRequest() [E] unable to resolve PR head ref`, + + // TestAPILFSUpload + `Put() [E] Whilst putting LFS OID[ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb]: Failed to copy to tmpPath: ca/97/8112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb Error: content size does not match`, + `[E] Error putting LFS MetaObject [ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb] into content store. Error: content size does not match`, + `UploadHandler() [E] Upload does not match LFS MetaObject [ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb]. Error: content size does not match`, + `Put() [E] Whilst putting LFS OID[2581dd7bbc1fe44726de4b7dd806a087a978b9c5aec0a60481259e34be09b06a]: Failed to copy to tmpPath: 25/81/dd7bbc1fe44726de4b7dd806a087a978b9c5aec0a60481259e34be09b06a Error: content hash does not match OID`, + `[E] Error putting LFS MetaObject [2581dd7bbc1fe44726de4b7dd806a087a978b9c5aec0a60481259e34be09b06a] into content store. Error: content hash does not match OID`, + `UploadHandler() [E] Upload does not match LFS MetaObject [2581dd7bbc1fe44726de4b7dd806a087a978b9c5aec0a60481259e34be09b06a]. Error: content hash does not match OID`, + `UploadHandler() [E] Upload does not match LFS MetaObject [83de2e488b89a0aa1c97496b888120a28b0c1e15463a4adb8405578c540f36d4]. Error: content size does not match`, + + // TestAPILFSVerify + `getAuthenticatedMeta() [E] Unable to get LFS OID[fb8f7d8435968c4f82a726a92395be4d16f2f63116caf36c8ad35c60831ab042] Error: LFS Meta object does not exist`, + + // TestAPIUpdateOrgAvatar + `UpdateAvatar() [E] UploadAvatar: image.DecodeConfig: image: unknown format`, + + // TestGetAttachment + `/data/attachments/a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18: no such file or directory`, + + // TestBlockUser + `BlockedUsersUnblock() [E] IsOrganization: org3 is an organization not a user`, + `BlockedUsersBlock() [E] IsOrganization: org3 is an organization not a user`, + `Action() [E] Cannot perform this action on an organization "unblock"`, + `Action() [E] Cannot perform this action on an organization "block"`, + + // TestBlockActions + `/gitea-repositories/user10/repo7.git Error: no such file or directory`, + + // TestE2e/explore.test.e2e + `TrString() [E] Missing translation "more_items"`, + + // TestRebuildCargo + `RebuildCargoIndex() [E] RebuildIndex failed: GetRepositoryByOwnerAndName: repository does not exist [id: 0, uid: 0, owner_name: user2, name: _cargo-index]`, +} + +func (w *testLoggerWriterCloser) recordError(msg string) { + for _, s := range ignoredErrorMessageSuffixes { + if strings.HasSuffix(msg, s) { + return + } + } + + w.Lock() + defer w.Unlock() + + err := w.err + if len(w.errs) > 0 { + err = w.errs[len(w.errs)-1] + } + + err = errors.Join(err, errors.New(msg)) + + if len(w.errs) > 0 { + w.errs[len(w.errs)-1] = err + } else { + w.err = err + } +} + +func (w *testLoggerWriterCloser) printMsg(msg string) { // There was a data race problem: the logger system could still try to output logs after the runner is finished. // So we must ensure that the "t" in stack is still valid. w.RLock() defer w.RUnlock() - var t testing.TB if len(w.t) > 0 { - t = w.t[len(w.t)-1] - } - - if len(p) > 0 && p[len(p)-1] == '\n' { - p = p[:len(p)-1] - } - - if t == nil { + t := w.t[len(w.t)-1] + t.Log(msg) + } else { // if there is no running test, the log message should be outputted to console, to avoid losing important information. // the "???" prefix is used to match the "===" and "+++" in PrintCurrentTest - return fmt.Fprintf(os.Stdout, "??? [TestLogger] %s\n", p) + fmt.Fprintln(os.Stdout, "??? [TestLogger]", msg) } - - t.Log(string(p)) - return len(p), nil } -func (w *testLoggerWriterCloser) popT() { +func (w *testLoggerWriterCloser) popT() error { w.Lock() + defer w.Unlock() + if len(w.t) > 0 { w.t = w.t[:len(w.t)-1] + err := w.errs[len(w.errs)-1] + w.errs = w.errs[:len(w.errs)-1] + return err } - w.Unlock() + return w.err } -func (w *testLoggerWriterCloser) Close() error { - return nil -} - -func (w *testLoggerWriterCloser) Reset() { +func (w *testLoggerWriterCloser) Reset() error { w.Lock() if len(w.t) > 0 { for _, t := range w.t { @@ -84,8 +180,12 @@ func (w *testLoggerWriterCloser) Reset() { t.Errorf("Unclosed logger writer in test: %s", t.Name()) } w.t = nil + w.errs = nil } + err := w.err + w.err = nil w.Unlock() + return err } // PrintCurrentTest prints the current test to os.Stdout @@ -132,7 +232,10 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() { _, _ = fmt.Fprintf(os.Stdout, "+++ %s had a slow clean-up flush (took %v)\n", t.Name(), flushTook) } } - WriterCloser.popT() + + if err := WriterCloser.popT(); err != nil { + t.Errorf("testlogger.go:recordError() FATAL ERROR: log.Error has been called: %v", err) + } } } @@ -146,19 +249,72 @@ func Printf(format string, args ...any) { _, _ = fmt.Fprintf(os.Stdout, "\t"+format, args...) } -// TestLogEventWriter is a logger which will write to the testing log -type TestLogEventWriter struct { - *log.EventWriterBaseImpl -} - // NewTestLoggerWriter creates a TestLogEventWriter as a log.LoggerProvider func NewTestLoggerWriter(name string, mode log.WriterMode) log.EventWriter { w := &TestLogEventWriter{} - w.EventWriterBaseImpl = log.NewEventWriterBase(name, "test-log-writer", mode) - w.OutputWriteCloser = WriterCloser + w.base = log.NewEventWriterBase(name, "test-log-writer", mode) + w.writer = WriterCloser return w } +// TestLogEventWriter is a logger which will write to the testing log +type TestLogEventWriter struct { + base *log.EventWriterBaseImpl + writer *testLoggerWriterCloser +} + +// Base implements log.EventWriter. +func (t *TestLogEventWriter) Base() *log.EventWriterBaseImpl { + return t.base +} + +// GetLevel implements log.EventWriter. +func (t *TestLogEventWriter) GetLevel() log.Level { + return t.base.GetLevel() +} + +// GetWriterName implements log.EventWriter. +func (t *TestLogEventWriter) GetWriterName() string { + return t.base.GetWriterName() +} + +// GetWriterType implements log.EventWriter. +func (t *TestLogEventWriter) GetWriterType() string { + return t.base.GetWriterType() +} + +// Run implements log.EventWriter. +func (t *TestLogEventWriter) Run(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case event, ok := <-t.base.Queue: + if !ok { + return + } + + var errorMsg string + + switch msg := event.Msg.(type) { + case string: + errorMsg = msg + case []byte: + errorMsg = string(msg) + case io.WriterTo: + var buf bytes.Buffer + if _, err := msg.WriteTo(&buf); err != nil { + panic(err) + } + errorMsg = buf.String() + default: + errorMsg = fmt.Sprint(msg) + } + t.writer.Log(event.Origin.Level, errorMsg) + } + } +} + func init() { const relFilePath = "modules/testlogger/testlogger.go" _, filename, _, _ := runtime.Caller(0) diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index 44c3fb0c88..0e6ddab401 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -114,16 +114,22 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { format := trKey idx, ok := l.store.trKeyToIdxMap[trKey] + found := false if ok { if msg, ok := l.idxToMsgMap[idx]; ok { format = msg // use the found translation + found = true } else if def, ok := l.store.localeMap[l.store.defaultLang]; ok { // try to use default locale's translation if msg, ok := def.idxToMsgMap[idx]; ok { format = msg + found = true } } } + if !found { + log.Error("Missing translation %q", trKey) + } msg, err := Format(format, trArgs...) if err != nil { diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 0e27147e10..375d681c7c 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -1655,6 +1655,7 @@ config.default_keep_email_private = أخفِ عناوين البريد الإل config.default_allow_create_organization = اسمح بإنشاء المنظمات مبدئيا config.enable_timetracking = فعّل تتبع الوقت config.default_enable_timetracking = فعّل تتبع الوقت مبدئيا +config.allow_dots_in_usernames = السماح للمستخدمين بوضع نقاط في أسمائهم. لا يؤثر على الحسابات الموجودة. config.default_allow_only_contributors_to_track_time = اسمح للمشتركين في المستودع موحدهم بتتبع الوقت [form] diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index cb984aad07..6697ba4b3f 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -3224,6 +3224,7 @@ config.default_keep_email_private=Ve výchozím nastavení skrýt e-mailové adr config.default_allow_create_organization=Povolit ve výchozím nastavení vytvářet organizace config.enable_timetracking=Povolit sledování času config.default_enable_timetracking=Povolit ve výchozím nastavení sledování času +config.allow_dots_in_usernames = Povolit uživatelům používat tečky ve svých uživatelských jménech. Neovlivní stávající účty. config.default_allow_only_contributors_to_track_time=Povolit sledování času pouze přispěvatelům config.no_reply_address=Skrytá e-mailová doména config.default_visibility_organization=Výchozí viditelnost nových organizací diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 983580598e..cb9c2fe725 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -3204,6 +3204,7 @@ config.default_keep_email_private=E-Mail-Adressen standardmäßig verbergen config.default_allow_create_organization=Erstellen von Organisationen standardmäßig erlauben config.enable_timetracking=Zeiterfassung aktivieren config.default_enable_timetracking=Zeiterfassung standardmäßig aktivieren +config.allow_dots_in_usernames = Erlaubt Benutzern die Verwendung von Punkten in ihren Benutzernamen. Hat keine Auswirkungen auf bestehende Konten. config.default_allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen config.no_reply_address=Versteckte E-Mail-Domain config.default_visibility_organization=Standard-Sichtbarkeit für neue Organisationen diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 7d43c83cc3..36b7518c62 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -3192,6 +3192,7 @@ config.default_keep_email_private=Να αποκρύπτονται οι διευ config.default_allow_create_organization=Να επιτρέπεται η δημιουργία οργανισμών από προεπιλογή config.enable_timetracking=Ενεργοποίηση καταγραφής χρόνου config.default_enable_timetracking=Ενεργοποίηση καταγραφής χρόνου από προεπιλογή +config.allow_dots_in_usernames = Επιτρέπει την χρήση τελείων σε ονόματα χρηστών. Δεν θα επηρεαστούν λογαριασμοί που ήδη υπάρχουν. config.default_allow_only_contributors_to_track_time=Να επιτρέπεται η καταγραφή χρόνου μόνο από συνεισφέροντες config.no_reply_address=Κρυφό email domain config.default_visibility_organization=Προεπιλεγμένη ορατότητα νέων οργανισμών diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 79f757ee66..86a7ebcf82 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3215,6 +3215,7 @@ config.default_keep_email_private = Hide email addresses by default config.default_allow_create_organization = Allow creation of organizations by default config.enable_timetracking = Enable time tracking config.default_enable_timetracking = Enable time tracking by default +config.allow_dots_in_usernames = Allow users to use dots in their usernames. Doesn't affect existing accounts. config.default_allow_only_contributors_to_track_time = Let only contributors track time config.no_reply_address = Hidden email domain config.default_visibility_organization = Default visibility of new organizations diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index 0f03a1b9ba..dfc842da19 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -303,6 +303,7 @@ smtp_from_invalid = La «Sendu retleterojn kiel» adreso malvalidas [admin] config.app_data_path = Programdatuja doseiervojo +config.allow_dots_in_usernames = Permesi ĉeeston de punktoj en uzantonomoj. Ne efikas je jamaj kontoj. [home] filter = Aliaj filtriloj diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 0b4f0bef33..3dbca87381 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -3107,6 +3107,7 @@ config.default_keep_email_private=Ocultar direcciones de correo electrónico por config.default_allow_create_organization=Permitir la creación de organizaciones por defecto config.enable_timetracking=Habilitar seguimiento de tiempo config.default_enable_timetracking=Habilitar seguimiento de tiempo por defecto +config.allow_dots_in_usernames = Permite utilizar puntos en los nombres de usuario. No tiene efecto sobre cuentas existentes. config.default_allow_only_contributors_to_track_time=Deje que solo los colaboradores hagan un seguimiento del tiempo config.no_reply_address=Dominio de correos electrónicos ocultos config.default_visibility_organization=Visibilidad por defecto para nuevas organizaciones diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 36c849bd7b..bdedee0e81 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -3229,6 +3229,7 @@ config.default_keep_email_private=Masquer les adresses courriel par défaut config.default_allow_create_organization=Autoriser la création d'organisations par défaut config.enable_timetracking=Activer le suivi du temps config.default_enable_timetracking=Activer le suivi de temps par défaut +config.allow_dots_in_usernames = Les points sont autorisés dans les noms d'utilisateurs. Sans effet sur les comptes existants. config.default_allow_only_contributors_to_track_time=Restreindre le suivi de temps aux contributeurs config.no_reply_address=Domaine pour les courriels cachés config.default_visibility_organization=Visibilité par défaut des nouvelles organisations diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 2d41edf59d..8811a12810 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -3145,6 +3145,7 @@ config.default_keep_email_private=Nascondi indirizzo email in modo predefinito config.default_allow_create_organization=Consenti la creazione di organizzazioni in modo predefinito config.enable_timetracking=Abilita il cronografo config.default_enable_timetracking=Attiva il cronografo di default +config.allow_dots_in_usernames = Consenti l'uso del punto nel nome utente. Non impatta gli account già esistenti. config.default_allow_only_contributors_to_track_time=Consenti soltanto ai contributori di utilizzare il cronografo config.no_reply_address=Dominio email nascosto config.default_visibility_organization=Visibilità predefinita per le nuove organizzazioni diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 1aa00440d9..7f3e1ded36 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -3184,6 +3184,7 @@ config.default_keep_email_private=デフォルトでメールアドレスを隠 config.default_allow_create_organization=デフォルトで組織の作成を許可 config.enable_timetracking=タイムトラッキング有効 config.default_enable_timetracking=デフォルトでタイムトラッキング有効 +config.allow_dots_in_usernames = ユーザー名にドットを使用できるようにします。既存のアカウントには影響しません。 config.default_allow_only_contributors_to_track_time=コントリビューターだけタイムトラッキングする config.no_reply_address=メールを隠すときのドメイン config.default_visibility_organization=新しい組織のデフォルトの表示設定 diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 95d370661d..e2a3dc694e 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -3096,6 +3096,7 @@ config.default_keep_email_private=Verberg standaard alle e-mailadressen config.default_allow_create_organization=Standaard toestaan om organisaties aan te maken config.enable_timetracking=Tijdregistratie inschakelen config.default_enable_timetracking=Tijdregistratie standaard inschakelen +config.allow_dots_in_usernames = Sta gebruikers toe om punten te gebruiken in hun gebruikersnaam. Heeft geen invloed op bestaande accounts. config.default_allow_only_contributors_to_track_time=Sta alleen bijdragers toe tijdregistratie te gebruiken config.no_reply_address=Verborgen e-maildomein config.default_visibility_organization=Standaard zichtbaarheid voor nieuwe organisaties diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index ade7c6c704..086cea74fa 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -3096,6 +3096,7 @@ config.default_keep_email_private=Ocultar endereços de e-mail por padrão config.default_allow_create_organization=Permitir a criação de organizações por padrão config.enable_timetracking=Habilitar Cronômetro config.default_enable_timetracking=Habilitar o Cronômetro por Padrão +config.allow_dots_in_usernames = Permitir pontos em nomes de usuário. Esta opção não afeta contas já existentes. config.default_allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo config.no_reply_address=Ocultar domínio de e-mail config.default_visibility_organization=Visibilidade padrão para novas organizações diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index e355d4c9c9..a6f861ba68 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -3189,6 +3189,7 @@ config.default_keep_email_private=Скрывать адреса эл. почты config.default_allow_create_organization=Разрешить создание организаций по умолчанию config.enable_timetracking=Отслеживание времени config.default_enable_timetracking=Включить отслеживание времени по умолчанию +config.allow_dots_in_usernames = Разрешить точки в логинах пользователей. Это не повлияет на уже созданные учётные записи. config.default_allow_only_contributors_to_track_time=Подсчитывать время могут только соавторы config.no_reply_address=Домен скрытых адресов почты config.default_visibility_organization=Видимость новых организаций по умолчанию diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index 01404dd0b4..d555f3fe19 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -267,6 +267,7 @@ config.git_config = Konfiguracija Git config.git_max_diff_line_characters = Največ različnih znakov na vrstico notices.view_detail_header = Podrobnosti obvestila config.log_config = Konfiguracija dnevnika +config.allow_dots_in_usernames = Uporabnikom dovolite uporabo pik v uporabniških imenih. Ne vpliva na obstoječe račune. [repo] migrate.github_token_desc = Tu lahko vstavite enega ali več žetonov, ločenih z vejico, da bo selitev hitrejša zaradi omejitve hitrosti GitHub API. OPOZORILO: Zloraba te funkcije lahko krši pravila ponudnika storitev in povzroči blokado računa. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index bc768afc6f..4ca2e70f21 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -3224,6 +3224,7 @@ config.default_keep_email_private=默认情况下隐藏电子邮件地址 config.default_allow_create_organization=默认情况下允许创建组织 config.enable_timetracking=启用时间跟踪 config.default_enable_timetracking=默认情况下启用时间跟踪 +config.allow_dots_in_usernames = 允许用户在用户名中使用英文句号。不影响已有的帐户。 config.default_allow_only_contributors_to_track_time=仅允许成员跟踪时间 config.no_reply_address=隐藏电子邮件域 config.default_visibility_organization=新组织的默认可见性 diff --git a/routers/api/actions/artifacts.go b/routers/api/actions/artifacts.go index d530e9cee5..2f8b3d0210 100644 --- a/routers/api/actions/artifacts.go +++ b/routers/api/actions/artifacts.go @@ -311,7 +311,7 @@ func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) { } artifactName := ctx.Req.URL.Query().Get("artifactName") if artifactName == "" { - log.Error("Error artifact name is empty") + log.Warn("Error artifact name is empty") ctx.Error(http.StatusBadRequest, "Error artifact name is empty") return } diff --git a/routers/api/actions/artifacts_utils.go b/routers/api/actions/artifacts_utils.go index aaf89ef40e..d2e7ccaea1 100644 --- a/routers/api/actions/artifacts_utils.go +++ b/routers/api/actions/artifacts_utils.go @@ -61,7 +61,7 @@ func validateArtifactHash(ctx *ArtifactContext, artifactName string) bool { if paramHash == artifactHash { return true } - log.Error("Invalid artifact hash: %s", paramHash) + log.Warn("Invalid artifact hash: %s", paramHash) ctx.Error(http.StatusBadRequest, "Invalid artifact hash") return false } diff --git a/routers/api/actions/artifactsv4.go b/routers/api/actions/artifactsv4.go index 8300989c75..5a251e2ef9 100644 --- a/routers/api/actions/artifactsv4.go +++ b/routers/api/actions/artifactsv4.go @@ -359,7 +359,7 @@ func (r *artifactV4Routes) finalizeArtifact(ctx *ArtifactContext) { checksum = req.Hash.Value } if err := mergeChunksForArtifact(ctx, chunks, r.fs, artifact, checksum); err != nil { - log.Error("Error merge chunks: %v", err) + log.Warn("Error merge chunks: %v", err) ctx.Error(http.StatusInternalServerError, "Error merge chunks") return } diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 27f0578db7..7571797593 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -49,7 +49,10 @@ var ( func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { // The maven client does not present the error message to the user. Log it for users with access to server logs. - if status == http.StatusBadRequest || status == http.StatusInternalServerError { + switch status { + case http.StatusBadRequest: + log.Warn(message) + case http.StatusInternalServerError: log.Error(message) } diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 59ebc74b89..6003f664a0 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -14,6 +14,7 @@ import ( "code.gitea.io/gitea/modules/activitypub" "code.gitea.io/gitea/modules/httplib" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" gitea_context "code.gitea.io/gitea/services/context" @@ -89,7 +90,8 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er func ReqHTTPSignature() func(ctx *gitea_context.APIContext) { return func(ctx *gitea_context.APIContext) { if authenticated, err := verifyHTTPSignatures(ctx); err != nil { - ctx.ServerError("verifyHttpSignatures", err) + log.Warn("verifyHttpSignatures failed: %v", err) + ctx.Error(http.StatusBadRequest, "reqSignature", "request signature verification failed") } else if !authenticated { ctx.Error(http.StatusForbidden, "reqSignature", "request signature verification failed") } diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 8d97e8a3f8..1744426ee8 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -10,7 +10,6 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" @@ -201,7 +200,6 @@ func ReadRepoNotifications(ctx *context.APIContext) { if !ctx.FormBool("all") { statuses := ctx.FormStrings("status-types") opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) - log.Error("%v", opts.Status) } nl, err := db.Find[activities_model.Notification](ctx, opts) if err != nil { diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 7c01226c95..99f2dd07dc 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -6,6 +6,7 @@ package actions import ( "bytes" "context" + "errors" "fmt" "slices" "strings" @@ -25,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" webhook_module "code.gitea.io/gitea/modules/webhook" "code.gitea.io/gitea/services/convert" @@ -190,6 +192,12 @@ func notify(ctx context.Context, input *notifyInput) error { baseRef := git.BranchPrefix + input.PullRequest.BaseBranch baseCommit, err := gitRepo.GetCommit(baseRef) if err != nil { + if prp, ok := input.Payload.(*api.PullRequestPayload); ok && errors.Is(err, util.ErrNotExist) { + // the baseBranch was deleted and the PR closed: the action can be skipped + if prp.Action == api.HookIssueClosed { + return nil + } + } return fmt.Errorf("gitRepo.GetCommit: %w", err) } baseWorkflows, _, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload) diff --git a/services/packages/packages.go b/services/packages/packages.go index 64b1ddd869..8f688a74f4 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -165,11 +165,15 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil { if err == packages_model.ErrDuplicatePackageVersion { versionCreated = false - } - if err != packages_model.ErrDuplicatePackageVersion || !allowDuplicate { + } else { log.Error("Error inserting package: %v", err) return nil, false, err } + + if !allowDuplicate { + // no need to log an error + return nil, false, err + } } if versionCreated { diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 514bcee8f1..cb63123463 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -65,7 +65,7 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, if status != nil { return status.State } - return structs.CommitStatusSuccess + return "" } return returnedStatus diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index 145fc7d53c..0bb738e2ad 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -123,7 +123,7 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep for i, repo := range repos { if results[i] == nil { results[i] = git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID]) - if results[i].State != "" { + if results[i] != nil { if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil { log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) } diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go index 5f5c146232..1ef152838d 100644 --- a/services/webhook/webhook_test.go +++ b/services/webhook/webhook_test.go @@ -28,10 +28,19 @@ func TestWebhook_GetSlackHook(t *testing.T) { }) } +func activateWebhook(t *testing.T, hookID int64) { + t.Helper() + updated, err := db.GetEngine(db.DefaultContext).ID(hookID).Cols("is_active").Update(webhook_model.Webhook{IsActive: true}) + assert.Equal(t, int64(1), updated) + assert.NoError(t, err) +} + func TestPrepareWebhooks(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + activateWebhook(t, 1) + hookTasks := []*webhook_model.HookTask{ {HookID: 1, EventType: webhook_module.HookEventPush}, } @@ -48,6 +57,8 @@ func TestPrepareWebhooksBranchFilterMatch(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + activateWebhook(t, 4) + hookTasks := []*webhook_model.HookTask{ {HookID: 4, EventType: webhook_module.HookEventPush}, } diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index df4fe95fdb..12681ab0f9 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -59,8 +59,10 @@ func TestMain(m *testing.M) { exitVal := m.Run() - testlogger.WriterCloser.Reset() - + if err := testlogger.WriterCloser.Reset(); err != nil { + fmt.Printf("testlogger.WriterCloser.Reset: %v\n", err) + os.Exit(1) + } if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil { fmt.Printf("util.RemoveAll: %v\n", err) os.Exit(1) diff --git a/tests/integration/api_activitypub_person_test.go b/tests/integration/api_activitypub_person_test.go index 42a2a09072..eb00d6031c 100644 --- a/tests/integration/api_activitypub_person_test.go +++ b/tests/integration/api_activitypub_person_test.go @@ -108,6 +108,6 @@ func TestActivityPubPersonInbox(t *testing.T) { // Unsigned request fails req := NewRequest(t, "POST", user2inboxurl) - MakeRequest(t, req, http.StatusInternalServerError) + MakeRequest(t, req, http.StatusBadRequest) }) } diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 0a2003447a..76b36a7349 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -145,7 +145,10 @@ func TestMain(m *testing.M) { // Instead, "No tests were found", last nonsense log is "According to the configuration, subsequent logs will not be printed to the console" exitCode := m.Run() - testlogger.WriterCloser.Reset() + if err := testlogger.WriterCloser.Reset(); err != nil { + fmt.Printf("testlogger.WriterCloser.Reset: %v\n", err) + os.Exit(1) + } if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil { fmt.Printf("util.RemoveAll: %v\n", err) diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index ee13a4bc57..e79d99b052 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -80,10 +80,22 @@ func testPullCleanUp(t *testing.T, session *TestSession, user, repo, pullnum str return resp } +func retrieveHookTasks(t *testing.T, hookID int64, activateWebhook bool) []*webhook.HookTask { + t.Helper() + if activateWebhook { + updated, err := db.GetEngine(db.DefaultContext).ID(hookID).Cols("is_active").Update(webhook.Webhook{IsActive: true}) + assert.Equal(t, int64(1), updated) + assert.NoError(t, err) + } + + hookTasks, err := webhook.HookTasks(db.DefaultContext, hookID, 1) + assert.NoError(t, err) + return hookTasks +} + func TestPullMerge(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(db.DefaultContext, 1, 1) // Retrieve previous hook number - assert.NoError(t, err) + hookTasks := retrieveHookTasks(t, 1, true) hookTasksLenBefore := len(hookTasks) session := loginUser(t, "user1") @@ -96,16 +108,14 @@ func TestPullMerge(t *testing.T) { assert.EqualValues(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false) - hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1) - assert.NoError(t, err) + hookTasks = retrieveHookTasks(t, 1, false) assert.Len(t, hookTasks, hookTasksLenBefore+1) }) } func TestPullRebase(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(db.DefaultContext, 1, 1) // Retrieve previous hook number - assert.NoError(t, err) + hookTasks := retrieveHookTasks(t, 1, true) hookTasksLenBefore := len(hookTasks) session := loginUser(t, "user1") @@ -118,16 +128,14 @@ func TestPullRebase(t *testing.T) { assert.EqualValues(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false) - hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1) - assert.NoError(t, err) + hookTasks = retrieveHookTasks(t, 1, false) assert.Len(t, hookTasks, hookTasksLenBefore+1) }) } func TestPullRebaseMerge(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(db.DefaultContext, 1, 1) // Retrieve previous hook number - assert.NoError(t, err) + hookTasks := retrieveHookTasks(t, 1, true) hookTasksLenBefore := len(hookTasks) session := loginUser(t, "user1") @@ -140,16 +148,14 @@ func TestPullRebaseMerge(t *testing.T) { assert.EqualValues(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebaseMerge, false) - hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1) - assert.NoError(t, err) + hookTasks = retrieveHookTasks(t, 1, false) assert.Len(t, hookTasks, hookTasksLenBefore+1) }) } func TestPullSquash(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { - hookTasks, err := webhook.HookTasks(db.DefaultContext, 1, 1) // Retrieve previous hook number - assert.NoError(t, err) + hookTasks := retrieveHookTasks(t, 1, true) hookTasksLenBefore := len(hookTasks) session := loginUser(t, "user1") @@ -163,8 +169,7 @@ func TestPullSquash(t *testing.T) { assert.EqualValues(t, "pulls", elem[3]) testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleSquash, false) - hookTasks, err = webhook.HookTasks(db.DefaultContext, 1, 1) - assert.NoError(t, err) + hookTasks = retrieveHookTasks(t, 1, false) assert.Len(t, hookTasks, hookTasksLenBefore+1) }) } diff --git a/web_src/js/components/DashboardRepoList.vue b/web_src/js/components/DashboardRepoList.vue index d6b7d6c96f..825803d939 100644 --- a/web_src/js/components/DashboardRepoList.vue +++ b/web_src/js/components/DashboardRepoList.vue @@ -251,9 +251,8 @@ const sfc = { this.repos = json.data.map((webSearchRepo) => { return { ...webSearchRepo.repository, - latest_commit_status_state: webSearchRepo.latest_commit_status.State, - locale_latest_commit_status_state: webSearchRepo.locale_latest_commit_status, - latest_commit_status_state_link: webSearchRepo.latest_commit_status.TargetURL + latest_commit_status: webSearchRepo.latest_commit_status, + locale_latest_commit_status_state: webSearchRepo.locale_latest_commit_status }; }); const count = response.headers.get('X-Total-Count'); @@ -416,9 +415,9 @@ export default sfc; // activate the IDE's Vue plugin - + - +