diff --git a/.github/workflows/pull-db-tests.yml b/.github/workflows/pull-db-tests.yml index 55c2d2bf5e30e..faf6a90e1bc5b 100644 --- a/.github/workflows/pull-db-tests.yml +++ b/.github/workflows/pull-db-tests.yml @@ -31,7 +31,7 @@ jobs: minio: # as github actions doesn't support "entrypoint", we need to use a non-official image # that has a custom entrypoint set to "minio server /data" - image: bitnami/minio:2023.8.31 + image: bitnamilegacy/minio:2023.8.31 env: MINIO_ROOT_USER: 123456 MINIO_ROOT_PASSWORD: 12345678 @@ -113,7 +113,7 @@ jobs: ports: - 6379:6379 minio: - image: bitnami/minio:2021.3.17 + image: bitnamilegacy/minio:2021.3.17 env: MINIO_ACCESS_KEY: 123456 MINIO_SECRET_KEY: 12345678 @@ -155,7 +155,7 @@ jobs: services: mysql: # the bitnami mysql image has more options than the official one, it's easier to customize - image: bitnami/mysql:8.0 + image: bitnamilegacy/mysql:8.0 env: ALLOW_EMPTY_PASSWORD: true MYSQL_DATABASE: testgitea diff --git a/CHANGELOG.md b/CHANGELOG.md index 0be6fa2d21dae..12d98da14fab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ This changelog goes through 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.com). +## [1.24.6](https://github.com/go-gitea/gitea/releases/tag/1.24.6) - 2025-09-10 + +* SECURITY + * Upgrade xz to v0.5.15 (#35385) +* BUGFIXES + * Fix a compare page 404 bug when the pull request disabled (#35441) (#35453) + * Fix bug when issue disabled, pull request number in the commit message cannot be redirected (#35420) (#35442) + * Add author.name field to Swift Package Registry API response (#35410) (#35431) + * Remove usernames when empty in discord webhook (#35412) (#35417) + * Allow foreachref parser to grow its buffer (#35365) (#35376) + * Allow deleting comment with content via API like web did (#35346) (#35354) + * Fix atom/rss mixed error (#35345) (#35347) + * Fix review request webhook bug (#35339) + * Remove duplicate html IDs (#35210) (#35325) + * Fix LFS range size header response (#35277) (#35293) + * Fix GitHub release assets URL validation (#35287) (#35290) + * Fix token lifetime, closes #35230 (#35271) (#35281) + * Fix push commits comments when changing the pull request target branch (#35386) (#35443) + ## [1.24.5](https://github.com/go-gitea/gitea/releases/tag/v1.24.5) - 2025-08-12 * BUGFIXES diff --git a/go.mod b/go.mod index 9b0421e1b65ac..bfeb81f26c8be 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 github.com/tstranex/u2f v1.0.0 - github.com/ulikunitz/xz v0.5.12 + github.com/ulikunitz/xz v0.5.15 github.com/urfave/cli/v2 v2.27.6 github.com/wneessen/go-mail v0.6.2 github.com/xeipuuv/gojsonschema v1.2.0 diff --git a/go.sum b/go.sum index 3b7364af8d4aa..4776a00e27d5f 100644 --- a/go.sum +++ b/go.sum @@ -757,8 +757,8 @@ github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGB github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= -github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs= github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM= github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= diff --git a/models/repo/collaboration_test.go b/models/repo/collaboration_test.go index 7b07dbffdf01b..1e0c84092bd88 100644 --- a/models/repo/collaboration_test.go +++ b/models/repo/collaboration_test.go @@ -85,8 +85,8 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) { assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin)) - // Disvard invalid input. - assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessMode(unittest.NonexistentID))) + // Discard invalid input. + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessMode(-1))) unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID}) } diff --git a/modules/git/foreachref/parser.go b/modules/git/foreachref/parser.go index de69eaa2c894d..ebdc7344d0ca3 100644 --- a/modules/git/foreachref/parser.go +++ b/modules/git/foreachref/parser.go @@ -30,6 +30,10 @@ type Parser struct { func NewParser(r io.Reader, format Format) *Parser { scanner := bufio.NewScanner(r) + // default MaxScanTokenSize = 64 kiB may be too small for some references, + // so allow the buffer to grow up to 4x if needed + scanner.Buffer(nil, 4*bufio.MaxScanTokenSize) + // in addition to the reference delimiter we specified in the --format, // `git for-each-ref` will always add a newline after every reference. refDelim := make([]byte, 0, len(format.refDelim)+1) @@ -70,6 +74,9 @@ func NewParser(r io.Reader, format Format) *Parser { // { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" } func (p *Parser) Next() map[string]string { if !p.scanner.Scan() { + if err := p.scanner.Err(); err != nil { + p.err = err + } return nil } fields, err := p.parseRef(p.scanner.Text()) diff --git a/modules/packages/swift/metadata.go b/modules/packages/swift/metadata.go index 24c4262ab7248..959c5292e4118 100644 --- a/modules/packages/swift/metadata.go +++ b/modules/packages/swift/metadata.go @@ -82,6 +82,7 @@ type ProgrammingLanguage struct { // https://schema.org/Person type Person struct { Type string `json:"@type,omitempty"` + Name string `json:"name,omitempty"` // inherited from https://schema.org/Thing GivenName string `json:"givenName,omitempty"` MiddleName string `json:"middleName,omitempty"` FamilyName string `json:"familyName,omitempty"` @@ -184,11 +185,17 @@ func ParsePackage(sr io.ReaderAt, size int64, mr io.Reader) (*Package, error) { p.Metadata.Description = ssc.Description p.Metadata.Keywords = ssc.Keywords p.Metadata.License = ssc.License - p.Metadata.Author = Person{ + author := Person{ + Name: ssc.Author.Name, GivenName: ssc.Author.GivenName, MiddleName: ssc.Author.MiddleName, FamilyName: ssc.Author.FamilyName, } + // If Name is not provided, generate it from individual name components + if author.Name == "" { + author.Name = author.String() + } + p.Metadata.Author = author p.Metadata.RepositoryURL = ssc.CodeRepository if !validation.IsValidURL(p.Metadata.RepositoryURL) { diff --git a/modules/packages/swift/metadata_test.go b/modules/packages/swift/metadata_test.go index 3913c2355ba21..461773cbfce07 100644 --- a/modules/packages/swift/metadata_test.go +++ b/modules/packages/swift/metadata_test.go @@ -97,10 +97,49 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, packageDescription, p.Metadata.Description) assert.ElementsMatch(t, []string{"swift", "package"}, p.Metadata.Keywords) assert.Equal(t, packageLicense, p.Metadata.License) + assert.Equal(t, packageAuthor, p.Metadata.Author.Name) assert.Equal(t, packageAuthor, p.Metadata.Author.GivenName) assert.Equal(t, packageRepositoryURL, p.Metadata.RepositoryURL) assert.ElementsMatch(t, []string{packageRepositoryURL}, p.RepositoryURLs) }) + + t.Run("WithExplicitNameField", func(t *testing.T) { + data := createArchive(map[string][]byte{ + "Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"), + }) + + authorName := "John Doe" + p, err := ParsePackage( + data, + data.Size(), + strings.NewReader(`{"name":"`+packageName+`","version":"`+packageVersion+`","description":"`+packageDescription+`","author":{"name":"`+authorName+`","givenName":"John","familyName":"Doe"}}`), + ) + assert.NotNil(t, p) + assert.NoError(t, err) + + assert.Equal(t, authorName, p.Metadata.Author.Name) + assert.Equal(t, "John", p.Metadata.Author.GivenName) + assert.Equal(t, "Doe", p.Metadata.Author.FamilyName) + }) + + t.Run("NameFieldGeneration", func(t *testing.T) { + data := createArchive(map[string][]byte{ + "Package.swift": []byte("// swift-tools-version:5.7\n//\n// Package.swift"), + }) + + // Test with only individual name components - Name should be auto-generated + p, err := ParsePackage( + data, + data.Size(), + strings.NewReader(`{"author":{"givenName":"John","middleName":"Q","familyName":"Doe"}}`), + ) + assert.NotNil(t, p) + assert.NoError(t, err) + assert.Equal(t, "John Q Doe", p.Metadata.Author.Name) + assert.Equal(t, "John", p.Metadata.Author.GivenName) + assert.Equal(t, "Q", p.Metadata.Author.MiddleName) + assert.Equal(t, "Doe", p.Metadata.Author.FamilyName) + }) } func TestTrimmedVersionString(t *testing.T) { @@ -142,3 +181,43 @@ func TestTrimmedVersionString(t *testing.T) { assert.Equal(t, c.Expected, TrimmedVersionString(c.Version)) } } + +func TestPersonNameString(t *testing.T) { + cases := []struct { + Name string + Person Person + Expected string + }{ + { + Name: "GivenNameOnly", + Person: Person{GivenName: "John"}, + Expected: "John", + }, + { + Name: "GivenAndFamily", + Person: Person{GivenName: "John", FamilyName: "Doe"}, + Expected: "John Doe", + }, + { + Name: "FullName", + Person: Person{GivenName: "John", MiddleName: "Q", FamilyName: "Doe"}, + Expected: "John Q Doe", + }, + { + Name: "MiddleAndFamily", + Person: Person{MiddleName: "Q", FamilyName: "Doe"}, + Expected: "Q Doe", + }, + { + Name: "Empty", + Person: Person{}, + Expected: "", + }, + } + + for _, c := range cases { + t.Run(c.Name, func(t *testing.T) { + assert.Equal(t, c.Expected, c.Person.String()) + }) + } +} diff --git a/modules/templates/util_format_test.go b/modules/templates/util_format_test.go index 13a57c24e26a2..89e42532f96fd 100644 --- a/modules/templates/util_format_test.go +++ b/modules/templates/util_format_test.go @@ -13,6 +13,6 @@ func TestCountFmt(t *testing.T) { assert.Equal(t, "125", countFmt(125)) assert.Equal(t, "1.3k", countFmt(int64(1317))) assert.Equal(t, "21.3M", countFmt(21317675)) - assert.Equal(t, "45.7G", countFmt(45721317675)) + assert.Equal(t, "45.7G", countFmt(int64(45721317675))) assert.Empty(t, countFmt("test")) } diff --git a/package-lock.json b/package-lock.json index 1ab429628ec53..bdf8b1e17a2c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,7 @@ "jquery": "3.7.1", "katex": "0.16.22", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "11.6.0", + "mermaid": "11.10.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.1", "monaco-editor": "0.52.2", @@ -1540,9 +1540,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", - "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz", + "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -6154,9 +6154,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", - "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -9249,14 +9249,14 @@ } }, "node_modules/mermaid": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", - "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.10.0.tgz", + "integrity": "sha512-oQsFzPBy9xlpnGxUqLbVY8pvknLlsNIJ0NWwi8SUJjhbP1IT0E0o1lfhU4iYV3ubpy+xkzkaOyDUQMn06vQElQ==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.4.0", + "@mermaid-js/parser": "^0.6.2", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", @@ -9265,11 +9265,11 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.13", - "dompurify": "^3.2.4", - "katex": "^0.16.9", + "dompurify": "^3.2.5", + "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", - "marked": "^15.0.7", + "marked": "^16.0.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", @@ -9277,15 +9277,15 @@ } }, "node_modules/mermaid/node_modules/marked": { - "version": "15.0.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz", - "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.0.tgz", + "integrity": "sha512-LbbTuye+0dWRz2TS9KJ7wsnD4KAtpj0MVkWc90XvBa6AslXsT0hTBVH5k32pcSyHH1fst9XEFJunXHktVy0zlg==", "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/micromark": { diff --git a/package.json b/package.json index 4874ad450dce8..cd32beb5b21f2 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "jquery": "3.7.1", "katex": "0.16.22", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "11.6.0", + "mermaid": "11.10.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.1", "monaco-editor": "0.52.2", diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index 47439c4c3bb7b..af7bf4e5a6965 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -230,6 +230,7 @@ func PackageVersionMetadata(ctx *context.Context) { }, Author: swift_module.Person{ Type: "Person", + Name: metadata.Author.String(), GivenName: metadata.Author.GivenName, MiddleName: metadata.Author.MiddleName, FamilyName: metadata.Author.FamilyName, diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index cc342a9313c71..4db1e878b13ca 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -721,8 +721,8 @@ func deleteIssueComment(ctx *context.APIContext) { if !ctx.IsSigned || (ctx.Doer.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) { ctx.Status(http.StatusForbidden) return - } else if comment.Type != issues_model.CommentTypeComment { - ctx.Status(http.StatusNoContent) + } else if !comment.Type.HasContentSupport() { + ctx.Status(http.StatusBadRequest) return } diff --git a/routers/web/feed/render.go b/routers/web/feed/render.go index d1a229e5b2723..014da253bdfbf 100644 --- a/routers/web/feed/render.go +++ b/routers/web/feed/render.go @@ -8,11 +8,18 @@ import ( ) // RenderBranchFeed render format for branch or file -func RenderBranchFeed(ctx *context.Context) { - _, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req) +func RenderBranchFeed(ctx *context.Context, feedType string) { if ctx.Repo.TreePath == "" { - ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType) + ShowBranchFeed(ctx, ctx.Repo.Repository, feedType) } else { - ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType) + ShowFileFeed(ctx, ctx.Repo.Repository, feedType) } } + +func RenderBranchFeedRSS(ctx *context.Context) { + RenderBranchFeed(ctx, "rss") +} + +func RenderBranchFeedAtom(ctx *context.Context) { + RenderBranchFeed(ctx, "atom") +} diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index c771b30e5ff85..c7c048d5169a2 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -523,7 +523,7 @@ func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { // Treat as pull request if both references are branches if ctx.Data["PageIsComparePull"] == nil { - ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch + ctx.Data["PageIsComparePull"] = headIsBranch && baseIsBranch && permBase.CanReadIssuesOrPulls(true) } if ctx.Data["PageIsComparePull"] == true && !permBase.CanReadIssuesOrPulls(true) { @@ -735,6 +735,7 @@ func CompareDiff(ctx *context.Context) { return } + ctx.Data["PageIsViewCode"] = true ctx.Data["PullRequestWorkInProgressPrefixes"] = setting.Repository.PullRequest.WorkInProgressPrefixes ctx.Data["DirectComparison"] = ci.DirectComparison ctx.Data["OtherCompareSeparator"] = ".." diff --git a/routers/web/web.go b/routers/web/web.go index 2232ec1f5b6a4..7b9c3e6378b2a 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1217,10 +1217,11 @@ func registerWebRoutes(m *web.Router) { // end "/{username}/{reponame}": view milestone, label, issue, pull, etc m.Group("/{username}/{reponame}/{type:issues}", func() { + // these handlers also check unit permissions internally m.Get("", repo.Issues) - m.Get("/{index}", repo.ViewIssue) - }, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypeExternalTracker)) - // end "/{username}/{reponame}": issue/pull list, issue/pull view, external tracker + m.Get("/{index}", repo.ViewIssue) // also do pull-request redirection (".../issues/{PR-number}" -> ".../pulls/{PR-number}") + }, optSignIn, context.RepoAssignment, context.RequireUnitReader(unit.TypeIssues, unit.TypePullRequests, unit.TypeExternalTracker)) + // end "/{username}/{reponame}": issue list, issue view (pull-request redirection), external tracker m.Group("/{username}/{reponame}", func() { // edit issues, pulls, labels, milestones, etc m.Group("/issues", func() { @@ -1592,8 +1593,8 @@ func registerWebRoutes(m *web.Router) { m.Get("/cherry-pick/{sha:([a-f0-9]{7,64})$}", repo.SetEditorconfigIfExists, context.RepoRefByDefaultBranch(), repo.CherryPick) }, repo.MustBeNotEmpty) - m.Get("/rss/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed) - m.Get("/atom/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeed) + m.Get("/rss/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeedRSS) + m.Get("/atom/branch/*", context.RepoRefByType(git.RefTypeBranch), feedEnabled, feed.RenderBranchFeedAtom) m.Group("/src", func() { m.Get("", func(ctx *context.Context) { ctx.Redirect(ctx.Repo.RepoLink) }) // there is no "{owner}/{repo}/src" page, so redirect to "{owner}/{repo}" to avoid 404 diff --git a/services/actions/auth.go b/services/actions/auth.go index 12a8fba53f44a..c742e19c601ee 100644 --- a/services/actions/auth.go +++ b/services/actions/auth.go @@ -53,7 +53,7 @@ func CreateAuthorizationToken(taskID, runID, jobID int64) (string, error) { claims := actionsClaims{ RegisteredClaims: jwt.RegisteredClaims{ - ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)), + ExpiresAt: jwt.NewNumericDate(now.Add(1*time.Hour + setting.Actions.EndlessTaskTimeout)), NotBefore: jwt.NewNumericDate(now), }, Scp: fmt.Sprintf("Actions.Results:%d:%d", runID, jobID), diff --git a/services/agit/agit.go b/services/agit/agit.go index 0ea8bbfa5dc66..6bfb9ec3bcd3b 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -250,7 +250,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. if err != nil { return nil, fmt.Errorf("failed to load pull issue. Error: %w", err) } - comment, err := pull_service.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i]) + comment, err := pull_service.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i], forcePush.Value()) if err == nil && comment != nil { notify_service.PullRequestPushCommits(ctx, pusher, pr, comment) } diff --git a/services/lfs/server.go b/services/lfs/server.go index 0a99287ed9cd5..46d3f94d39179 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -111,7 +111,7 @@ func DownloadHandler(ctx *context.Context) { } } - ctx.Resp.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", fromByte, toByte, meta.Size-fromByte)) + ctx.Resp.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", fromByte, toByte, meta.Size)) ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Range") } } diff --git a/services/migrations/github.go b/services/migrations/github.go index d0a0d719869c3..199ed38ddfeff 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -354,7 +354,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(ctx context.Context, rel *gith // Prevent open redirect if !hasBaseURL(redirectURL, g.baseURL) && - !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") { + !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") && + !hasBaseURL(redirectURL, "https://release-assets.githubusercontent.com/") { WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL) return io.NopCloser(strings.NewReader(redirectURL)), nil diff --git a/services/pull/comment.go b/services/pull/comment.go index 53587d4f542be..f12edaf0326b0 100644 --- a/services/pull/comment.go +++ b/services/pull/comment.go @@ -14,42 +14,28 @@ import ( ) // getCommitIDsFromRepo get commit IDs from repo in between oldCommitID and newCommitID -// isForcePush will be true if oldCommit isn't on the branch // Commit on baseBranch will skip -func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, isForcePush bool, err error) { +func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, err error) { gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) if err != nil { - return nil, false, err + return nil, err } defer closer.Close() oldCommit, err := gitRepo.GetCommit(oldCommitID) if err != nil { - return nil, false, err + return nil, err } newCommit, err := gitRepo.GetCommit(newCommitID) if err != nil { - return nil, false, err - } - - isForcePush, err = newCommit.IsForcePush(oldCommitID) - if err != nil { - return nil, false, err - } - - if isForcePush { - commitIDs = make([]string, 2) - commitIDs[0] = oldCommitID - commitIDs[1] = newCommitID - - return commitIDs, isForcePush, err + return nil, err } // Find commits between new and old commit excluding base branch commits commits, err := gitRepo.CommitsBetweenNotBase(newCommit, oldCommit, baseBranch) if err != nil { - return nil, false, err + return nil, err } commitIDs = make([]string, 0, len(commits)) @@ -57,38 +43,40 @@ func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldC commitIDs = append(commitIDs, commits[i].ID.String()) } - return commitIDs, isForcePush, err + return commitIDs, err } // CreatePushPullComment create push code to pull base comment -func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (comment *issues_model.Comment, err error) { +func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *issues_model.PullRequest, oldCommitID, newCommitID string, isForcePush bool) (comment *issues_model.Comment, err error) { if pr.HasMerged || oldCommitID == "" || newCommitID == "" { return nil, nil } - ops := &issues_model.CreateCommentOptions{ - Type: issues_model.CommentTypePullRequestPush, - Doer: pusher, - Repo: pr.BaseRepo, + opts := &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypePullRequestPush, + Doer: pusher, + Repo: pr.BaseRepo, + IsForcePush: isForcePush, + Issue: pr.Issue, } var data issues_model.PushActionContent - - data.CommitIDs, data.IsForcePush, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch) - if err != nil { - return nil, err + if opts.IsForcePush { + data.CommitIDs = []string{oldCommitID, newCommitID} + } else { + data.CommitIDs, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch) + if err != nil { + return nil, err + } } - ops.Issue = pr.Issue - dataJSON, err := json.Marshal(data) if err != nil { return nil, err } - ops.Content = string(dataJSON) - - comment, err = issues_model.CreateComment(ctx, ops) + opts.Content = string(dataJSON) + comment, err = issues_model.CreateComment(ctx, opts) return comment, err } diff --git a/services/pull/pull.go b/services/pull/pull.go index 81be797832aa4..53c834afe0fbd 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -28,7 +28,6 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/globallock" "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -142,36 +141,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { return err } - compareInfo, err := baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), - git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false, false) - if err != nil { - return err - } - if len(compareInfo.Commits) == 0 { - return nil - } - - data := issues_model.PushActionContent{IsForcePush: false} - data.CommitIDs = make([]string, 0, len(compareInfo.Commits)) - for i := len(compareInfo.Commits) - 1; i >= 0; i-- { - data.CommitIDs = append(data.CommitIDs, compareInfo.Commits[i].ID.String()) - } - - dataJSON, err := json.Marshal(data) - if err != nil { - return err - } - - ops := &issues_model.CreateCommentOptions{ - Type: issues_model.CommentTypePullRequestPush, - Doer: issue.Poster, - Repo: repo, - Issue: pr.Issue, - IsForcePush: false, - Content: string(dataJSON), - } - - if _, err = issues_model.CreateComment(ctx, ops); err != nil { + if _, err := CreatePushPullComment(ctx, issue.Poster, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false); err != nil { return err } @@ -193,6 +163,20 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers) + // Request reviews, these should be requested before other notifications because they will add request reviews record + // on database + permDoer, err := access_model.GetUserRepoPermission(ctx, repo, issue.Poster) + for _, reviewer := range opts.Reviewers { + if _, err = issue_service.ReviewRequest(ctx, pr.Issue, issue.Poster, &permDoer, reviewer, true); err != nil { + return err + } + } + for _, teamReviewer := range opts.TeamReviewers { + if _, err = issue_service.TeamReviewRequest(ctx, pr.Issue, issue.Poster, teamReviewer, true); err != nil { + return err + } + } + mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content) if err != nil { return err @@ -211,17 +195,7 @@ func NewPullRequest(ctx context.Context, opts *NewPullRequestOptions) error { } notify_service.IssueChangeAssignee(ctx, issue.Poster, issue, assignee, false, assigneeCommentMap[assigneeID]) } - permDoer, err := access_model.GetUserRepoPermission(ctx, repo, issue.Poster) - for _, reviewer := range opts.Reviewers { - if _, err = issue_service.ReviewRequest(ctx, pr.Issue, issue.Poster, &permDoer, reviewer, true); err != nil { - return err - } - } - for _, teamReviewer := range opts.TeamReviewers { - if _, err = issue_service.TeamReviewRequest(ctx, pr.Issue, issue.Poster, teamReviewer, true); err != nil { - return err - } - } + return nil } @@ -332,24 +306,42 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer pr.CommitsAhead = divergence.Ahead pr.CommitsBehind = divergence.Behind - if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files", "base_branch", "commits_ahead", "commits_behind"); err != nil { + // add first push codes comment + baseGitRepo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) + if err != nil { return err } + defer baseGitRepo.Close() - // Create comment - options := &issues_model.CreateCommentOptions{ - Type: issues_model.CommentTypeChangeTargetBranch, - Doer: doer, - Repo: pr.Issue.Repo, - Issue: pr.Issue, - OldRef: oldBranch, - NewRef: targetBranch, - } - if _, err = issues_model.CreateComment(ctx, options); err != nil { - return fmt.Errorf("CreateChangeTargetBranchComment: %w", err) - } + return db.WithTx(ctx, func(ctx context.Context) error { + if err := pr.UpdateColsIfNotMerged(ctx, "merge_base", "status", "conflicted_files", "changed_protected_files", "base_branch", "commits_ahead", "commits_behind"); err != nil { + return err + } - return nil + // Create comment + options := &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypeChangeTargetBranch, + Doer: doer, + Repo: pr.Issue.Repo, + Issue: pr.Issue, + OldRef: oldBranch, + NewRef: targetBranch, + } + if _, err = issues_model.CreateComment(ctx, options); err != nil { + return fmt.Errorf("CreateChangeTargetBranchComment: %w", err) + } + + // Delete all old push comments and insert new push comments + if _, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). + And("type = ?", issues_model.CommentTypePullRequestPush). + NoAutoCondition(). + Delete(new(issues_model.Comment)); err != nil { + return err + } + + _, err = CreatePushPullComment(ctx, doer, pr, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false) + return err + }) } func checkForInvalidation(ctx context.Context, requests issues_model.PullRequestList, repoID int64, doer *user_model.User, branch string) error { @@ -410,7 +402,7 @@ func AddTestPullRequestTask(opts TestPullRequestOptions) { } StartPullRequestCheckImmediately(ctx, pr) - comment, err := CreatePushPullComment(ctx, opts.Doer, pr, opts.OldCommitID, opts.NewCommitID) + comment, err := CreatePushPullComment(ctx, opts.Doer, pr, opts.OldCommitID, opts.NewCommitID, opts.IsForcePush) if err == nil && comment != nil { notify_service.PullRequestPushCommits(ctx, opts.Doer, pr, comment) } diff --git a/services/webhook/discord.go b/services/webhook/discord.go index 0e8a9aa67c87c..b6de41df0a9c7 100644 --- a/services/webhook/discord.go +++ b/services/webhook/discord.go @@ -57,7 +57,7 @@ type ( DiscordPayload struct { Wait bool `json:"wait"` Content string `json:"content"` - Username string `json:"username"` + Username string `json:"username,omitempty"` AvatarURL string `json:"avatar_url,omitempty"` TTS bool `json:"tts"` Embeds []DiscordEmbed `json:"embeds"` diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index b49818c6b7c63..dbcfa79aa83b1 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -119,7 +119,7 @@ {{range $idx, $code := .FileContent}} {{$line := Eval $idx "+" 1}} - + {{if $.EscapeStatus.Escaped}} {{if (index $.LineEscapeStatus $idx).Escaped}}{{end}} {{end}} diff --git a/tests/integration/api_packages_swift_test.go b/tests/integration/api_packages_swift_test.go index b29e8459ffb2b..8cb2d757e476d 100644 --- a/tests/integration/api_packages_swift_test.go +++ b/tests/integration/api_packages_swift_test.go @@ -355,6 +355,7 @@ func TestPackageSwift(t *testing.T) { assert.Equal(t, packageVersion, result.Metadata.Version) assert.Equal(t, packageDescription, result.Metadata.Description) assert.Equal(t, "Swift", result.Metadata.ProgrammingLanguage.Name) + assert.Equal(t, packageAuthor, result.Metadata.Author.Name) assert.Equal(t, packageAuthor, result.Metadata.Author.GivenName) req = NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/%s.json", url, packageScope, packageName, packageVersion)). diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 2e6a12df2cf47..48667106296ea 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/indexer/issues" @@ -471,19 +472,38 @@ func TestIssueRedirect(t *testing.T) { session := loginUser(t, "user2") // Test external tracker where style not set (shall default numeric) - req := NewRequest(t, "GET", path.Join("org26", "repo_external_tracker", "issues", "1")) + req := NewRequest(t, "GET", "/org26/repo_external_tracker/issues/1") resp := session.MakeRequest(t, req, http.StatusSeeOther) assert.Equal(t, "https://tracker.com/org26/repo_external_tracker/issues/1", test.RedirectURL(resp)) // Test external tracker with numeric style - req = NewRequest(t, "GET", path.Join("org26", "repo_external_tracker_numeric", "issues", "1")) + req = NewRequest(t, "GET", "/org26/repo_external_tracker_numeric/issues/1") resp = session.MakeRequest(t, req, http.StatusSeeOther) assert.Equal(t, "https://tracker.com/org26/repo_external_tracker_numeric/issues/1", test.RedirectURL(resp)) // Test external tracker with alphanumeric style (for a pull request) - req = NewRequest(t, "GET", path.Join("org26", "repo_external_tracker_alpha", "issues", "1")) + req = NewRequest(t, "GET", "/org26/repo_external_tracker_alpha/issues/1") resp = session.MakeRequest(t, req, http.StatusSeeOther) - assert.Equal(t, "/"+path.Join("org26", "repo_external_tracker_alpha", "pulls", "1"), test.RedirectURL(resp)) + assert.Equal(t, "/org26/repo_external_tracker_alpha/pulls/1", test.RedirectURL(resp)) + + // test to check that the PR redirection works if the issue unit is disabled + // repo1 is a normal repository with issue unit enabled, visit issue 2(which is a pull request) + // will redirect to pulls + req = NewRequest(t, "GET", "/user2/repo1/issues/2") + resp = session.MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/user2/repo1/pulls/2", test.RedirectURL(resp)) + + repoUnit := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoUnit{RepoID: 1, Type: unit.TypeIssues}) + + // disable issue unit, it will be reset + _, err := db.DeleteByID[repo_model.RepoUnit](t.Context(), repoUnit.ID) + assert.NoError(t, err) + + // even if the issue unit is disabled, visiting an issue which is a pull request + // will still redirect to pull request + req = NewRequest(t, "GET", "/user2/repo1/issues/2") + resp = session.MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/user2/repo1/pulls/2", test.RedirectURL(resp)) } func TestSearchIssues(t *testing.T) { diff --git a/tests/integration/pull_compare_test.go b/tests/integration/pull_compare_test.go index 86bdd1b9e351c..9e03366bf1e47 100644 --- a/tests/integration/pull_compare_test.go +++ b/tests/integration/pull_compare_test.go @@ -105,7 +105,15 @@ func TestPullCompare_EnableAllowEditsFromMaintainer(t *testing.T) { // user4 creates a new branch and a PR testEditFileToNewBranch(t, user4Session, "user4", forkedRepoName, "master", "user4/update-readme", "README.md", "Hello, World\n(Edited by user4)\n") - resp := testPullCreateDirectly(t, user4Session, repo3.OwnerName, repo3.Name, "master", "user4", forkedRepoName, "user4/update-readme", "PR for user4 forked repo3") + resp := testPullCreateDirectly(t, user4Session, createPullRequestOptions{ + BaseRepoOwner: repo3.OwnerName, + BaseRepoName: repo3.Name, + BaseBranch: "master", + HeadRepoOwner: "user4", + HeadRepoName: forkedRepoName, + HeadBranch: "user4/update-readme", + Title: "PR for user4 forked repo3", + }) prURL := test.RedirectURL(resp) // user2 (admin of repo3) goes to the PR files page diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go index 179c84e673966..11bff3e417f55 100644 --- a/tests/integration/pull_create_test.go +++ b/tests/integration/pull_create_test.go @@ -60,26 +60,50 @@ func testPullCreate(t *testing.T, session *TestSession, user, repo string, toSel return resp } -func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder { - headCompare := headBranch - if headRepoOwner != "" { - if headRepoName != "" { - headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch) +type createPullRequestOptions struct { + BaseRepoOwner string + BaseRepoName string + BaseBranch string + HeadRepoOwner string + HeadRepoName string + HeadBranch string + Title string + ReviewerIDs string // comma-separated list of user IDs +} + +func (opts createPullRequestOptions) IsValid() bool { + return opts.BaseRepoOwner != "" && opts.BaseRepoName != "" && opts.BaseBranch != "" && + opts.HeadBranch != "" && opts.Title != "" +} + +func testPullCreateDirectly(t *testing.T, session *TestSession, opts createPullRequestOptions) *httptest.ResponseRecorder { + if !opts.IsValid() { + t.Fatal("Invalid pull request options") + } + + headCompare := opts.HeadBranch + if opts.HeadRepoOwner != "" { + if opts.HeadRepoName != "" { + headCompare = fmt.Sprintf("%s/%s:%s", opts.HeadRepoOwner, opts.HeadRepoName, opts.HeadBranch) } else { - headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch) + headCompare = fmt.Sprintf("%s:%s", opts.HeadRepoOwner, opts.HeadBranch) } } - req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare)) + req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", opts.BaseRepoOwner, opts.BaseRepoName, opts.BaseBranch, headCompare)) resp := session.MakeRequest(t, req, http.StatusOK) // Submit the form for creating the pull htmlDoc := NewHTMLParser(t, resp.Body) link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action") assert.True(t, exists, "The template has changed") - req = NewRequestWithValues(t, "POST", link, map[string]string{ + params := map[string]string{ "_csrf": htmlDoc.GetCSRF(), - "title": title, - }) + "title": opts.Title, + } + if opts.ReviewerIDs != "" { + params["reviewer_ids"] = opts.ReviewerIDs + } + req = NewRequestWithValues(t, "POST", link, params) resp = session.MakeRequest(t, req, http.StatusOK) return resp } @@ -246,7 +270,15 @@ func TestPullCreatePrFromBaseToFork(t *testing.T) { testEditFile(t, sessionBase, "user2", "repo1", "master", "README.md", "Hello, World (Edited)\n") // Create a PR - resp := testPullCreateDirectly(t, sessionFork, "user1", "repo1", "master", "user2", "repo1", "master", "This is a pull title") + resp := testPullCreateDirectly(t, sessionFork, createPullRequestOptions{ + BaseRepoOwner: "user1", + BaseRepoName: "repo1", + BaseBranch: "master", + HeadRepoOwner: "user2", + HeadRepoName: "repo1", + HeadBranch: "master", + Title: "This is a pull title", + }) // check the redirected URL url := test.RedirectURL(resp) assert.Regexp(t, "^/user1/repo1/pulls/[0-9]*$", url) diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 1121751c88bc7..628ac1c9800ff 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -184,13 +184,29 @@ func TestPullView_CodeOwner(t *testing.T) { session := loginUser(t, "user5") // create a pull request on the forked repository, code reviewers should not be mentioned - testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository") + testPullCreateDirectly(t, session, createPullRequestOptions{ + BaseRepoOwner: "user5", + BaseRepoName: "test_codeowner", + BaseBranch: forkedRepo.DefaultBranch, + HeadRepoOwner: "", + HeadRepoName: "", + HeadBranch: "codeowner-basebranch-forked", + Title: "Test Pull Request on Forked Repository", + }) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) unittest.AssertNotExistsBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) // create a pull request to base repository, code reviewers should be mentioned - testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3") + testPullCreateDirectly(t, session, createPullRequestOptions{ + BaseRepoOwner: repo.OwnerName, + BaseRepoName: repo.Name, + BaseBranch: repo.DefaultBranch, + HeadRepoOwner: forkedRepo.OwnerName, + HeadRepoName: forkedRepo.Name, + HeadBranch: "codeowner-basebranch-forked", + Title: "Test Pull Request3", + }) pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go index 3ccc34f20f514..81de5481a9e29 100644 --- a/tests/integration/repo_webhook_test.go +++ b/tests/integration/repo_webhook_test.go @@ -14,6 +14,7 @@ import ( "time" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -529,15 +530,30 @@ func Test_WebhookPullRequest(t *testing.T) { }, http.StatusOK) defer provider.Close() + testCtx := NewAPITestContext(t, "user2", "repo1", auth_model.AccessTokenScopeAll) + // add user4 as collabrator so that it can be a reviewer + doAPIAddCollaborator(testCtx, "user4", perm.AccessModeWrite)(t) + // 1. create a new webhook with special webhook for repo1 - session := loginUser(t, "user2") + sessionUser2 := loginUser(t, "user2") + sessionUser4 := loginUser(t, "user4") - testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request") + // ignore the possible review_requested event to keep the test deterministic + testAPICreateWebhookForRepo(t, sessionUser2, "user2", "repo1", provider.URL(), "pull_request_only") - testAPICreateBranch(t, session, "user2", "repo1", "master", "master2", http.StatusCreated) + testAPICreateBranch(t, sessionUser2, "user2", "repo1", "master", "master2", http.StatusCreated) // 2. trigger the webhook repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1}) - testCreatePullToDefaultBranch(t, session, repo1, repo1, "master2", "first pull request") + testPullCreateDirectly(t, sessionUser4, createPullRequestOptions{ + BaseRepoOwner: repo1.OwnerName, + BaseRepoName: repo1.Name, + BaseBranch: repo1.DefaultBranch, + HeadRepoOwner: "", + HeadRepoName: "", + HeadBranch: "master2", + Title: "first pull request", + ReviewerIDs: "2", // add user2 as reviewer + }) // 3. validate the webhook is triggered assert.Equal(t, "pull_request", triggeredEvent) @@ -549,6 +565,8 @@ func Test_WebhookPullRequest(t *testing.T) { assert.Equal(t, 0, *payloads[0].PullRequest.Additions) assert.Equal(t, 0, *payloads[0].PullRequest.ChangedFiles) assert.Equal(t, 0, *payloads[0].PullRequest.Deletions) + assert.Len(t, payloads[0].PullRequest.RequestedReviewers, 1) + assert.Equal(t, int64(2), payloads[0].PullRequest.RequestedReviewers[0].ID) }) }