Skip to content

Commit a035dd2

Browse files
authored
Merge branch 'main' into lunny/refactor_review_request
2 parents 440f1ed + c5332fd commit a035dd2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+1177
-670
lines changed

.github/workflows/pull-db-tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ jobs:
7272
go-version-file: go.mod
7373
check-latest: true
7474
- run: make deps-backend
75-
- run: make backend
75+
- run: GOEXPERIMENT='' make backend
7676
env:
7777
TAGS: bindata gogit sqlite sqlite_unlock_notify
7878
- name: run migration tests
7979
run: make test-sqlite-migration
8080
- name: run tests
81-
run: make test-sqlite
81+
run: GOEXPERIMENT='' make test-sqlite
8282
timeout-minutes: 50
8383
env:
8484
TAGS: bindata gogit sqlite sqlite_unlock_notify
@@ -142,7 +142,7 @@ jobs:
142142
RACE_ENABLED: true
143143
GITHUB_READ_TOKEN: ${{ secrets.GITHUB_READ_TOKEN }}
144144
- name: unit-tests-gogit
145-
run: make unit-test-coverage test-check
145+
run: GOEXPERIMENT='' make unit-test-coverage test-check
146146
env:
147147
TAGS: bindata gogit
148148
RACE_ENABLED: true

Makefile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ DIST := dist
1818
DIST_DIRS := $(DIST)/binaries $(DIST)/release
1919
IMPORT := code.gitea.io/gitea
2020

21+
# By default use go's 1.25 experimental json v2 library when building
22+
# TODO: remove when no longer experimental
23+
export GOEXPERIMENT ?= jsonv2
24+
2125
GO ?= go
2226
SHASUM ?= shasum -a 256
2327
HAS_GO := $(shell hash $(GO) > /dev/null 2>&1 && echo yes)
@@ -766,7 +770,7 @@ generate-go: $(TAGS_PREREQ)
766770

767771
.PHONY: security-check
768772
security-check:
769-
go run $(GOVULNCHECK_PACKAGE) -show color ./...
773+
GOEXPERIMENT= go run $(GOVULNCHECK_PACKAGE) -show color ./...
770774

771775
$(EXECUTABLE): $(GO_SOURCES) $(TAGS_PREREQ)
772776
ifneq ($(and $(STATIC),$(findstring pam,$(TAGS))),)

go.mod

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ require (
1616
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
1717
gitea.com/go-chi/cache v0.2.1
1818
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
19-
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96
19+
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15
2020
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
2121
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
2222
github.com/42wim/httpsig v1.2.3
@@ -61,6 +61,7 @@ require (
6161
github.com/go-redsync/redsync/v4 v4.13.0
6262
github.com/go-sql-driver/mysql v1.9.3
6363
github.com/go-webauthn/webauthn v0.13.4
64+
github.com/goccy/go-json v0.10.5
6465
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
6566
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
6667
github.com/golang-jwt/jwt/v5 v5.3.0
@@ -75,7 +76,6 @@ require (
7576
github.com/huandu/xstrings v1.5.0
7677
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
7778
github.com/jhillyerd/enmime v1.3.0
78-
github.com/json-iterator/go v1.1.12
7979
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
8080
github.com/klauspost/compress v1.18.0
8181
github.com/klauspost/cpuid/v2 v2.3.0
@@ -200,7 +200,6 @@ require (
200200
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
201201
github.com/go-ini/ini v1.67.0 // indirect
202202
github.com/go-webauthn/x v0.1.24 // indirect
203-
github.com/goccy/go-json v0.10.5 // indirect
204203
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
205204
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
206205
github.com/golang-sql/sqlexp v0.1.0 // indirect
@@ -220,6 +219,7 @@ require (
220219
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
221220
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
222221
github.com/josharian/intern v1.0.0 // indirect
222+
github.com/json-iterator/go v1.1.12 // indirect
223223
github.com/kevinburke/ssh_config v1.4.0 // indirect
224224
github.com/klauspost/pgzip v1.2.6 // indirect
225225
github.com/libdns/libdns v1.1.1 // indirect
@@ -277,7 +277,7 @@ require (
277277
go.uber.org/zap v1.27.0 // indirect
278278
go.uber.org/zap/exp v0.3.0 // indirect
279279
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
280-
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
280+
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
281281
golang.org/x/mod v0.27.0 // indirect
282282
golang.org/x/time v0.12.0 // indirect
283283
golang.org/x/tools v0.36.0 // indirect

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ gitea.com/go-chi/cache v0.2.1 h1:bfAPkvXlbcZxPCpcmDVCWoHgiBSBmZN/QosnZvEC0+g=
4343
gitea.com/go-chi/cache v0.2.1/go.mod h1:Qic0HZ8hOHW62ETGbonpwz8WYypj9NieU9659wFUJ8Q=
4444
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098 h1:p2ki+WK0cIeNQuqjR98IP2KZQKRzJJiV7aTeMAFwaWo=
4545
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098/go.mod h1:LjzIOHlRemuUyO7WR12fmm18VZIlCAaOt9L3yKw40pk=
46-
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96 h1:IFDiMBObsP6CZIRaDLd54SR6zPYAffPXiXck5Xslu0Q=
47-
gitea.com/go-chi/session v0.0.0-20240316035857-16768d98ec96/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
46+
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15 h1:qFYmz05u/s9664o7+XEgrlHXSPQ4uHO8/ccZGUb1uxA=
47+
gitea.com/go-chi/session v0.0.0-20250926004215-636cadd82e15/go.mod h1:0iEpFKnwO5dG0aF98O4eq6FMsAiXkNBaDIlUOlq4BtM=
4848
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
4949
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
5050
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o=
@@ -848,8 +848,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
848848
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
849849
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
850850
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
851-
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
852-
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
851+
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0=
852+
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4=
853853
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
854854
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
855855
golang.org/x/image v0.30.0 h1:jD5RhkmVAnjqaCUXfbGBrn3lpxbknfN9w2UhHHU+5B4=

models/issues/pull.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,10 +416,6 @@ func (pr *PullRequest) GetGitHeadRefName() string {
416416
return fmt.Sprintf("%s%d/head", git.PullPrefix, pr.Index)
417417
}
418418

419-
func (pr *PullRequest) GetGitHeadBranchRefName() string {
420-
return fmt.Sprintf("%s%s", git.BranchPrefix, pr.HeadBranch)
421-
}
422-
423419
// GetReviewCommentsCount returns the number of review comments made on the diff of a PR review (not including comments on commits or issues in a PR)
424420
func (pr *PullRequest) GetReviewCommentsCount(ctx context.Context) int {
425421
opts := FindCommentsOptions{
@@ -645,9 +641,8 @@ func (pr *PullRequest) UpdateCols(ctx context.Context, cols ...string) error {
645641
}
646642

647643
// UpdateColsIfNotMerged updates specific fields of a pull request if it has not been merged
648-
func (pr *PullRequest) UpdateColsIfNotMerged(ctx context.Context, cols ...string) error {
649-
_, err := db.GetEngine(ctx).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
650-
return err
644+
func (pr *PullRequest) UpdateColsIfNotMerged(ctx context.Context, cols ...string) (int64, error) {
645+
return db.GetEngine(ctx).Where("id = ? AND has_merged = ?", pr.ID, false).Cols(cols...).Update(pr)
651646
}
652647

653648
// IsWorkInProgress determine if the Pull Request is a Work In Progress by its title

models/migrations/v1_12/v136.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ package v1_12
66
import (
77
"fmt"
88
"math"
9-
"path/filepath"
10-
"strings"
119
"time"
1210

13-
"code.gitea.io/gitea/modules/git"
11+
repo_model "code.gitea.io/gitea/models/repo"
12+
"code.gitea.io/gitea/modules/gitrepo"
1413
"code.gitea.io/gitea/modules/graceful"
1514
"code.gitea.io/gitea/modules/log"
1615
"code.gitea.io/gitea/modules/setting"
@@ -85,12 +84,9 @@ func AddCommitDivergenceToPulls(x *xorm.Engine) error {
8584
log.Error("Missing base repo with id %d for PR ID %d", pr.BaseRepoID, pr.ID)
8685
continue
8786
}
88-
userPath := filepath.Join(setting.RepoRootPath, strings.ToLower(baseRepo.OwnerName))
89-
repoPath := filepath.Join(userPath, strings.ToLower(baseRepo.Name)+".git")
90-
87+
repoStore := repo_model.StorageRepo(repo_model.RelativePath(baseRepo.OwnerName, baseRepo.Name))
9188
gitRefName := fmt.Sprintf("refs/pull/%d/head", pr.Index)
92-
93-
divergence, err := git.GetDivergingCommits(graceful.GetManager().HammerContext(), repoPath, pr.BaseBranch, gitRefName)
89+
divergence, err := gitrepo.GetDivergingCommits(graceful.GetManager().HammerContext(), repoStore, pr.BaseBranch, gitRefName)
9490
if err != nil {
9591
log.Warn("Could not recalculate Divergence for pull: %d", pr.ID)
9692
pr.CommitsAhead = 0

modules/actions/workflows.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -377,20 +377,28 @@ func matchIssuesEvent(issuePayload *api.IssuePayload, evt *jobparser.Event) bool
377377
// Actions with the same name:
378378
// opened, edited, closed, reopened, assigned, unassigned, milestoned, demilestoned
379379
// Actions need to be converted:
380-
// label_updated -> labeled
380+
// label_updated -> labeled (when adding) or unlabeled (when removing)
381381
// label_cleared -> unlabeled
382382
// Unsupported activity types:
383383
// deleted, transferred, pinned, unpinned, locked, unlocked
384384

385-
action := issuePayload.Action
386-
switch action {
385+
actions := []string{}
386+
switch issuePayload.Action {
387387
case api.HookIssueLabelUpdated:
388-
action = "labeled"
388+
if len(issuePayload.Changes.AddedLabels) > 0 {
389+
actions = append(actions, "labeled")
390+
}
391+
if len(issuePayload.Changes.RemovedLabels) > 0 {
392+
actions = append(actions, "unlabeled")
393+
}
389394
case api.HookIssueLabelCleared:
390-
action = "unlabeled"
395+
actions = append(actions, "unlabeled")
396+
default:
397+
actions = append(actions, string(issuePayload.Action))
391398
}
399+
392400
for _, val := range vals {
393-
if glob.MustCompile(val, '/').Match(string(action)) {
401+
if slices.ContainsFunc(actions, glob.MustCompile(val, '/').Match) {
394402
matchTimes++
395403
break
396404
}

modules/actions/workflows_test.go

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,184 @@ func TestDetectMatched(t *testing.T) {
154154
})
155155
}
156156
}
157+
158+
func TestMatchIssuesEvent(t *testing.T) {
159+
testCases := []struct {
160+
desc string
161+
payload *api.IssuePayload
162+
yamlOn string
163+
expected bool
164+
eventType string
165+
}{
166+
{
167+
desc: "Label deletion should trigger unlabeled event",
168+
payload: &api.IssuePayload{
169+
Action: api.HookIssueLabelUpdated,
170+
Issue: &api.Issue{
171+
Labels: []*api.Label{},
172+
},
173+
Changes: &api.ChangesPayload{
174+
RemovedLabels: []*api.Label{
175+
{ID: 123, Name: "deleted-label"},
176+
},
177+
},
178+
},
179+
yamlOn: "on:\n issues:\n types: [unlabeled]",
180+
expected: true,
181+
eventType: "unlabeled",
182+
},
183+
{
184+
desc: "Label deletion with existing labels should trigger unlabeled event",
185+
payload: &api.IssuePayload{
186+
Action: api.HookIssueLabelUpdated,
187+
Issue: &api.Issue{
188+
Labels: []*api.Label{
189+
{ID: 456, Name: "existing-label"},
190+
},
191+
},
192+
Changes: &api.ChangesPayload{
193+
AddedLabels: nil,
194+
RemovedLabels: []*api.Label{
195+
{ID: 123, Name: "deleted-label"},
196+
},
197+
},
198+
},
199+
yamlOn: "on:\n issues:\n types: [unlabeled]",
200+
expected: true,
201+
eventType: "unlabeled",
202+
},
203+
{
204+
desc: "Label addition should trigger labeled event",
205+
payload: &api.IssuePayload{
206+
Action: api.HookIssueLabelUpdated,
207+
Issue: &api.Issue{
208+
Labels: []*api.Label{
209+
{ID: 123, Name: "new-label"},
210+
},
211+
},
212+
Changes: &api.ChangesPayload{
213+
AddedLabels: []*api.Label{
214+
{ID: 123, Name: "new-label"},
215+
},
216+
RemovedLabels: []*api.Label{}, // Empty array, no labels removed
217+
},
218+
},
219+
yamlOn: "on:\n issues:\n types: [labeled]",
220+
expected: true,
221+
eventType: "labeled",
222+
},
223+
{
224+
desc: "Label clear should trigger unlabeled event",
225+
payload: &api.IssuePayload{
226+
Action: api.HookIssueLabelCleared,
227+
Issue: &api.Issue{
228+
Labels: []*api.Label{},
229+
},
230+
},
231+
yamlOn: "on:\n issues:\n types: [unlabeled]",
232+
expected: true,
233+
eventType: "unlabeled",
234+
},
235+
{
236+
desc: "Both adding and removing labels should trigger labeled event",
237+
payload: &api.IssuePayload{
238+
Action: api.HookIssueLabelUpdated,
239+
Issue: &api.Issue{
240+
Labels: []*api.Label{
241+
{ID: 789, Name: "new-label"},
242+
},
243+
},
244+
Changes: &api.ChangesPayload{
245+
AddedLabels: []*api.Label{
246+
{ID: 789, Name: "new-label"},
247+
},
248+
RemovedLabels: []*api.Label{
249+
{ID: 123, Name: "deleted-label"},
250+
},
251+
},
252+
},
253+
yamlOn: "on:\n issues:\n types: [labeled]",
254+
expected: true,
255+
eventType: "labeled",
256+
},
257+
{
258+
desc: "Both adding and removing labels should trigger unlabeled event",
259+
payload: &api.IssuePayload{
260+
Action: api.HookIssueLabelUpdated,
261+
Issue: &api.Issue{
262+
Labels: []*api.Label{
263+
{ID: 789, Name: "new-label"},
264+
},
265+
},
266+
Changes: &api.ChangesPayload{
267+
AddedLabels: []*api.Label{
268+
{ID: 789, Name: "new-label"},
269+
},
270+
RemovedLabels: []*api.Label{
271+
{ID: 123, Name: "deleted-label"},
272+
},
273+
},
274+
},
275+
yamlOn: "on:\n issues:\n types: [unlabeled]",
276+
expected: true,
277+
eventType: "unlabeled",
278+
},
279+
{
280+
desc: "Both adding and removing labels should trigger both events",
281+
payload: &api.IssuePayload{
282+
Action: api.HookIssueLabelUpdated,
283+
Issue: &api.Issue{
284+
Labels: []*api.Label{
285+
{ID: 789, Name: "new-label"},
286+
},
287+
},
288+
Changes: &api.ChangesPayload{
289+
AddedLabels: []*api.Label{
290+
{ID: 789, Name: "new-label"},
291+
},
292+
RemovedLabels: []*api.Label{
293+
{ID: 123, Name: "deleted-label"},
294+
},
295+
},
296+
},
297+
yamlOn: "on:\n issues:\n types: [labeled, unlabeled]",
298+
expected: true,
299+
eventType: "multiple",
300+
},
301+
}
302+
303+
for _, tc := range testCases {
304+
t.Run(tc.desc, func(t *testing.T) {
305+
evts, err := GetEventsFromContent([]byte(tc.yamlOn))
306+
assert.NoError(t, err)
307+
assert.Len(t, evts, 1)
308+
309+
// Test if the event matches as expected
310+
assert.Equal(t, tc.expected, matchIssuesEvent(tc.payload, evts[0]))
311+
312+
// For extra validation, check that action mapping works correctly
313+
if tc.eventType == "multiple" {
314+
// Skip direct action mapping validation for multiple events case
315+
// as one action can map to multiple event types
316+
return
317+
}
318+
319+
// Determine expected action for single event case
320+
var expectedAction string
321+
switch tc.payload.Action {
322+
case api.HookIssueLabelUpdated:
323+
if tc.eventType == "labeled" {
324+
expectedAction = "labeled"
325+
} else if tc.eventType == "unlabeled" {
326+
expectedAction = "unlabeled"
327+
}
328+
case api.HookIssueLabelCleared:
329+
expectedAction = "unlabeled"
330+
default:
331+
expectedAction = string(tc.payload.Action)
332+
}
333+
334+
assert.Equal(t, expectedAction, tc.eventType, "Event type should match expected")
335+
})
336+
}
337+
}

0 commit comments

Comments
 (0)