From 0eba0e371f360eb91af2df3b881b6ee7bca91d2f Mon Sep 17 00:00:00 2001 From: metsw24-max Date: Mon, 15 Jun 2026 22:44:14 +0530 Subject: [PATCH 01/10] fix(packages): validate module version in goproxy ParsePackage (#38104) **Unvalidated version in goproxy ParsePackage** The module version is read straight from the zip directory path and never checked, so a crafted upload can leave a newline in it; `EnumeratePackageVersions` then writes each stored version on its own line for the `@v/list` endpoint, letting a module advertise fabricated versions to `go` clients. Validated the parsed version with `semver.IsValid` inside the parser, matching the version checks the other package parsers already do. Co-authored-by: Lunny Xiao --- go.mod | 2 +- modules/packages/goproxy/metadata.go | 10 ++++++++++ modules/packages/goproxy/metadata_test.go | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index a5f42c6caf..9d23a99410 100644 --- a/go.mod +++ b/go.mod @@ -105,6 +105,7 @@ require ( go.yaml.in/yaml/v4 v4.0.0-rc.5 golang.org/x/crypto v0.53.0 golang.org/x/image v0.42.0 + golang.org/x/mod v0.37.0 golang.org/x/net v0.56.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.21.0 @@ -267,7 +268,6 @@ require ( go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org v0.0.0-20260112195520-a5071408f32f // indirect - golang.org/x/mod v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.45.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260610212136-7ab31c22f7ad // indirect diff --git a/modules/packages/goproxy/metadata.go b/modules/packages/goproxy/metadata.go index b19738db56..db50ac7faa 100644 --- a/modules/packages/goproxy/metadata.go +++ b/modules/packages/goproxy/metadata.go @@ -10,6 +10,8 @@ import ( "strings" "gitea.dev/modules/util" + + "golang.org/x/mod/semver" ) const ( @@ -20,6 +22,7 @@ const ( var ( ErrInvalidStructure = util.NewInvalidArgumentErrorf("package has invalid structure") + ErrInvalidVersion = util.NewInvalidArgumentErrorf("package version is invalid") ErrGoModFileTooLarge = util.NewInvalidArgumentErrorf("go.mod file is too large") ) @@ -54,6 +57,13 @@ func ParsePackage(r io.ReaderAt, size int64) (*Package, error) { Name: strings.TrimSuffix(nameAndVersion, "@"+parts[1]), Version: versionParts[0], } + + // the version is taken verbatim from the zip path and later written + // one per line into the @v/list proxy response, so it has to be a + // valid module version (no newlines or other stray characters) + if !semver.IsValid(p.Version) { + return nil, ErrInvalidVersion + } } if len(versionParts) > 1 { diff --git a/modules/packages/goproxy/metadata_test.go b/modules/packages/goproxy/metadata_test.go index 4e7f394f8b..24db359651 100644 --- a/modules/packages/goproxy/metadata_test.go +++ b/modules/packages/goproxy/metadata_test.go @@ -59,6 +59,16 @@ func TestParsePackage(t *testing.T) { assert.Equal(t, "module gitea.com/go-gitea/gitea", p.GoMod) }) + t.Run("InvalidVersion", func(t *testing.T) { + data := createArchive(map[string][]byte{ + packageName + "@v1.0.0\nv99.0.0/go.mod": []byte("module " + packageName), + }) + + p, err := ParsePackage(data, int64(data.Len())) + assert.Nil(t, p) + assert.ErrorIs(t, err, ErrInvalidVersion) + }) + t.Run("Valid", func(t *testing.T) { data := createArchive(map[string][]byte{ packageName + "@" + packageVersion + "/subdir/go.mod": []byte("invalid"), From 7997c1ccadf2f0b1a0275a499f0c95c182c79895 Mon Sep 17 00:00:00 2001 From: bircni Date: Mon, 15 Jun 2026 19:55:31 +0200 Subject: [PATCH 02/10] fix(pull): preserve squash message trailers and additional commit messages (#37954) * Closes #37950 * Closes #37946 * Fixes https://github.com/go-gitea/gitea/issues/37529 --------- Co-authored-by: wxiaoguang --- modules/git/commit_message.go | 4 +- modules/git/commit_message_test.go | 2 + services/pull/pull.go | 143 +++++++++++++++------------ services/pull/pull_test.go | 59 ++++++++--- tests/integration/pull_merge_test.go | 2 +- 5 files changed, 128 insertions(+), 82 deletions(-) diff --git a/modules/git/commit_message.go b/modules/git/commit_message.go index 8fd3601f0d..0729609d87 100644 --- a/modules/git/commit_message.go +++ b/modules/git/commit_message.go @@ -70,9 +70,11 @@ func (c *CommitMessage) MessageTrailer() CommitMessageTrailerValues { var commitMessageTrailerSplit = sync.OnceValue(func() *regexp.Regexp { // the sep is either something like "\n---\n" or "\n\n" in the body, or at the start of the body like "---\n" - return regexp.MustCompile(`(?s)^(?P.*?)(?P^|^\n|^-{3,}\n|\n-{3,}\n|\n\n)(?P(?:[A-Za-z0-9][-A-Za-z0-9]*:[^\n]*\n?)*)$`) + return regexp.MustCompile(`(?s)^(?P.*?)(?P^|^\n|^-{3,}\n+|\n-{3,}\n+|\n\n)(?P(?:[A-Za-z0-9][-A-Za-z0-9]*:[^\n]*\n?)*\n*)$`) }) +// CommitMessageSplitTrailer tries to split the message by the trailer separator +// content + sep + trailer will reconstruct the original message func CommitMessageSplitTrailer(s string) (content, sep, trailer string) { s = util.NormalizeStringEOL(s) re := commitMessageTrailerSplit() diff --git a/modules/git/commit_message_test.go b/modules/git/commit_message_test.go index 049f1c03f7..1d13f81803 100644 --- a/modules/git/commit_message_test.go +++ b/modules/git/commit_message_test.go @@ -26,8 +26,10 @@ func TestCommitMessageTrailer(t *testing.T) { {"a", "a", "", ""}, {"a\n\nk", "a\n\nk", "", ""}, {"a\n\nk:v", "a", "\n\n", "k:v"}, + {"a\n\nk:v\n\n", "a", "\n\n", "k:v\n\n"}, {"a\n--\nk:v", "a\n--\nk:v", "", ""}, {"a\n---\nk:v", "a", "\n---\n", "k:v"}, + {"a\n\n---\n\nk:v", "a\n", "\n---\n\n", "k:v"}, {"k: v", "", "", "k: v"}, {"\nk:v", "", "\n", "k:v"}, diff --git a/services/pull/pull.go b/services/pull/pull.go index 4491406b33..13f8ffa8df 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -4,14 +4,15 @@ package pull import ( + "bytes" "context" "errors" "fmt" "io" - "regexp" "slices" "strings" "time" + "unicode" "unicode/utf8" "gitea.dev/models/db" @@ -767,8 +768,6 @@ func CloseRepoBranchesPulls(ctx context.Context, doer *user_model.User, repo *re return errors.Join(errs...) } -var commitMessageTrailersPattern = regexp.MustCompile(`(?:^|\n\n)(?:[\w-]+[ \t]*:[^\n]+\n*(?:[ \t]+[^\n]+\n*)*)+$`) - // GetSquashMergeCommitMessages returns the commit messages between head and merge base (if there is one) func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequest) string { if err := pr.LoadIssue(ctx); err != nil { @@ -819,54 +818,44 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ return "" } - posterSig := pr.Issue.Poster.NewGitSig().String() + mergeMessage := strings.TrimSpace(pr.Issue.Content) // use PR's title and description as squash commit message + if setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages { + mergeMessage = formatSquashMergeCommitMessages(limitedCommits) // use PR's commit messages as squash commit message + } + coAuthors := collectSquashMergeCommitCoAuthors(ctx, gitRepo, pr, headCommitRef, mergeBaseRef, limit, limitedCommits) + return buildSquashMergeCommitMessages(mergeMessage, coAuthors) +} - uniqueAuthors := make(container.Set[string]) - authors := make([]string, 0, len(limitedCommits)) - stringBuilder := strings.Builder{} - - if !setting.Repository.PullRequest.PopulateSquashCommentWithCommitMessages { - // use PR's title and description as squash commit message - message := strings.TrimSpace(pr.Issue.Content) - stringBuilder.WriteString(message) - if stringBuilder.Len() > 0 { - stringBuilder.WriteRune('\n') - if !commitMessageTrailersPattern.MatchString(message) { - // TODO: this trailer check doesn't work with the separator line added below for the co-authors - stringBuilder.WriteRune('\n') - } - } - } else { - // use PR's commit messages as squash commit message - // commits list is in reverse chronological order - maxMsgSize := setting.Repository.PullRequest.DefaultMergeMessageSize - for _, commit := range slices.Backward(limitedCommits) { - msg := strings.TrimSpace(commit.MessageUTF8()) - if msg == "" { - continue - } - - // This format follows GitHub's squash commit message style, - // even if there are other "* " in the commit message body, they are written as-is. - // Maybe, ideally, we should indent those lines too. - _, _ = fmt.Fprintf(&stringBuilder, "* %s\n\n", msg) - if maxMsgSize > 0 && stringBuilder.Len() >= maxMsgSize { - tmp := stringBuilder.String() - wasValidUtf8 := utf8.ValidString(tmp) - tmp = tmp[:maxMsgSize] + "..." - if wasValidUtf8 { - // If the message was valid UTF-8 before truncation, ensure it remains valid after truncation - // For non-utf8 messages, we can't do much about it, end users should use utf-8 as much as possible - tmp = strings.ToValidUTF8(tmp, "") - } - stringBuilder.Reset() - stringBuilder.WriteString(tmp) - break - } - } +func buildSquashMergeCommitMessages(mergeMessage string, coAuthors []string) string { + if len(coAuthors) == 0 { + return mergeMessage } - // collect co-authors + msgContent, msgSep, msgTrailer := git.CommitMessageSplitTrailer(mergeMessage) + if (msgSep == "" || msgSep == "\n\n") && msgTrailer == "" { + msgContent = strings.TrimRightFunc(msgContent, unicode.IsSpace) + msgSep = "\n\n---------\n\n" + } + var sb strings.Builder + sb.WriteString(msgContent) + sb.WriteString(msgSep) + if msgTrailer = strings.TrimSpace(msgTrailer); msgTrailer != "" { + sb.WriteString(msgTrailer) + sb.WriteRune('\n') + } + for _, author := range coAuthors { + sb.WriteString(git.CoAuthoredByTrailer + ": ") + sb.WriteString(author) + sb.WriteRune('\n') + } + return sb.String() +} + +func collectSquashMergeCommitCoAuthors(ctx context.Context, gitRepo *git.Repository, pr *issues_model.PullRequest, headCommitRef, mergeBaseRef git.RefName, limitFirst int, limitedCommits []*git.Commit) []string { + posterSig := pr.Issue.Poster.NewGitSig().String() + uniqueAuthors := make(container.Set[string]) + authors := make([]string, 0, len(limitedCommits)) + for _, commit := range limitedCommits { authorString := commit.Author.String() if uniqueAuthors.Add(authorString) && authorString != posterSig { @@ -880,14 +869,14 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ } // collect the remaining authors - if limit >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors { - skip := limit - limit = 30 + if limitFirst >= 0 && setting.Repository.PullRequest.DefaultMergeMessageAllAuthors { + skip := limitFirst + batchLimit := 30 for { - commits, err := gitRepo.CommitsBetween(headCommitRef, mergeBaseRef, limit, skip) + commits, err := gitRepo.CommitsBetween(headCommitRef, mergeBaseRef, batchLimit, skip) if err != nil { log.Error("Unable to get commits between: %s %s Error: %v", pr.HeadBranch, pr.MergeBase, err) - return "" + return authors } if len(commits) == 0 { break @@ -901,22 +890,46 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ } } } - skip += limit + skip += batchLimit + } + } + return authors +} + +func formatSquashMergeCommitMessages(commits []*git.Commit) string { + maxMsgSize := setting.Repository.PullRequest.DefaultMergeMessageSize + sb := &bytes.Buffer{} + // commits list is in reverse chronological order + for _, commit := range slices.Backward(commits) { + msg := strings.TrimSpace(commit.MessageUTF8()) + if msg == "" { + continue + } + + // This format follows GitHub's squash commit message style, + // even if there are other "* " in the commit message body, they are written as-is. + // Maybe, ideally, we should indent those lines too. + _, _ = fmt.Fprintf(sb, "* %s\n\n", msg) + if maxMsgSize > 0 && sb.Len() >= maxMsgSize { + break } } - if stringBuilder.Len() > 0 && len(authors) > 0 { - // TODO: this separator line doesn't work with the trailer check (commitMessageTrailersPattern) above - stringBuilder.WriteString("---------\n\n") + buf := bytes.TrimSpace(sb.Bytes()) + if maxMsgSize > 0 && len(buf) > maxMsgSize { + buf = buf[:maxMsgSize] + for { + r, sz := utf8.DecodeLastRune(buf) + if r == utf8.RuneError && sz == 1 { + buf = buf[:len(buf)-1] + continue + } + break + } + buf = append(buf, '.', '.', '.') } - - for _, author := range authors { - stringBuilder.WriteString(git.CoAuthoredByTrailer + ": ") - stringBuilder.WriteString(author) - stringBuilder.WriteRune('\n') - } - - return stringBuilder.String() + buf = append(buf, '\n', '\n') + return util.UnsafeBytesToString(buf) } // GetIssuesAllCommitStatus returns a map of issue ID to a list of all statuses for the most recent commit as well as a map of issue ID to only the commit's latest status diff --git a/services/pull/pull_test.go b/services/pull/pull_test.go index 0648cd383d..554c9ed577 100644 --- a/services/pull/pull_test.go +++ b/services/pull/pull_test.go @@ -11,28 +11,33 @@ import ( repo_model "gitea.dev/models/repo" "gitea.dev/models/unit" "gitea.dev/models/unittest" + "gitea.dev/modules/git" "gitea.dev/modules/gitrepo" + "gitea.dev/modules/setting" + "gitea.dev/modules/test" "github.com/stretchr/testify/assert" ) // TODO TestPullRequest_PushToBaseRepo -func TestPullRequest_CommitMessageTrailersPattern(t *testing.T) { - // Not a valid trailer section - assert.False(t, commitMessageTrailersPattern.MatchString("")) - assert.False(t, commitMessageTrailersPattern.MatchString("No trailer.")) - assert.False(t, commitMessageTrailersPattern.MatchString("Signed-off-by: Bob \nNot a trailer due to following text.")) - assert.False(t, commitMessageTrailersPattern.MatchString("Message body not correctly separated from trailer section by empty line.\nSigned-off-by: Bob ")) - // Valid trailer section - assert.True(t, commitMessageTrailersPattern.MatchString("Signed-off-by: Bob ")) - assert.True(t, commitMessageTrailersPattern.MatchString("Signed-off-by: Bob \nOther-Trailer: Value")) - assert.True(t, commitMessageTrailersPattern.MatchString("Message body correctly separated from trailer section by empty line.\n\nSigned-off-by: Bob ")) - assert.True(t, commitMessageTrailersPattern.MatchString("Multiple trailers.\n\nSigned-off-by: Bob \nOther-Trailer: Value")) - assert.True(t, commitMessageTrailersPattern.MatchString("Newline after trailer section.\n\nSigned-off-by: Bob \n")) - assert.True(t, commitMessageTrailersPattern.MatchString("No space after colon is accepted.\n\nSigned-off-by:Bob ")) - assert.True(t, commitMessageTrailersPattern.MatchString("Additional whitespace is accepted.\n\nSigned-off-by \t : \tBob ")) - assert.True(t, commitMessageTrailersPattern.MatchString("Folded value.\n\nFolded-trailer: This is\n a folded\n trailer value\nOther-Trailer: Value")) +func TestPullRequest_FormatSquashMergeCommitMessages(t *testing.T) { + oldest := &git.Commit{CommitMessage: git.CommitMessage{MessageRaw: "commit msg 1"}} + newest := &git.Commit{CommitMessage: git.CommitMessage{MessageRaw: "commit msg 2\n\nCommit description."}} + + defer test.MockVariableValue(&setting.Repository.PullRequest.DefaultMergeMessageSize, 0)() + + assert.Equal(t, "* commit msg 1\n\n* commit msg 2\n\nCommit description.\n\n", formatSquashMergeCommitMessages([]*git.Commit{newest, oldest})) + + utf8Msg := &git.Commit{CommitMessage: git.CommitMessage{MessageRaw: "🌞"}} + setting.Repository.PullRequest.DefaultMergeMessageSize = 3 + assert.Equal(t, "* ...\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg})) + setting.Repository.PullRequest.DefaultMergeMessageSize = 4 + assert.Equal(t, "* ...\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg})) + setting.Repository.PullRequest.DefaultMergeMessageSize = 5 + assert.Equal(t, "* ...\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg})) + setting.Repository.PullRequest.DefaultMergeMessageSize = 6 + assert.Equal(t, "* 🌞\n\n", formatSquashMergeCommitMessages([]*git.Commit{utf8Msg})) } func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) { @@ -88,3 +93,27 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) { assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo2:branch2 into master", mergeMessage) } + +func TestBuildSquashMergeCommitMessages(t *testing.T) { + cases := []struct { + msg string + coAuthors []string + expected string + }{ + {"title", nil, "title"}, + {"title", []string{"the-user"}, "title\n\n---------\n\nCo-authored-by: the-user\n"}, + {"title\n\n", []string{"the-user"}, "title\n\n---------\n\nCo-authored-by: the-user\n"}, + {"title\n\nKey: val", []string{"the-user"}, "title\n\nKey: val\nCo-authored-by: the-user\n"}, + {"title\n\n----\nKey: val", []string{"the-user"}, "title\n\n----\nKey: val\nCo-authored-by: the-user\n"}, + {"title\n\n----\nKey: val\n\n", []string{"the-user"}, "title\n\n----\nKey: val\nCo-authored-by: the-user\n"}, + + {"title\n\nbody", nil, "title\n\nbody"}, + {"title\n\nbody", []string{"the-user"}, "title\n\nbody\n\n---------\n\nCo-authored-by: the-user\n"}, + {"title\n\nbody\n\nKey: val", []string{"the-user"}, "title\n\nbody\n\nKey: val\nCo-authored-by: the-user\n"}, + {"title\n\nbody\n\n----\nKey: val", []string{"the-user"}, "title\n\nbody\n\n----\nKey: val\nCo-authored-by: the-user\n"}, + } + for _, c := range cases { + msg := buildSquashMergeCommitMessages(c.msg, c.coAuthors) + assert.Equal(t, c.expected, msg, "msg: %s", c.msg) + } +} diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 8469057620..ae1f8a3490 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -1272,7 +1272,7 @@ Commit description. commitMessage: `loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong message`, }, }, - expectedMessage: `* looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo...`, + expectedMessage: "* looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo...\n\n", }, { name: "Test Co-authored-by", From 8ff8422307d88467610aa30cc49a922630083ce6 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Mon, 15 Jun 2026 13:23:13 -0700 Subject: [PATCH 03/10] chore(deps): update module github.com/go-swagger/go-swagger to v0.34.1 (#38122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [github.com/go-swagger/go-swagger](https://redirect.github.com/go-swagger/go-swagger) | `v0.34.0` → `v0.34.1` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-swagger%2fgo-swagger/v0.34.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-swagger%2fgo-swagger/v0.34.0/v0.34.1?slim=true) | --- ### Release Notes
go-swagger/go-swagger (github.com/go-swagger/go-swagger) ### [`v0.34.1`](https://redirect.github.com/go-swagger/go-swagger/releases/tag/v0.34.1) [Compare Source](https://redirect.github.com/go-swagger/go-swagger/compare/v0.34.0...v0.34.1) go-swagger release 0.34.1 *** Released on 2026 Jun 05 ##### [0.34.1](https://redirect.github.com/go-swagger/go-swagger/tree/v0.34.1) - 2026-06-05 Fixed regression on initialisms (codegen) **Full Changelog**: 7 commits in this release. *** ##### Fixed bugs - fix(codegen): fixed regression from v0.34.0 : initialisms skipped by [@​fredbi](https://redirect.github.com/fredbi) in [#​3360](https://redirect.github.com/go-swagger/go-swagger/pull/3360) [...](https://redirect.github.com/go-swagger/go-swagger/commit/e5c5fec88a5ad02a2ded264bbecd276b83340d12) ##### Miscellaneous tasks - fix(ci): fixed release announcement to discord by [@​fredbi](https://redirect.github.com/fredbi) in [#​3354](https://redirect.github.com/go-swagger/go-swagger/pull/3354) [...](https://redirect.github.com/go-swagger/go-swagger/commit/d9244f53d41c79c3788867a4a031bea920cd418b) - ci: fixed event by [@​fredbi](https://redirect.github.com/fredbi) [...](https://redirect.github.com/go-swagger/go-swagger/commit/ba881e358085d4c4c004c5f61697d74f65390fa1) - ci: repair release by [@​fredbi](https://redirect.github.com/fredbi) in [#​3353](https://redirect.github.com/go-swagger/go-swagger/pull/3353) [...](https://redirect.github.com/go-swagger/go-swagger/commit/70b7c214ce3d9174956293f45f54d06ee3c4112f) ##### Updates - chore(deps): bump the development-dependencies group with 3 updates by [@​dependabot\[bot\]](https://redirect.github.com/dependabot\[bot]) in [#​3359](https://redirect.github.com/go-swagger/go-swagger/pull/3359) [...](https://redirect.github.com/go-swagger/go-swagger/commit/65d6af084bb829d38bf53fc4d6cdc4a0cacec3ba) - chore(deps): bump golang from `91eda97` to `f23e8b2` in the development-dependencies group across 1 directory by [@​dependabot\[bot\]](https://redirect.github.com/dependabot\[bot]) in [#​3356](https://redirect.github.com/go-swagger/go-swagger/pull/3356) [...](https://redirect.github.com/go-swagger/go-swagger/commit/b1dfdf0c14f4de2d90b6de3b64619effcc82d67c) - chore(deps): bump the development-dependencies group with 2 updates by [@​dependabot\[bot\]](https://redirect.github.com/dependabot\[bot]) in [#​3355](https://redirect.github.com/go-swagger/go-swagger/pull/3355) [...](https://redirect.github.com/go-swagger/go-swagger/commit/340826c27f8ecd6d597d708505db83bf16033dcd) *** ##### People who contributed to this release - [@​fredbi](https://redirect.github.com/fredbi) *** **[go-swagger](https://redirect.github.com/go-swagger/go-swagger) license terms** [![License][license-badge]][license-url] [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg [license-url]: https://redirect.github.com/go-swagger/go-swagger/?tab=Apache-2.0-1-ov-file#readme *** Released by [GoReleaser](https://redirect.github.com/goreleaser/goreleaser).
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - Only on Monday (`* * * * 1`) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://redirect.github.com/renovatebot/renovate). Co-authored-by: bircni --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 939264233f..86d21dcf44 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.15 # renovate: datasource=go MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.8.0 # renovate: datasource=go -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.34.0 # renovate: datasource=go +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.34.1 # renovate: datasource=go XGO_PACKAGE ?= src.techknowlogick.com/xgo@v1.9.0 # renovate: datasource=go GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.3.0 # renovate: datasource=go ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.7.12 # renovate: datasource=go From 89d11314f98e2826b30793c9b7da27d65faf9ec3 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Tue, 16 Jun 2026 01:27:50 +0000 Subject: [PATCH 04/10] [skip ci] Updated translations via Crowdin --- options/locale/locale_ga-IE.json | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_ga-IE.json b/options/locale/locale_ga-IE.json index 9bff2c24be..c1e11779d1 100644 --- a/options/locale/locale_ga-IE.json +++ b/options/locale/locale_ga-IE.json @@ -2205,10 +2205,10 @@ "repo.settings.trust_model.collaborator.desc": "Déanfar sínithe bailí ó chomhoibritheoirí an stórais seo a mharcáil mar \"iontaofa\", cibé acu a mheaitseálann siad an tiomnóir nó nach meaitseálann. Seachas sin, déanfar sínithe bailí a mharcáil mar \"neamhiontaofa\" má mheaitseálann an síniú an tiomnóir agus \"gan mheaitseáil\" mura bhfuil.", "repo.settings.trust_model.committer": "Coimisitheoir", "repo.settings.trust_model.committer.long": "Tiomnaithe: Sínithe muiníne a mheaitseálann tiomnóirí. Meaitseálann sé seo iompar GitHub agus cuirfidh sé iallach ar thiomnóirí atá sínithe ag Gitea Gitea a bheith mar an tiomnóir.", - "repo.settings.trust_model.committer.desc": "Ní mharcálfar sínithe bailí mar \"iontaofa\" ach amháin má mheaitseálann siad an tiomnaí, nó marcálfar iad mar \"gan mheaitseáil\". Cuireann sé seo iallach ar Gitea a bheith ina tiomnaí ar thiomnuithe sínithe, agus an tiomnaí iarbhír marcáilte mar Chomhúdaraithe ag: agus Co-thiomnaithe ag: leantóir sa tiomnú. Caithfidh eochair réamhshocraithe Gitea a bheith ag teacht le húsáideoir sa bhunachar sonraí.", + "repo.settings.trust_model.committer.desc": "Ní mharcálfar sínithe bailí mar \"iontaofa\" ach amháin má mheaitseálann siad an tiomnóir, nó marcálfar iad mar \"gan mheaitseáil\". Cuireann sé seo iallach ar Gitea a bheith ina thiomnóir ar thiomnuithe sínithe, agus an tiomnóir iarbhír marcáilte mar leantóir Co-authored-by: sa thiomnú. Caithfidh eochair réamhshocraithe Gitea a bheith ag teacht le húsáideoir sa bhunachar sonraí.", "repo.settings.trust_model.collaboratorcommitter": "Comhoibritheo+Coimiteoir", "repo.settings.trust_model.collaboratorcommitter.long": "Comhoibrí+Coiste: sínithe muiníne ó chomhoibrithe a mheaitseálann an tiomnóir", - "repo.settings.trust_model.collaboratorcommitter.desc": "Marcálfar sínithe bailí ó chomhoibritheoirí an stórais seo mar \"iontaofa\" má mheaitseálann siad an tiomnaí. Seachas sin, marcálfar sínithe bailí mar \"neamhiontaofa\" má mheaitseálann an síniú an tiomnaí agus \"gan mheaitseáil\" murach sin. Cuirfidh sé seo iallach ar Gitea a bheith marcáilte mar an tiomnaí ar thiomnuithe sínithe, agus an tiomnaí iarbhír marcáilte mar Chomhúdaraithe ag: agus Co-Tiomnaithe ag: leantóir sa tiomnú. Ní mór don eochair réamhshocraithe Gitea a bheith ag teacht le húsáideoir sa bhunachar sonraí.", + "repo.settings.trust_model.collaboratorcommitter.desc": "Marcálfar sínithe bailí ó chomhoibritheoirí an stórais seo mar \"iontaofa\" má mheaitseálann siad an tiomnóir. Seachas sin, marcálfar sínithe bailí mar \"neamhiontaofa\" má mheaitseálann an síniú an tiomnóir agus \"gan mheaitseáil\" murach sin. Cuirfidh sé seo iallach ar Gitea a bheith marcáilte mar an tiomnóir ar thiomnuithe sínithe, agus an tiomnóir iarbhír marcáilte mar leantóir Co-Authored-By: sa tiomnú. Ní mór don eochair réamhshocraithe Gitea a bheith ag teacht le húsáideoir sa bhunachar sonraí.", "repo.settings.wiki_delete": "Scrios Sonraí Vicí", "repo.settings.wiki_delete_desc": "Tá sonraí wiki stóras a scriosadh buan agus ní féidir iad a chur ar ais.", "repo.settings.wiki_delete_notices_1": "- Scriosfaidh agus díchumasóidh sé seo an stóras vicí do %s go buan.", @@ -2599,6 +2599,9 @@ "repo.diff.review.reject": "Iarr athruithe", "repo.diff.review.self_approve": "Ní féidir le húdair iarratais tarraing a n-iarratas tarraingthe féin a chead", "repo.diff.committed_by": "tiomanta ag", + "repo.diff.coauthored_by": "comhúdaraithe ag", + "repo.commits.avatar_stack_and": "agus", + "repo.commits.avatar_stack_people": "%d duine", "repo.diff.protected": "Cosanta", "repo.diff.image.side_by_side": "Taobh le Taobh", "repo.diff.image.swipe": "Scaoil", @@ -2862,6 +2865,14 @@ "org.teams.all_repositories_read_permission_desc": "Tugann an fhoireann seo rochtain do Léamh ar gach stórais: is féidir le baill amharc ar stórais agus iad a chlónáil.", "org.teams.all_repositories_write_permission_desc": "Tugann an fhoireann seo rochtain do Scríobh ar gach stórais: is féidir le baill léamh ó stórais agus iad a bhrú chucu.", "org.teams.all_repositories_admin_permission_desc": "Tugann an fhoireann seo rochtain Riarthóra ar gach stóras: is féidir le comhaltaí léamh, brú a dhéanamh agus comhoibritheoirí a chur le stórtha.", + "org.teams.visibility": "Infheictheacht", + "org.teams.visibility_private": "Príobháideach", + "org.teams.visibility_private_helper": "Le feiceáil ag baill foirne agus úinéirí eagraíochta amháin.", + "org.teams.visibility_limited": "Teoranta", + "org.teams.visibility_limited_helper": "Infheicthe ag gach ball den eagraíocht seo.", + "org.teams.visibility_public": "Poiblí", + "org.teams.visibility_public_helper": "Infheicthe ag aon úsáideoir atá sínithe isteach.", + "org.teams.owners_visibility_fixed": "Ní féidir infheictheacht fhoireann na nÚinéirí a athrú.", "org.teams.invite.title": "Tugadh cuireadh duit dul isteach i bhfoireann %s san eagraíocht %s.", "org.teams.invite.by": "Ar cuireadh ó %s", "org.teams.invite.description": "Cliceáil ar an gcnaipe thíos le do thoil chun dul isteach san fhoireann.", @@ -3774,6 +3785,7 @@ "actions.runs.no_matching_online_runner_helper": "Gan aon reathaí ar líne a mheaitseáil le lipéad: %s", "actions.runs.no_job_without_needs": "Caithfidh post amháin ar a laghad a bheith sa sreabhadh oibre gan spleáchas.", "actions.runs.no_job": "Caithfidh post amháin ar a laghad a bheith sa sreabhadh oibre", + "actions.runs.invalid_reusable_workflow_uses": "Sreabhadh oibre in-athúsáidte neamhbhailí \"úsáidí\": %s", "actions.runs.actor": "Aisteoir", "actions.runs.status": "Stádas", "actions.runs.actors_no_select": "Gach aisteoir", @@ -3794,13 +3806,17 @@ "actions.runs.view_workflow_file": "Féach ar chomhad sreabha oibre", "actions.runs.summary": "Achoimre", "actions.runs.all_jobs": "Gach post", + "actions.runs.job_summaries": "Achoimrí poist", "actions.runs.expand_caller_jobs": "Taispeáin poist an ghlaoiteora sreabha oibre in-athúsáidte seo", "actions.runs.collapse_caller_jobs": "Folaigh poist an ghlaoiteora sreabha oibre in-athúsáidte seo", "actions.runs.attempt": "Iarracht", "actions.runs.latest": "Is déanaí", "actions.runs.latest_attempt": "An iarracht is déanaí", "actions.runs.triggered_via": "Spreagtha trí %s", - "actions.runs.total_duration": "Fad iomlán:", + "actions.runs.rerun_triggered": "Athrith spreagtha", + "actions.runs.back_to_pull_request": "Ar ais chuig an iarratas tarraingthe", + "actions.runs.back_to_workflow": "Ar ais chuig an sreabhadh oibre", + "actions.runs.total_duration": "Fad iomlán", "actions.runs.workflow_dependencies": "Spleáchais ar Shreabhadh Oibre", "actions.runs.graph_jobs_count_1": "%d post", "actions.runs.graph_jobs_count_n": "%d poist", From 134fcced88b054226a0fe2b01f63a9a891e12ef5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 16 Jun 2026 00:07:32 -0700 Subject: [PATCH 05/10] ci: trigger giteabot maintenance on main pushes (#38135) --- .github/workflows/giteabot.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/giteabot.yml b/.github/workflows/giteabot.yml index efa9fe047a..5ffde2e75e 100644 --- a/.github/workflows/giteabot.yml +++ b/.github/workflows/giteabot.yml @@ -1,9 +1,16 @@ name: giteabot on: + # When main advances, rerun merge queue maintenance so the oldest + # reviewed/wait-merge PR can be updated against the new base promptly. + push: + branches: + - main # pull_request_target gives this workflow access to GITEABOT_TOKEN on PRs from # forks, which the bot needs to write labels, statuses and comments. Safe here # because the job only runs a pinned action and never checks out PR HEAD. + # These PR lifecycle events drive label maintenance, queue maintenance, and + # explicit bot actions triggered by relevant label changes. pull_request_target: # zizmor: ignore[dangerous-triggers] types: - opened @@ -13,13 +20,19 @@ on: - closed - review_requested - review_request_removed + # Review events keep review-derived state such as lgtm labels and status checks + # in sync after approvals, edits, or dismissals. pull_request_review: types: - submitted - edited - dismissed + # Periodic maintenance is still useful as a backstop for queue cleanup and + # other housekeeping, even though main pushes now trigger it promptly. schedule: - cron: "15 3 * * *" + # Allow maintainers to rerun selected checks manually when debugging bot + # behavior without waiting for another repository event. workflow_dispatch: inputs: checks: From b7bd222e87fbe4c03294ee1101ce7d29bf30abe5 Mon Sep 17 00:00:00 2001 From: Giteabot Date: Tue, 16 Jun 2026 01:21:09 -0700 Subject: [PATCH 06/10] fix(deps): update npm dependencies (#38137) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [@types/node](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://redirect.github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | [`25.9.2` → `25.9.3`](https://renovatebot.com/diffs/npm/@types%2fnode/25.9.2/25.9.3) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@types%2fnode/25.9.3?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@types%2fnode/25.9.2/25.9.3?slim=true) | | [vue](https://vuejs.org/) ([source](https://redirect.github.com/vuejs/core)) | [`3.5.35` → `3.5.37`](https://renovatebot.com/diffs/npm/vue/3.5.35/3.5.37) | ![age](https://developer.mend.io/api/mc/badges/age/npm/vue/3.5.37?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vue/3.5.35/3.5.37?slim=true) | --- ### Release Notes
vuejs/core (vue) ### [`v3.5.37`](https://redirect.github.com/vuejs/core/blob/HEAD/CHANGELOG.md#3537-2026-06-11) [Compare Source](https://redirect.github.com/vuejs/core/compare/v3.5.36...v3.5.37) ### [`v3.5.36`](https://redirect.github.com/vuejs/core/blob/HEAD/CHANGELOG.md#3536-2026-06-11) [Compare Source](https://redirect.github.com/vuejs/core/compare/v3.5.35...v3.5.36) ##### Bug Fixes - **compiler-core:** avoid crash on CDATA at the document root ([#​14916](https://redirect.github.com/vuejs/core/issues/14916)) ([0ea17e2](https://redirect.github.com/vuejs/core/commit/0ea17e232f9a8f4a9acf57c6addc78cf4f279c13)) - **compiler-core:** prefix dynamic keys on v-memo elements ([#​14922](https://redirect.github.com/vuejs/core/issues/14922)) ([68e978e](https://redirect.github.com/vuejs/core/commit/68e978e3e71a8ae40701808e78966f2168c5907c)), closes [#​14920](https://redirect.github.com/vuejs/core/issues/14920) - **compiler-sfc:** handle vue-ignore on leading intersection/union type ([#​14950](https://redirect.github.com/vuejs/core/issues/14950)) ([0dcd225](https://redirect.github.com/vuejs/core/commit/0dcd225c01f211ebb8ce4dda8c8eac10539abf1a)), closes [#​12254](https://redirect.github.com/vuejs/core/issues/12254) - **compiler-sfc:** respect var hoisting in props destructure ([48ad452](https://redirect.github.com/vuejs/core/commit/48ad452dd61926a59e358da3c74c5ef750ae21c4)) - **reactivity:** preserve watch callback return value when wrapped for `once: true` ([#​14902](https://redirect.github.com/vuejs/core/issues/14902)) ([450a8a8](https://redirect.github.com/vuejs/core/commit/450a8a8e45520f30fe8343c5016a777d888e53fc)) - **runtime-core:** add dev warning for silent catch in compat mode and fix test description typo ([#​14891](https://redirect.github.com/vuejs/core/issues/14891)) ([db3e117](https://redirect.github.com/vuejs/core/commit/db3e117025a7193291ed6676180a5a44bbe0ae76)) - **runtime-core:** force model update when reverted before sync ([#​14897](https://redirect.github.com/vuejs/core/issues/14897)) ([7f76378](https://redirect.github.com/vuejs/core/commit/7f76378b0d178a29113ee07d67faa48b637944e8)), closes [#​13524](https://redirect.github.com/vuejs/core/issues/13524) - **runtime-core:** skip async component callbacks after unmount ([#​14911](https://redirect.github.com/vuejs/core/issues/14911)) ([5300ead](https://redirect.github.com/vuejs/core/commit/5300ead57b3c14942d4c155ef5e485d5409e7f02)) - **transition:** avoid move transition for hidden v-show group children ([#​14895](https://redirect.github.com/vuejs/core/issues/14895)) ([c11f6ee](https://redirect.github.com/vuejs/core/commit/c11f6ee644412edf3eef6736991e895e4a3e1dde)), closes [#​14894](https://redirect.github.com/vuejs/core/issues/14894) - **watch:** trigger immediate callback for empty sources ([#​14914](https://redirect.github.com/vuejs/core/issues/14914)) ([1f2ca7e](https://redirect.github.com/vuejs/core/commit/1f2ca7e4837b1b0de0b91048fffdb03710c0b03e)), closes [#​14898](https://redirect.github.com/vuejs/core/issues/14898)
--- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - Only on Monday (`* * * * 1`) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://redirect.github.com/renovatebot/renovate). --- package.json | 4 +- pnpm-lock.yaml | 194 ++++++++++++++++++++++++------------------------- 2 files changed, 99 insertions(+), 99 deletions(-) diff --git a/package.json b/package.json index ff7f8f1be0..137d5a0783 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "vanilla-colorful": "0.7.2", "vite": "8.0.16", "vite-string-plugin": "2.0.4", - "vue": "3.5.35", + "vue": "3.5.37", "vue-bar-graph": "2.2.0", "vue-chartjs": "5.3.3" }, @@ -83,7 +83,7 @@ "@types/jquery": "4.0.1", "@types/js-yaml": "4.0.9", "@types/katex": "0.16.8", - "@types/node": "25.9.2", + "@types/node": "25.9.3", "@types/pdfobject": "2.2.5", "@types/sortablejs": "1.15.9", "@types/swagger-ui-dist": "3.30.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61615ef6f5..90b34abce4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,7 +94,7 @@ importers: version: 2.6.2 '@vitejs/plugin-vue': specifier: 6.0.7 - version: 6.0.7(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0))(vue@3.5.35(typescript@6.0.3)) + version: 6.0.7(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0))(vue@3.5.37(typescript@6.0.3)) ansi_up: specifier: 6.0.6 version: 6.0.6 @@ -193,19 +193,19 @@ importers: version: 0.7.2 vite: specifier: 8.0.16 - version: 8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0) + version: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0) vite-string-plugin: specifier: 2.0.4 - version: 2.0.4(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)) + version: 2.0.4(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)) vue: - specifier: 3.5.35 - version: 3.5.35(typescript@6.0.3) + specifier: 3.5.37 + version: 3.5.37(typescript@6.0.3) vue-bar-graph: specifier: 2.2.0 version: 2.2.0(typescript@6.0.3) vue-chartjs: specifier: 5.3.3 - version: 5.3.3(chart.js@4.5.1)(vue@3.5.35(typescript@6.0.3)) + version: 5.3.3(chart.js@4.5.1)(vue@3.5.37(typescript@6.0.3)) devDependencies: '@eslint-community/eslint-plugin-eslint-comments': specifier: 4.7.2 @@ -235,8 +235,8 @@ importers: specifier: 0.16.8 version: 0.16.8 '@types/node': - specifier: 25.9.2 - version: 25.9.2 + specifier: 25.9.3 + version: 25.9.3 '@types/pdfobject': specifier: 2.2.5 version: 2.2.5 @@ -257,7 +257,7 @@ importers: version: 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) '@vitest/eslint-plugin': specifier: 1.6.20 - version: 1.6.20(@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.8(@types/node@25.9.2)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0))) + version: 1.6.20(@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.8(@types/node@25.9.3)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0))) eslint: specifier: 10.4.1 version: 10.4.1(jiti@2.7.0) @@ -347,7 +347,7 @@ importers: version: 17.18.0 vitest: specifier: 4.1.8 - version: 4.1.8(@types/node@25.9.2)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)) + version: 4.1.8(@types/node@25.9.3)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)) vue-tsc: specifier: 3.3.4 version: 3.3.4(typescript@6.0.3) @@ -1359,8 +1359,8 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@25.9.2': - resolution: {integrity: sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==} + '@types/node@25.9.3': + resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} '@types/pdfobject@2.2.5': resolution: {integrity: sha512-7gD5tqc/RUDq0PyoLemL0vEHxBYi+zY0WVaFAx/Y0jBsXFgot1vB9No1GhDZGwRGJMCIZbgAb74QG9MTyTNU/g==} @@ -1647,37 +1647,37 @@ packages: '@volar/typescript@2.4.28': resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} - '@vue/compiler-core@3.5.35': - resolution: {integrity: sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==} + '@vue/compiler-core@3.5.37': + resolution: {integrity: sha512-TfQz4bsBQTPoTeBWTUPJPq+4FCTTXg2pbp8TjjAyrGaLAu9nfrZTxKLf6mdAlclnwtyUToFaMQu7QRS63Qek1g==} - '@vue/compiler-dom@3.5.35': - resolution: {integrity: sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==} + '@vue/compiler-dom@3.5.37': + resolution: {integrity: sha512-oqfl/QaCVEWxphALFEZ7m+q9z+Sghz9ZCcoJ/oplTGxsOgx2czVzSZxkMkzQrWIahywOeyGHdg9ml/WUz3DMzw==} - '@vue/compiler-sfc@3.5.35': - resolution: {integrity: sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==} + '@vue/compiler-sfc@3.5.37': + resolution: {integrity: sha512-hYu+efs678xaPHYxhFRK3ZhkQ/FueMVnROooZqemOYlyQBQg06qkIrpyAUrUWWqMLfifgOdWwU6CL6FuTRvP4A==} - '@vue/compiler-ssr@3.5.35': - resolution: {integrity: sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==} + '@vue/compiler-ssr@3.5.37': + resolution: {integrity: sha512-ihbdCLJLXFKV3efEQlFfzy6TLHRuOQ/+dze2vZfg0DIncVxkcUxwCqPewCiSVdWXFeoiuMMON87wpt+G+yT22A==} '@vue/language-core@3.3.4': resolution: {integrity: sha512-IuHqQ5zGGOE7CXP72VX6A42IVeIzYv4WAhO6arej11TRNqtdZfGyH8Yr2FOCaDX0dSQG+JwULLoFHGY1igYVjQ==} - '@vue/reactivity@3.5.35': - resolution: {integrity: sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==} + '@vue/reactivity@3.5.37': + resolution: {integrity: sha512-M7j7YF68IUd2uFNIqhwybpzUG/Sk9HUtk+ULmC+g6JeZ80LyCyGnjv6SYBR86t3fyyuYlZUSb18yu4UYLgw5jg==} - '@vue/runtime-core@3.5.35': - resolution: {integrity: sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==} + '@vue/runtime-core@3.5.37': + resolution: {integrity: sha512-N0IWRirNPzJp/DuUCR9M+obVUHZArMkmldRApKsJRIWA+XDO6iwF4Zh94HP6uCzYVgWVwr8YuKeWF4H52VxTbw==} - '@vue/runtime-dom@3.5.35': - resolution: {integrity: sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==} + '@vue/runtime-dom@3.5.37': + resolution: {integrity: sha512-9VkutCFwfVOiMRH7mgi7QapsqC8Hxcow3DLvBKf+mRH88P94Ib/D/u6l/ln62ST+fIvmsOO7+Db99LzWv9slJg==} - '@vue/server-renderer@3.5.35': - resolution: {integrity: sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==} + '@vue/server-renderer@3.5.37': + resolution: {integrity: sha512-CP7nbxJb1Zc0/oeBqu6CtMf9TN64fz6qE75BZ8mWh04zAEya8EAZmqH2gRQWkoUUjFqv9i7h/mV+E4/LL4JXXw==} peerDependencies: - vue: 3.5.35 + vue: 3.5.37 - '@vue/shared@3.5.35': - resolution: {integrity: sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==} + '@vue/shared@3.5.37': + resolution: {integrity: sha512-JzFx4aYGz+EtBl8zzw8XECNWSmNXTrU5jpub3T1lQ+2X5Ys9QzHWafxhE+E5qS2Ry/mVl8o2QqrwRGYYOFYguw==} abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} @@ -4841,8 +4841,8 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue@3.5.35: - resolution: {integrity: sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==} + vue@3.5.37: + resolution: {integrity: sha512-So4bMq165gsD4+hDVC1/bcbsOpTlUFGGkpuH2sx9vCflChIWahy4C0K4ZcX/COe0ad1IToIRT3VQz0dl4XPihg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -5542,14 +5542,14 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 25.9.2 + '@types/node': 25.9.3 jest-mock: 29.7.0 '@jest/fake-timers@29.7.0': dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 25.9.2 + '@types/node': 25.9.3 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -5563,7 +5563,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.9.2 + '@types/node': 25.9.3 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -6060,7 +6060,7 @@ snapshots: '@types/jsdom@20.0.1': dependencies: - '@types/node': 25.9.2 + '@types/node': 25.9.3 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -6074,7 +6074,7 @@ snapshots: '@types/ms@2.1.0': {} - '@types/node@25.9.2': + '@types/node@25.9.3': dependencies: undici-types: 7.24.6 @@ -6105,7 +6105,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.2 + '@types/node': 25.9.3 '@types/yargs-parser@21.0.3': {} @@ -6358,13 +6358,13 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0))(vue@3.5.35(typescript@6.0.3))': + '@vitejs/plugin-vue@6.0.7(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0))(vue@3.5.37(typescript@6.0.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0) - vue: 3.5.35(typescript@6.0.3) + vite: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0) + vue: 3.5.37(typescript@6.0.3) - '@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.8(@types/node@25.9.2)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)))': + '@vitest/eslint-plugin@1.6.20(@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3)(vitest@4.1.8(@types/node@25.9.3)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)))': dependencies: '@typescript-eslint/scope-manager': 8.61.0 '@typescript-eslint/utils': 8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) @@ -6372,7 +6372,7 @@ snapshots: optionalDependencies: '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3))(eslint@10.4.1(jiti@2.7.0))(typescript@6.0.3) typescript: 6.0.3 - vitest: 4.1.8(@types/node@25.9.2)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)) + vitest: 4.1.8(@types/node@25.9.3)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)) transitivePeerDependencies: - supports-color @@ -6385,13 +6385,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0))': + '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0) '@vitest/pretty-format@4.1.8': dependencies: @@ -6429,69 +6429,69 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.1.0 - '@vue/compiler-core@3.5.35': + '@vue/compiler-core@3.5.37': dependencies: '@babel/parser': 7.29.7 - '@vue/shared': 3.5.35 + '@vue/shared': 3.5.37 entities: 7.0.1 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.35': + '@vue/compiler-dom@3.5.37': dependencies: - '@vue/compiler-core': 3.5.35 - '@vue/shared': 3.5.35 + '@vue/compiler-core': 3.5.37 + '@vue/shared': 3.5.37 - '@vue/compiler-sfc@3.5.35': + '@vue/compiler-sfc@3.5.37': dependencies: '@babel/parser': 7.29.7 - '@vue/compiler-core': 3.5.35 - '@vue/compiler-dom': 3.5.35 - '@vue/compiler-ssr': 3.5.35 - '@vue/shared': 3.5.35 + '@vue/compiler-core': 3.5.37 + '@vue/compiler-dom': 3.5.37 + '@vue/compiler-ssr': 3.5.37 + '@vue/shared': 3.5.37 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.15 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.5.35': + '@vue/compiler-ssr@3.5.37': dependencies: - '@vue/compiler-dom': 3.5.35 - '@vue/shared': 3.5.35 + '@vue/compiler-dom': 3.5.37 + '@vue/shared': 3.5.37 '@vue/language-core@3.3.4': dependencies: '@volar/language-core': 2.4.28 - '@vue/compiler-dom': 3.5.35 - '@vue/shared': 3.5.35 + '@vue/compiler-dom': 3.5.37 + '@vue/shared': 3.5.37 alien-signals: 3.2.1 muggle-string: 0.4.1 path-browserify: 1.0.1 picomatch: 4.0.4 - '@vue/reactivity@3.5.35': + '@vue/reactivity@3.5.37': dependencies: - '@vue/shared': 3.5.35 + '@vue/shared': 3.5.37 - '@vue/runtime-core@3.5.35': + '@vue/runtime-core@3.5.37': dependencies: - '@vue/reactivity': 3.5.35 - '@vue/shared': 3.5.35 + '@vue/reactivity': 3.5.37 + '@vue/shared': 3.5.37 - '@vue/runtime-dom@3.5.35': + '@vue/runtime-dom@3.5.37': dependencies: - '@vue/reactivity': 3.5.35 - '@vue/runtime-core': 3.5.35 - '@vue/shared': 3.5.35 + '@vue/reactivity': 3.5.37 + '@vue/runtime-core': 3.5.37 + '@vue/shared': 3.5.37 csstype: 3.2.3 - '@vue/server-renderer@3.5.35(vue@3.5.35(typescript@6.0.3))': + '@vue/server-renderer@3.5.37(vue@3.5.37(typescript@6.0.3))': dependencies: - '@vue/compiler-ssr': 3.5.35 - '@vue/shared': 3.5.35 - vue: 3.5.35(typescript@6.0.3) + '@vue/compiler-ssr': 3.5.37 + '@vue/shared': 3.5.37 + vue: 3.5.37(typescript@6.0.3) - '@vue/shared@3.5.35': {} + '@vue/shared@3.5.37': {} abab@2.0.6: {} @@ -6700,7 +6700,7 @@ snapshots: buffer-image-size@0.6.4: dependencies: - '@types/node': 25.9.2 + '@types/node': 25.9.3 buffer@5.7.1: dependencies: @@ -8049,7 +8049,7 @@ snapshots: happy-dom@20.10.2: dependencies: - '@types/node': 25.9.2 + '@types/node': 25.9.3 '@types/whatwg-mimetype': 3.0.2 '@types/ws': 8.18.1 buffer-image-size: 0.6.4 @@ -8365,7 +8365,7 @@ snapshots: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 '@types/jsdom': 20.0.1 - '@types/node': 25.9.2 + '@types/node': 25.9.3 jest-mock: 29.7.0 jest-util: 29.7.0 jsdom: 20.0.3 @@ -8389,13 +8389,13 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.9.2 + '@types/node': 25.9.3 jest-util: 29.7.0 jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 25.9.2 + '@types/node': 25.9.3 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -10019,11 +10019,11 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite-string-plugin@2.0.4(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)): + vite-string-plugin@2.0.4(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)): dependencies: - vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0) - vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0): + vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 @@ -10031,15 +10031,15 @@ snapshots: rolldown: 1.0.3 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.9.2 + '@types/node': 25.9.3 esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 - vitest@4.1.8(@types/node@25.9.2)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)): + vitest@4.1.8(@types/node@25.9.3)(happy-dom@20.10.2)(jsdom@20.0.3)(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)) + '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -10056,10 +10056,10 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.0.16(@types/node@25.9.3)(esbuild@0.28.1)(jiti@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.9.2 + '@types/node': 25.9.3 happy-dom: 20.10.2 jsdom: 20.0.3 transitivePeerDependencies: @@ -10069,14 +10069,14 @@ snapshots: vue-bar-graph@2.2.0(typescript@6.0.3): dependencies: - vue: 3.5.35(typescript@6.0.3) + vue: 3.5.37(typescript@6.0.3) transitivePeerDependencies: - typescript - vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.35(typescript@6.0.3)): + vue-chartjs@5.3.3(chart.js@4.5.1)(vue@3.5.37(typescript@6.0.3)): dependencies: chart.js: 4.5.1 - vue: 3.5.35(typescript@6.0.3) + vue: 3.5.37(typescript@6.0.3) vue-eslint-parser@10.4.0(eslint@10.4.1(jiti@2.7.0)): dependencies: @@ -10096,13 +10096,13 @@ snapshots: '@vue/language-core': 3.3.4 typescript: 6.0.3 - vue@3.5.35(typescript@6.0.3): + vue@3.5.37(typescript@6.0.3): dependencies: - '@vue/compiler-dom': 3.5.35 - '@vue/compiler-sfc': 3.5.35 - '@vue/runtime-dom': 3.5.35 - '@vue/server-renderer': 3.5.35(vue@3.5.35(typescript@6.0.3)) - '@vue/shared': 3.5.35 + '@vue/compiler-dom': 3.5.37 + '@vue/compiler-sfc': 3.5.37 + '@vue/runtime-dom': 3.5.37 + '@vue/server-renderer': 3.5.37(vue@3.5.37(typescript@6.0.3)) + '@vue/shared': 3.5.37 optionalDependencies: typescript: 6.0.3 From 0be7543560da892e246ad08f4aad2825fc9117ae Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 16 Jun 2026 10:40:13 -0700 Subject: [PATCH 07/10] fix(mssql): expand legacy issue and comment long-text columns (#38120) ## Summary This fixes pull request creation failures on upgraded MSSQL instances where legacy `issue` and `comment` long-text columns are still limited to `nvarchar(4000)`. When a PR is created, Gitea stores a pull request push timeline comment containing JSON with `commit_ids`. For PRs with many commits, that payload can exceed 4000 characters and MSSQL rejects the insert with: > String or binary data would be truncated in table 'comment', column 'content' This change adds a migration that expands the affected legacy MSSQL columns to `NVARCHAR(MAX)`. The previous migration in models/migrations/v1_16/v191.go only applies to MySQL, not MSSQL. migration now skips columns already using NVARCHAR(MAX) / VARCHAR(MAX) Closes #37893 ## Changes - add migration `338` for MSSQL-only long-text expansion - expand: - `issue.content` - `comment.content` - `comment.patch` - add an MSSQL regression test that starts from a legacy `VARCHAR(4000)` schema and verifies inserts larger than 4000 characters succeed after migration ## Why this approach The current model already declares these fields as `LONGTEXT`, so the bug is caused by stale upgraded MSSQL schemas rather than by PR creation logic itself. Fixing the schema is the smallest and safest change, and also prevents similar truncation issues for other long issue/comment content. --- models/migrations/migrations.go | 1 + models/migrations/v1_27/v338.go | 73 ++++++++++++++++++++++++++++ models/migrations/v1_27/v338_test.go | 52 ++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 models/migrations/v1_27/v338.go create mode 100644 models/migrations/v1_27/v338_test.go diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 81a618a207..be7b333a2e 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -415,6 +415,7 @@ func prepareMigrationTasks() []*migration { newMigration(335, "Add reusable workflow fields and action_run_attempt_job_id_index table for ActionRunJob", v1_27.AddReusableWorkflowFieldsToActionRunJob), newMigration(336, "Add ActionRunJobSummary table", v1_27.AddActionRunJobSummaryTable), newMigration(337, "Add visibility to team", v1_27.AddVisibilityToTeam), + newMigration(338, "Expand legacy MSSQL issue/comment long-text columns", v1_27.ExpandIssueAndCommentLongTextFieldsForMSSQL), } return preparedMigrations } diff --git a/models/migrations/v1_27/v338.go b/models/migrations/v1_27/v338.go new file mode 100644 index 0000000000..a495907174 --- /dev/null +++ b/models/migrations/v1_27/v338.go @@ -0,0 +1,73 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_27 + +import ( + "fmt" + "strings" + + "gitea.dev/models/db" + "gitea.dev/models/migrations/base" + + "xorm.io/xorm/schemas" +) + +type issueWithLongTextContent struct { + Content string `xorm:"LONGTEXT"` +} + +func (issueWithLongTextContent) TableName() string { + return "issue" +} + +type commentWithLongTextFields struct { + Content string `xorm:"LONGTEXT"` + PatchQuoted string `xorm:"LONGTEXT patch"` +} + +func (commentWithLongTextFields) TableName() string { + return "comment" +} + +func isMSSQLMaxTextColumn(column *schemas.Column) bool { + if column.Length != -1 { + return false + } + return strings.EqualFold(column.SQLType.Name, schemas.Varchar) || strings.EqualFold(column.SQLType.Name, schemas.NVarchar) +} + +func modifyLongTextColumnsForMSSQL(x db.EngineMigration, bean any, columnNames ...string) error { + table, err := x.TableInfo(bean) + if err != nil { + return err + } + + for _, columnName := range columnNames { + column := table.GetColumn(columnName) + if column == nil { + return fmt.Errorf("column %s does not exist in table %s", columnName, table.Name) + } + if isMSSQLMaxTextColumn(column) { + continue + } + if err := base.ModifyColumn(x, table.Name, column); err != nil { + return fmt.Errorf("modify %s.%s: %w", table.Name, columnName, err) + } + } + + return nil +} + +// ExpandIssueAndCommentLongTextFieldsForMSSQL expands legacy MSSQL nvarchar(4000) +// columns to nvarchar(max) so PR push comments and long issue content are not truncated. +func ExpandIssueAndCommentLongTextFieldsForMSSQL(x db.EngineMigration) error { + if x.Dialect().URI().DBType != schemas.MSSQL { + return nil + } + + if err := modifyLongTextColumnsForMSSQL(x, new(issueWithLongTextContent), "content"); err != nil { + return err + } + return modifyLongTextColumnsForMSSQL(x, new(commentWithLongTextFields), "content", "patch") +} diff --git a/models/migrations/v1_27/v338_test.go b/models/migrations/v1_27/v338_test.go new file mode 100644 index 0000000000..4e47253779 --- /dev/null +++ b/models/migrations/v1_27/v338_test.go @@ -0,0 +1,52 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_27 + +import ( + "strings" + "testing" + + "gitea.dev/models/migrations/migrationtest" + "gitea.dev/modules/setting" + + "github.com/stretchr/testify/require" +) + +type issueBeforeLongTextMSSQLMigration struct { + ID int64 `xorm:"pk autoincr"` + Content string `xorm:"VARCHAR(4000)"` +} + +func (issueBeforeLongTextMSSQLMigration) TableName() string { + return "issue" +} + +type commentBeforeLongTextMSSQLMigration struct { + ID int64 `xorm:"pk autoincr"` + Content string `xorm:"VARCHAR(4000)"` + Patch string `xorm:"VARCHAR(4000) patch"` +} + +func (commentBeforeLongTextMSSQLMigration) TableName() string { + return "comment" +} + +func Test_ExpandIssueAndCommentLongTextFieldsForMSSQL(t *testing.T) { + if !setting.Database.Type.IsMSSQL() { + t.Skip("Only MSSQL needs to expand legacy nvarchar(4000) long-text columns") + } + + x, deferrable := migrationtest.PrepareTestEnv(t, 0, new(issueBeforeLongTextMSSQLMigration), new(commentBeforeLongTextMSSQLMigration)) + defer deferrable() + + require.NoError(t, ExpandIssueAndCommentLongTextFieldsForMSSQL(x)) + require.NoError(t, ExpandIssueAndCommentLongTextFieldsForMSSQL(x)) + + longText := strings.Repeat("x", 5000) + _, err := x.Insert(&issueBeforeLongTextMSSQLMigration{Content: longText}) + require.NoError(t, err) + + _, err = x.Insert(&commentBeforeLongTextMSSQLMigration{Content: longText, Patch: longText}) + require.NoError(t, err) +} From 795531cea0644dd2d0ad5eddc9e56e70d3eb1db0 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Wed, 17 Jun 2026 01:24:58 +0000 Subject: [PATCH 08/10] [skip ci] Updated translations via Crowdin --- options/locale/locale_zh-CN.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/options/locale/locale_zh-CN.json b/options/locale/locale_zh-CN.json index 03c6e417ce..7c3d45f528 100644 --- a/options/locale/locale_zh-CN.json +++ b/options/locale/locale_zh-CN.json @@ -2865,6 +2865,14 @@ "org.teams.all_repositories_read_permission_desc": "此团队授予读取所有仓库的访问权限: 成员可以查看和克隆仓库。", "org.teams.all_repositories_write_permission_desc": "此团队授予修改所有仓库的访问权限: 成员可以查看和推送至仓库。", "org.teams.all_repositories_admin_permission_desc": "该团队拥有 管理 所有仓库的权限:团队成员可以读取、克隆、推送以及添加其它仓库协作者。", + "org.teams.visibility": "可见性", + "org.teams.visibility_private": "私有", + "org.teams.visibility_private_helper": "仅对团队成员和组织所有者可见。", + "org.teams.visibility_limited": "受限", + "org.teams.visibility_limited_helper": "对组织所有成员可见。", + "org.teams.visibility_public": "公开", + "org.teams.visibility_public_helper": "对任何登录用户可见。", + "org.teams.owners_visibility_fixed": "所有者的团队可见性无法更改。", "org.teams.invite.title": "您已被邀请加入组织 %s 中的团队 %s。", "org.teams.invite.by": "邀请人 %s", "org.teams.invite.description": "请点击下面的按钮加入团队。", From 9e84deb969aff5c1115c2984e41250f28c78451f Mon Sep 17 00:00:00 2001 From: bircni Date: Wed, 17 Jun 2026 06:50:25 +0200 Subject: [PATCH 09/10] fix: Various sec fixes 2 (#38108) - Enforce repository token scope on RSS/Atom feed endpoints so a PAT without repo scope can no longer read private repo commit data. - Block HTTP redirects during repository migration clones to prevent SSRF reaching internal addresses via an attacker-controlled redirect. - Redact the notification subject after repo access is revoked so private issue/PR metadata is no longer leaked through the notification API. --------- Co-authored-by: Lunny Xiao --- modules/git/repo.go | 3 +++ modules/git/repo_test.go | 23 ++++++++++++++++ routers/web/feed/branch.go | 3 +++ routers/web/feed/file.go | 3 +++ routers/web/feed/release.go | 3 +++ routers/web/feed/render.go | 9 +++++++ routers/web/feed/repo.go | 3 +++ services/convert/notification.go | 29 +++++++++++--------- services/convert/notification_test.go | 30 +++++++++++++++++++++ tests/integration/feed_repo_test.go | 39 +++++++++++++++++++++++++++ 10 files changed, 132 insertions(+), 13 deletions(-) diff --git a/modules/git/repo.go b/modules/git/repo.go index e17e7e1460..289033332b 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -121,6 +121,9 @@ func Clone(ctx context.Context, from, to string, opts CloneRepoOptions) error { } cmd := gitcmd.NewCommand().AddArguments("clone") + // Never follow HTTP redirects: no clone caller needs them, and a remote redirecting to an + // otherwise-blocked address would be an SSRF vector (e.g. migrating from an attacker URL). + cmd.AddArguments("-c", "http.followRedirects=false") if opts.SkipTLSVerify { cmd.AddArguments("-c", "http.sslVerify=false") } diff --git a/modules/git/repo_test.go b/modules/git/repo_test.go index 776c297a34..be0a21a83d 100644 --- a/modules/git/repo_test.go +++ b/modules/git/repo_test.go @@ -4,7 +4,10 @@ package git import ( + "net/http" + "net/http/httptest" "path/filepath" + "sync/atomic" "testing" "github.com/stretchr/testify/assert" @@ -19,3 +22,23 @@ func TestRepoIsEmpty(t *testing.T) { assert.NoError(t, err) assert.True(t, isEmpty) } + +// TestCloneRefusesRedirects ensures Clone never follows HTTP redirects, so a remote +// cannot redirect to an otherwise-blocked address (SSRF, e.g. during migration). +func TestCloneRefusesRedirects(t *testing.T) { + var targetHit atomic.Bool + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + targetHit.Store(true) + w.WriteHeader(http.StatusNotFound) + })) + defer target.Close() + + redirect := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, target.URL+r.URL.Path, http.StatusFound) + })) + defer redirect.Close() + + err := Clone(t.Context(), redirect.URL, filepath.Join(t.TempDir(), "dst"), CloneRepoOptions{}) + assert.Error(t, err) + assert.False(t, targetHit.Load(), "git must not follow the redirect to the target") +} diff --git a/routers/web/feed/branch.go b/routers/web/feed/branch.go index e7a1cbbe4f..139b7ebc43 100644 --- a/routers/web/feed/branch.go +++ b/routers/web/feed/branch.go @@ -15,6 +15,9 @@ import ( // ShowBranchFeed shows tags and/or releases on the repo as RSS / Atom feed func ShowBranchFeed(ctx *context.Context, repo *repo.Repository, formatType string) { + if !checkRepoFeedTokenScope(ctx) { + return + } var commits []*git.Commit var err error if ctx.Repo.Commit != nil { diff --git a/routers/web/feed/file.go b/routers/web/feed/file.go index f115521928..fa590dfcf0 100644 --- a/routers/web/feed/file.go +++ b/routers/web/feed/file.go @@ -16,6 +16,9 @@ import ( // ShowFileFeed shows tags and/or releases on the repo as RSS / Atom feed func ShowFileFeed(ctx *context.Context, repo *repo.Repository, formatType string) { + if !checkRepoFeedTokenScope(ctx) { + return + } fileName := ctx.Repo.TreePath if len(fileName) == 0 { return diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go index 6767c502ff..274eb0cd8a 100644 --- a/routers/web/feed/release.go +++ b/routers/web/feed/release.go @@ -15,6 +15,9 @@ import ( // shows tags and/or releases on the repo as RSS / Atom feed func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleasesOnly bool, formatType string) { + if !checkRepoFeedTokenScope(ctx) { + return + } releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ IncludeTags: !isReleasesOnly, RepoID: ctx.Repo.Repository.ID, diff --git a/routers/web/feed/render.go b/routers/web/feed/render.go index 21ae03a595..d06aa22e64 100644 --- a/routers/web/feed/render.go +++ b/routers/web/feed/render.go @@ -4,9 +4,18 @@ package feed import ( + auth_model "gitea.dev/models/auth" "gitea.dev/services/context" ) +// checkRepoFeedTokenScope ensures an API token has repository read scope before a +// feed serves private repository content, mirroring checkDownloadTokenScope for +// downloads. Returns false (and writes the response) when the token is denied. +func checkRepoFeedTokenScope(ctx *context.Context) bool { + context.CheckRepoScopedToken(ctx, ctx.Repo.Repository, auth_model.Read) + return !ctx.Written() +} + // RenderBranchFeed render format for branch or file func RenderBranchFeed(ctx *context.Context, feedType string) { if ctx.Repo.TreePath == "" { diff --git a/routers/web/feed/repo.go b/routers/web/feed/repo.go index 8c8946eec1..2986e00bb6 100644 --- a/routers/web/feed/repo.go +++ b/routers/web/feed/repo.go @@ -16,6 +16,9 @@ import ( // ShowRepoFeed shows user activity on the repo as RSS / Atom feed func ShowRepoFeed(ctx *context.Context, repo *repo_model.Repository, formatType string) { + if !checkRepoFeedTokenScope(ctx) { + return + } actions, _, err := feed_service.GetFeeds(ctx, activities_model.GetFeedsOptions{ RequestedRepo: repo, Actor: ctx.Doer, diff --git a/services/convert/notification.go b/services/convert/notification.go index 7ddd8ea5d1..fee1de4074 100644 --- a/services/convert/notification.go +++ b/services/convert/notification.go @@ -24,19 +24,22 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification) } // since user only get notifications when he has access to use minimal access mode - if n.Repository != nil { - perm, err := access_model.GetIndividualUserRepoPermission(ctx, n.Repository, n.User) - if err != nil { - log.Error("GetIndividualUserRepoPermission failed: %v", err) - return result - } - if perm.HasAnyUnitAccessOrPublicAccess() { // if user has been revoked access to repo, do not show repo info - result.Repository = ToRepo(ctx, n.Repository, perm) - // This permission is not correct and we should not be reporting it - for repository := result.Repository; repository != nil; repository = repository.Parent { - repository.Permissions = nil - } - } + if n.Repository == nil { + return result + } + perm, err := access_model.GetIndividualUserRepoPermission(ctx, n.Repository, n.User) + if err != nil { + log.Error("GetIndividualUserRepoPermission failed: %v", err) + return result + } + // if the user has been revoked access to the repo, do not leak repo or subject info + if !perm.HasAnyUnitAccessOrPublicAccess() { + return result + } + result.Repository = ToRepo(ctx, n.Repository, perm) + // This permission is not correct and we should not be reporting it + for repository := result.Repository; repository != nil; repository = repository.Parent { + repository.Permissions = nil } // handle Subject diff --git a/services/convert/notification_test.go b/services/convert/notification_test.go index 5718258672..277ee83ec2 100644 --- a/services/convert/notification_test.go +++ b/services/convert/notification_test.go @@ -39,6 +39,36 @@ func TestToNotificationThreadOmitsRepoWhenAccessRevoked(t *testing.T) { assert.Nil(t, thread.Repository) } +func TestToNotificationThreadOmitsSubjectWhenAccessRevoked(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + ctx := t.Context() + // repo 2 is private; user 4 has no access to it + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + assert.NoError(t, repo.LoadOwner(ctx)) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4, RepoID: repo.ID}) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + + n := &activities_model.Notification{ + ID: 12345, + UserID: user.ID, + RepoID: repo.ID, + Status: activities_model.NotificationStatusUnread, + Source: activities_model.NotificationSourceIssue, + IssueID: issue.ID, + UpdatedUnix: timeutil.TimeStampNow(), + Issue: issue, + Repository: repo, + User: user, + } + + thread := ToNotificationThread(ctx, n) + + // must not leak private issue metadata once access is revoked + assert.Nil(t, thread.Repository) + assert.Nil(t, thread.Subject) +} + func TestToNotificationThread(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/tests/integration/feed_repo_test.go b/tests/integration/feed_repo_test.go index 59189bf070..ffe908c74b 100644 --- a/tests/integration/feed_repo_test.go +++ b/tests/integration/feed_repo_test.go @@ -8,6 +8,7 @@ import ( "net/http" "testing" + auth_model "gitea.dev/models/auth" "gitea.dev/tests" "github.com/stretchr/testify/assert" @@ -33,3 +34,41 @@ func TestFeedRepo(t *testing.T) { assert.NotEmpty(t, rss.Channel.Items[0].PubDate) }) } + +// TestFeedRepoContentTokenScopes ensures repository feed endpoints enforce the +// repository token scope, so a PAT without repository scope cannot read private +// repository commit/activity data through RSS/Atom feeds. +func TestFeedRepoContentTokenScopes(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // user2/repo2 is a private repository owned by user2 + ownerReadToken := getUserToken(t, "user2", auth_model.AccessTokenScopeReadRepository) + miscToken := getUserToken(t, "user2", auth_model.AccessTokenScopeReadMisc) + + urls := []string{ + "/user2/repo2.rss", + "/user2/repo2.atom", + "/user2/repo2/rss/branch/master", + "/user2/repo2/atom/branch/master", + "/user2/repo2/rss/branch/master/README.md", + "/user2/repo2/tags.rss", + "/user2/repo2/tags.atom", + "/user2/repo2/releases.rss", + "/user2/repo2/releases.atom", + } + + for _, url := range urls { + t.Run(url, func(t *testing.T) { + // feed routes only accept basic auth, so authenticate as the advisory PoC does (user:token) + reqDenied := NewRequest(t, "GET", url) + reqDenied.SetBasicAuth("user2", miscToken) + // a token without repository scope must be denied + MakeRequest(t, reqDenied, http.StatusForbidden) + + reqAllowed := NewRequest(t, "GET", url) + reqAllowed.SetBasicAuth("user2", ownerReadToken) + // a token with repository read scope is allowed + MakeRequest(t, reqAllowed, http.StatusOK) + }) + } +} From c68925152b1b6c8f92806cdbda9c4672dcc1608f Mon Sep 17 00:00:00 2001 From: bircni Date: Wed, 17 Jun 2026 08:39:22 +0200 Subject: [PATCH 10/10] docs: add development setup guide (#37960) Moves the "Hacking on Gitea" page out of the documentation website and into the repository as `docs/development.md`, so contributors find build and test instructions next to the code. The content has been cleaned up and corrected for in-repo use. --------- Signed-off-by: bircni Co-authored-by: wxiaoguang Co-authored-by: Lunny Xiao --- CONTRIBUTING.md | 50 +++---- README.md | 53 ++++---- contrib/development/README.md | 4 +- docs/build-setup.md | 67 ++++++++++ docs/build-source.md | 92 +++++++++++++ docs/development.md | 141 ++++++++++++++++++++ docs/guideline-backend.md | 63 --------- docs/guideline-frontend.md | 17 --- docs/guidelines-backend.md | 132 +++++++++++++++++++ docs/guidelines-frontend.md | 98 ++++++++++++++ docs/guidelines-refactoring.md | 38 ++++++ docs/testing.md | 151 ++++++++++++++++++++++ tests/integration/README.md | 94 -------------- tests/integration/api_org_avatar_test.go | 8 +- tests/integration/api_repo_avatar_test.go | 8 +- tests/integration/api_user_avatar_test.go | 8 +- 16 files changed, 762 insertions(+), 262 deletions(-) create mode 100644 docs/build-setup.md create mode 100644 docs/build-source.md create mode 100644 docs/development.md delete mode 100644 docs/guideline-backend.md delete mode 100644 docs/guideline-frontend.md create mode 100644 docs/guidelines-backend.md create mode 100644 docs/guidelines-frontend.md create mode 100644 docs/guidelines-refactoring.md create mode 100644 docs/testing.md delete mode 100644 tests/integration/README.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6888b4ad65..71087d3446 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,12 +2,17 @@ This document explains how to contribute changes to the Gitea project. Topic-specific guides live in separate files so the essentials are easier to find. -| Topic | Document | -| :---- | :------- | -| Backend (Go modules, API v1) | [docs/guideline-backend.md](docs/guideline-backend.md) | -| Frontend (npm, UI guidelines) | [docs/guideline-frontend.md](docs/guideline-frontend.md) | -| Maintainers, TOC, labels, merge queue, commit format for mergers | [docs/community-governance.md](docs/community-governance.md) | -| Release cycle, backports, tagging releases | [docs/release-management.md](docs/release-management.md) | +| Topic | Document | +|:-----------------------|:-----------------------------------------------------------------| +| Setup and requirements | [docs/build-setup.md](docs/build-setup.md) | +| Development workflow | [docs/development.md](docs/development.md) | +| Build from source | [docs/build-source.md](docs/build-source.md) | +| Running the tests | [docs/testing.md](docs/testing.md) | +| Frontend guidelines | [docs/guidelines-frontend.md](docs/guidelines-frontend.md) | +| Backend guidelines | [docs/guidelines-backend.md](docs/guidelines-backend.md) | +| Refactoring | [docs/guidelines-refactoring.md](docs/guidelines-refactoring.md) | +| Community Governance | [docs/community-governance.md](docs/community-governance.md) | +| Release management | [docs/release-management.md](docs/release-management.md) |
Table of Contents @@ -43,7 +48,7 @@ This document explains how to contribute changes to the Gitea project. Topic-spe It assumes you have followed the [installation instructions](https://docs.gitea.com/category/installation). \ Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io). -For configuring IDEs for Gitea development, see the [contributed IDE configurations](contrib/ide/). +For configuring IDEs for Gitea development, see the [IDE setup notes](docs/development.md#ide-configuration) and the [contributed configurations](contrib/development/). ## AI Contribution Policy @@ -106,7 +111,8 @@ If further discussion is needed, we encourage you to open a new issue instead an ## Building Gitea -See the [development setup instructions](https://docs.gitea.com/development/hacking-on-gitea). +See [docs/setup.md](docs/setup.md) for prerequisites and [docs/development.md](docs/development.md) +for building Gitea and the development workflow. ## Styleguide @@ -125,33 +131,7 @@ Afterwards, copyright should only be modified when the copyright author changes. ## Testing -Before submitting a pull request, run all tests to make sure your changes don't cause a regression elsewhere. - -Here's how to run the test suite: - -- code lint - -| | | -| :-------------------- | :--------------------------------------------------------------------------- | -|``make lint`` | lint everything (not needed if you only change the front- **or** backend) | -|``make lint-frontend`` | lint frontend files | -|``make lint-backend`` | lint backend files | - -- run tests (we suggest running them on Linux) - -| Command | Action | | -|:----------------------------------------------|:-----------------------------------------------------| ------------------------------------------- | -| ``make test-backend[\#SpecificTestName]`` | run unit test(s) | | -| ``make test-integration[\#SpecificTestName]`` | run [integration](tests/integration) test(s) | [More details](tests/integration/README.md) | -| ``make test-e2e`` | run [end-to-end](tests/e2e) test(s) using Playwright | | - -- E2E test environment variables - -| Variable | Description | -| :-------------------------------- | :---------------------------------------------------------- | -| ``GITEA_TEST_E2E_DEBUG`` | When set, show Gitea server output | -| ``GITEA_TEST_E2E_FLAGS`` | Additional flags passed to Playwright, for example ``--ui`` | -| ``GITEA_TEST_E2E_TIMEOUT_FACTOR`` | Timeout multiplier (default: 4 on CI, 1 locally) | +Before submitting a pull request, run the linters (`make lint`, or the scoped `make lint-backend` / `make lint-frontend`) and the tests to make sure your changes don't cause a regression elsewhere. See [docs/testing.md](docs/testing.md) for how to run the unit, integration, end-to-end, and migration tests. ## Translation diff --git a/README.md b/README.md index 3aa5138dac..521f2baf24 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,21 @@ ## Purpose -The goal of this project is to make the easiest, fastest, and most -painless way of setting up a self-hosted Git service. +The goal of Gitea is to make the easiest, fastest, and most painless way of +setting up a self-hosted all-in-one software development service, +including Git hosting, code management, code review, issue tracking, project kanban, wiki, +team collaboration, package registry and CI/CD which can reuse GitHub Actions. As Gitea is written in Go, it works across **all** the platforms and -architectures that are supported by Go, including Linux, macOS, and -Windows on x86, amd64, ARM and PowerPC architectures. -This project has been -[forked](https://blog.gitea.com/welcome-to-gitea/) from -[Gogs](https://gogs.io) since November of 2016, but a lot has changed. +architectures that are supported by Go, including Linux, macOS, FreeBSD/OpenBSD and Windows +on x86, amd64, ARM, RISC-V 64 and PowerPC architectures. For online demonstrations, you can visit [demo.gitea.com](https://demo.gitea.com). For accessing free Gitea service (with a limited number of repositories), you can visit [gitea.com](https://gitea.com/user/login). -To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com). +To quickly deploy your own dedicated Gitea instance on Gitea Cloud, you can start a free trial at [cloud.gitea.com](https://cloud.gitea.com), +or use container (docker/podman/etc) to deploy on your own server with the [official image](https://hub.docker.com/r/gitea/gitea). ## Documentation @@ -40,27 +40,12 @@ If you have any suggestions or would like to contribute to it, you can visit the ## Building -From the root of the source tree, run: +See [docs/build-setup.md](docs/build-setup.md) for prerequisites +and [docs/development.md](docs/development.md) for setting up a local development environment, linting, and testing. - TAGS="bindata" make build +If you'd like to build from source or make a distribution package, see [docs/build-source.md](docs/build-source.md) for more information. -The `build` target is split into two sub-targets: - -- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod). -- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and [pnpm](https://pnpm.io/installation). - -Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js. - -More info: https://docs.gitea.com/installation/install-from-source - -## Using - -After building, a binary file named `gitea` will be generated in the root of the source tree by default. To run it, use: - - ./gitea web - -> [!NOTE] -> If you're interested in using our APIs, we have experimental support with [documentation](https://docs.gitea.com/api). +After building, you can run `./gitea web` to start the server, or `./gitea help` to see all available commands. ## Contributing @@ -69,7 +54,8 @@ Expected workflow is: Fork -> Patch -> Push -> Pull Request > [!NOTE] > > 1. **YOU MUST READ THE [CONTRIBUTORS GUIDE](CONTRIBUTING.md) BEFORE STARTING TO WORK ON A PULL REQUEST.** -> 2. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks! +> 2. New to the codebase? The [development guide](docs/development.md) walks through setting up a local environment and building from source. +> 3. If you have found a vulnerability in the project, please write privately to **security@gitea.io**. Thanks! ## Translating @@ -126,14 +112,19 @@ Support this project by becoming a sponsor. Your logo will show up here with a l Gitea is pronounced [/ɡɪ’ti:/](https://youtu.be/EM71-2uDAoY) as in "gi-tea" with a hard g. -**Why is this not hosted on a Gitea instance?** +**How do I configure Gitea?** -We're [working on it](https://github.com/go-gitea/gitea/issues/1029). +For dynamic config options, you can change it on your admin panel's configuration section. + +For static config options, you can edit your `app.ini` file and resart the instance. +See [app.example.ini](https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini) or [configuration documentation](https://docs.gitea.com/administration/config-cheat-sheet) for more details. **Where can I find the security patches?** In the [release log](https://github.com/go-gitea/gitea/releases) or the [change log](https://github.com/go-gitea/gitea/blob/main/CHANGELOG.md), search for the keyword `SECURITY` to find the security patches. +(more FAQs are listed in [FAQ documentation](https://docs.gitea.com/help/faq)) + ## License This project is licensed under the MIT License. @@ -143,7 +134,7 @@ for the full license text. ## Further information
-Looking for an overview of the interface? Check it out! +Looking for an overview of the interface? Check it out the screenshots! ### Login/Register Page diff --git a/contrib/development/README.md b/contrib/development/README.md index a9abfa3ef1..5fc5dd39f0 100644 --- a/contrib/development/README.md +++ b/contrib/development/README.md @@ -1,12 +1,14 @@ # IDE and code editor configuration ## Table of Contents + - [IDE and code editor configuration](#ide-and-code-editor-configuration) - [Microsoft Visual Studio Code](#microsoft-visual-studio-code) ## Microsoft Visual Studio Code + Download Microsoft Visual Studio Code at https://code.visualstudio.com/ and follow instructions at https://code.visualstudio.com/docs/languages/go to setup Go extension for it. -Create new directory `.vscode` in Gitea root folder and copy contents of folder [contrib/ide/vscode](vscode/) to it. You can now use `Ctrl`+`Shift`+`B` to build gitea executable and `F5` to run it in debug mode. +Create new directory `.vscode` in Gitea root folder and copy contents of folder [contrib/development/vscode](vscode/) to it. You can now use `Ctrl`+`Shift`+`B` to build gitea executable and `F5` to run it in debug mode. Supported on Debian, Ubuntu, Red Hat, Fedora, SUSE Linux, MacOS and Microsoft Windows. diff --git a/docs/build-setup.md b/docs/build-setup.md new file mode 100644 index 0000000000..d1cd465a60 --- /dev/null +++ b/docs/build-setup.md @@ -0,0 +1,67 @@ +# Setup and requirements + +This document lists the tools you need to build Gitea from source and how to get +the code. Once your environment is ready, see [development.md](development.md) for +the build and development workflow, and [testing.md](testing.md) for running tests. + +For the contribution workflow and review process, see [CONTRIBUTING.md](../CONTRIBUTING.md). + +## Requirements + +### Go + +[Install Go](https://go.dev/doc/install) and set up your Go environment. The +required version is the one declared in [`go.mod`](../go.mod); installing the same +version your continuous integration uses avoids `gofmt` differences between Go +releases. + +> [!NOTE] +> Some `make` tasks build external Go tools on demand (for example `make +> watch-backend`). To use them, the `"$GOPATH"/bin` directory must be on your +> executable `PATH`; otherwise you have to manage those tools yourself. + +### Node.js and pnpm + +[Install Node.js](https://nodejs.org/en/download/) to build the JavaScript and CSS +files. The minimum supported version is the one declared in +[`package.json`](../package.json) (`engines.node`); the latest LTS is recommended. + +Gitea manages frontend dependencies with [pnpm](https://pnpm.io/). The `make` +targets invoke it for you, so installing pnpm manually is only needed if you want +to run `pnpm` commands directly. + +### Make + +Gitea uses [Make](https://www.gnu.org/software/make/) to drive builds, linting, and +tests. On Windows it can be installed via [MSYS2](https://www.msys2.org/) or +[Chocolatey](https://chocolatey.org/packages/make). + +### Python with uv (optional) + +Linting the templates, workflow files, and YAML requires Python tooling that Gitea +runs through [uv](https://docs.astral.sh/uv/). After installing uv, `make` creates +the environment automatically (`uv sync`); you only need this if you run +`make lint-templates`, `make lint-yaml`, or `make lint-actions` locally. + +### Git LFS + +The integration tests require [Git LFS](https://git-lfs.com/) to be installed. + +## Getting the source code + +Clone the repository: + +```bash +git clone https://github.com/go-gitea/gitea +``` + +To contribute changes, [fork the repository](https://github.com/go-gitea/gitea) on +GitHub and add your fork as a git remote so you can push branches and open pull +requests. See GitHub's [working with forks](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks) +documentation for the details. + +## Installing dependencies + +Most build and test targets install the dependencies they need on their own. To +fetch everything up front, run `make deps` (or the per-group `make deps-frontend`, +`make deps-backend`, `make deps-tools`, `make deps-py`). diff --git a/docs/build-source.md b/docs/build-source.md new file mode 100644 index 0000000000..17e2ef7ae0 --- /dev/null +++ b/docs/build-source.md @@ -0,0 +1,92 @@ +# Prepare build environment + +Complete the steps in [build-setup.md](build-setup.md) to prepare your environment for building Gitea from source. + +## Choose a branch + +By default, the cloned repository is on main branch (the current development branch for next major release, aka: main nightly). + +You can switch to a versioned branch (the branch for the next minor stable release, aka: stable nightly ) +or a versioned tag (matches the official releases with version numbers) + +To test a Pull Request, you can fetch its code by its Pull Request number (take `PR #123456` as example): + +```bash +git fetch origin pull/123456/head:pr-123456 +``` + +# Build + +Various [make tasks](https://github.com/go-gitea/gitea/blob/main/Makefile) +are provided to keep the build process as simple as possible. + +Depending on requirements, the following build tags can be included. + +- `bindata`: Build a single monolithic binary, with all assets included. Required for distribution and production build. +- `pam`: Enable support for PAM (Linux Pluggable Authentication Modules). + Can be used to authenticate local users or extend authentication to methods available to PAM. +- `gogit`: (EXPERIMENTAL) Use go-git variants of Git commands. + +To include all assets, use the `bindata` tag: + +```bash +TAGS="bindata" make build +``` + +Tag `gogit` is used to try to resolve some Windows-specific performance problems, POSIX systems don't need it. +You can build a Windows binary by: + +```bash +GOOS=windows TAGS="bindata gogit" make build +``` + +## Changing default paths + +Gitea will search for a number of things from the _`CustomPath`_. +By default, this is the `custom/` directory in the current working directory when running Gitea. +It will also look for its configuration file _`CustomConf`_ in `$(CustomPath)/conf/app.ini`, +and will use the current working directory as the relative base path _`AppWorkPath`_. + +These values, although useful when developing, may conflict with downstream users preferences. + +For packagers who need to use paths like `/etc/gitea/app.ini`, +they should define these values at build time for `make build` by environment variable like +`LDFLAGS='-X "module.Var1=Value1" -X "module.Var2=Value2"' TAGS="bindata" make build`. + +- _`CustomConf`_: `-X "code.gitea.io/gitea/modules/setting.CustomConf=/etc/gitea/app.ini"` +- _`AppWorkPath`_: `-X "code.gitea.io/gitea/modules/setting.AppWorkPath=/var/lib/gitea"` +- _`CustomPath`_: `-X "code.gitea.io/gitea/modules/setting.CustomPath=/var/lib/gitea/custom"` +- Default PID file location: `-X "code.gitea.io/gitea/cmd.PIDFile=/run/gitea.pid"` + +Add as many of the strings with their preceding `-X` to the `LDFLAGS` variable and run `make build` +with the appropriate `TAGS` as above. + +Running `gitea help` will allow you to review what the computed settings will be for your `gitea`. + +## Cross Build + +Gitea use's Golang's toolchain variables for cross-building. + +For example, to cross build for Linux ARM64: + +``` +GOOS=linux GOARCH=arm64 TAGS="bindata" make build +``` + +### Adding shell autocompletion + +Shell completion can be generated directly from binary with: +```sh +gitea completion +``` + +Supported values for `` are `bash`, `fish`, `pwsh` and `zsh`. +Details on how to load the completion for your shell can be found in the completion command help. + +## Source Maps + +By default, gitea generates reduced source maps for frontend files to conserve space. This can be controlled with the `ENABLE_SOURCEMAP` environment variable: + +- `ENABLE_SOURCEMAP=true` generates all source maps, the default for development builds +- `ENABLE_SOURCEMAP=reduced` generates limited source maps, the default for production builds +- `ENABLE_SOURCEMAP=false` generates no source maps diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000000..e0858a716f --- /dev/null +++ b/docs/development.md @@ -0,0 +1,141 @@ +# Development + +This document describes how to build Gitea from source and the day-to-day +development workflow. For prerequisites and how to obtain the code, see +[build-setup.md](build-setup.md). For running tests, see [testing.md](testing.md). For the +contribution workflow and review process, see [CONTRIBUTING.md](../CONTRIBUTING.md). + +Area-specific guidelines: + +- [Backend development guidelines](guidelines-backend.md) +- [Frontend development guidelines](guidelines-frontend.md) +- [Refactoring guidelines](guidelines-refactoring.md) + +## Building + +To build Gitea for development, run: + +```bash +make build +``` + +No build tags are required: SQLite support is compiled in by default, which is +enough for local development. The `build` target runs two sub-targets, `frontend` +and `backend`. The `bindata` tag embeds the frontend assets into the binary and is +only needed when packaging a self-contained build, so leave it out during +development. + +See `make help` for all available targets, and the workflows in +[`.github/workflows`](https://github.com/go-gitea/gitea/tree/main/.github/workflows) +to see how continuous integration builds and checks Gitea. + +## Building continuously + +To rebuild automatically when source files change: + +```bash +# watch both frontend and backend +make watch + +# or watch only the frontend (starts the Vite dev server) +make watch-frontend + +# or watch only the backend (Go) +make watch-backend +``` + +Watching all backend source files may hit the default open-files limit on macOS or +Linux; raise it with `ulimit -n 12288` for the current shell, or in your shell +startup file to make it permanent. + +## Formatting, linting and checks + +Continuous integration rejects pull requests that fail formatting, linting, or +consistency checks. Format your code first: + +```bash +make fmt +``` + +Then lint: + +```bash +# lint everything +make lint +# or only one side +make lint-backend +make lint-frontend +``` + +Many linters can fix issues automatically with `make lint-fix` (or the scoped +`make lint-backend-fix` / `make lint-frontend-fix`). The combined consistency +checks that CI runs are available as `make checks`. + +## Building and adding SVGs + +SVG icons are built with `make svg`, which compiles the icon sources into +`public/assets/img/svg`. Custom icons can be added under `web_src/svg`. + +## Updating the API + +When you create or change API routes, you **must** update the +[Swagger](https://swagger.io/docs/specification/2-0/what-is-swagger/) documentation +using [go-swagger](https://goswagger.io/) comments. See the +[backend development guidelines](guidelines-backend.md) for how API routes, +request/response structs, and swagger definitions fit together. + +Regenerate and validate the spec after changing an endpoint, then commit the +updated JSON: + +```bash +make generate-swagger +make swagger-validate +``` + +CI verifies the committed spec is up to date with: + +```bash +make swagger-check +``` + +## Creating new configuration options + +When adding configuration options it is not enough to add them to the +`modules/setting` files. Also update +[`custom/conf/app.example.ini`](../custom/conf/app.example.ini), and document them in +the [configuration cheat sheet](https://docs.gitea.com/administration/config-cheat-sheet), +which lives in the [documentation repository](https://gitea.com/gitea/docs). + +## Database migrations + +If you make breaking changes to a database-persisted struct under `models/`, add a +new migration in `models/migrations/`. See [testing.md](testing.md#migration-tests) +for running the migration tests. + +## Testing + +For unit, integration, end-to-end, and migration tests, see [testing.md](testing.md). + +## IDE configuration + +### Visual Studio Code + +A `launch.json` and `tasks.json` are provided in +[`contrib/development/vscode`](../contrib/development/vscode). See +[`contrib/development/README.md`](../contrib/development/README.md) for details. + +### GoLand + +Clicking the `Run Application` arrow on `func main()` in `/main.go` starts a +debuggable Gitea instance. + +The `Output Directory` in `Run/Debug Configuration` **must** be set to the Gitea +project directory (the one containing `main.go` and `go.mod`). Otherwise the working +directory is a GoLand temporary directory, which prevents Gitea from loading dynamic +resources (such as templates) in development. + +## Submitting your changes + +Push your branch and open a pull request. See [CONTRIBUTING.md](../CONTRIBUTING.md) +for the review process and PR requirements. For help, join the `#Develop` channel on +[Discord](https://discord.gg/gitea). diff --git a/docs/guideline-backend.md b/docs/guideline-backend.md deleted file mode 100644 index 29fa92288c..0000000000 --- a/docs/guideline-backend.md +++ /dev/null @@ -1,63 +0,0 @@ -# Backend development - -This document covers backend-specific contribution expectations. For general contribution workflow, see [CONTRIBUTING.md](../CONTRIBUTING.md). - -For coding style and architecture, see also the [backend development guideline](https://docs.gitea.com/contributing/guidelines-backend) on the documentation site. - -## Dependencies - -Go dependencies are managed using [Go Modules](https://go.dev/cmd/go/#hdr-Module_maintenance). \ -You can find more details in the [go mod documentation](https://go.dev/ref/mod) and the [Go Modules Wiki](https://github.com/golang/go/wiki/Modules). - -Pull requests should only modify `go.mod` and `go.sum` where it is related to your change, be it a bugfix or a new feature. \ -Apart from that, these files should only be modified by Pull Requests whose only purpose is to update dependencies. - -The `go.mod`, `go.sum` update needs to be justified as part of the PR description, -and must be verified by the reviewers and/or merger to always reference -an existing upstream commit. - -## API v1 - -The API is documented by [swagger](https://gitea.com/api/swagger) and is based on [the GitHub API](https://docs.github.com/en/rest). - -### GitHub API compatibility - -Gitea's API should use the same endpoints and fields as the GitHub API as far as possible, unless there are good reasons to deviate. \ -If Gitea provides functionality that GitHub does not, a new endpoint can be created. \ -If information is provided by Gitea that is not provided by the GitHub API, a new field can be used that doesn't collide with any GitHub fields. \ -Updating an existing API should not remove existing fields unless there is a really good reason to do so. \ -The same applies to status responses. If you notice a problem, feel free to leave a comment in the code for future refactoring to API v2 (which is currently not planned). - -### Adding/Maintaining API routes - -All expected results (errors, success, fail messages) must be documented ([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L319-L327)). \ -All JSON input types must be defined as a struct in [modules/structs/](modules/structs/) ([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L76-L91)) \ -and referenced in [routers/api/v1/swagger/options.go](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/options.go). \ -They can then be used like [this example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L318). \ -All JSON responses must be defined as a struct in [modules/structs/](modules/structs/) ([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/modules/structs/issue.go#L36-L68)) \ -and referenced in its category in [routers/api/v1/swagger/](routers/api/v1/swagger/) ([example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/swagger/issue.go#L11-L16)) \ -They can be used like [this example](https://github.com/go-gitea/gitea/blob/c620eb5b2d0d874da68ebd734d3864c5224f71f7/routers/api/v1/repo/issue.go#L277-L279). - -### When to use what HTTP method - -In general, HTTP methods are chosen as follows: - -- **GET** endpoints return the requested object(s) and status **OK (200)** -- **DELETE** endpoints return the status **No Content (204)** and no content either -- **POST** endpoints are used to **create** new objects (e.g. a User) and return the status **Created (201)** and the created object -- **PUT** endpoints are used to **add/assign** existing Objects (e.g. a user to a team) and return the status **No Content (204)** and no content either -- **PATCH** endpoints are used to **edit/change** an existing object and return the changed object and the status **OK (200)** - -### Requirements for API routes - -All parameters of endpoints changing/editing an object must be optional (except the ones to identify the object, which are required). - -Endpoints returning lists must - -- support pagination (`page` & `limit` options in query) -- set `X-Total-Count` header via **SetTotalCountHeader** ([example](https://github.com/go-gitea/gitea/blob/7aae98cc5d4113f1e9918b7ee7dd09f67c189e3e/routers/api/v1/repo/issue.go#L444)) - -### Knowledge - -- Partially database table migration must use `SyncWithOptions(IgnoreDrop...)` -- Template variables with "camelCase" or "snake_case" are used for restoring the form values from a submitted form diff --git a/docs/guideline-frontend.md b/docs/guideline-frontend.md deleted file mode 100644 index 80ebe82177..0000000000 --- a/docs/guideline-frontend.md +++ /dev/null @@ -1,17 +0,0 @@ -# Frontend development - -This document covers frontend-specific contribution expectations. For general contribution workflow, see [CONTRIBUTING.md](../CONTRIBUTING.md). - -## Dependencies - -For the frontend, we use [npm](https://www.npmjs.com/). - -The same restrictions apply for frontend dependencies as for [backend dependencies](guideline-backend.md#dependencies), with the exceptions that the files for it are `package.json` and `package-lock.json`, and that new versions must always reference an existing version. - -## Design guideline - -Depending on your change, please read the - -- [backend development guideline](https://docs.gitea.com/contributing/guidelines-backend) -- [frontend development guideline](https://docs.gitea.com/contributing/guidelines-frontend) -- [refactoring guideline](https://docs.gitea.com/contributing/guidelines-refactoring) diff --git a/docs/guidelines-backend.md b/docs/guidelines-backend.md new file mode 100644 index 0000000000..5342c1d971 --- /dev/null +++ b/docs/guidelines-backend.md @@ -0,0 +1,132 @@ +# Backend development guidelines + +This document covers backend-specific architecture and contribution expectations. +For the general workflow see [CONTRIBUTING.md](../CONTRIBUTING.md), and for building +and testing see [development.md](development.md) and [testing.md](testing.md). + +## Background + +The backend is written in Go. Web routing is handled by +[chi](https://github.com/go-chi/chi) and database access goes through the +[XORM](https://xorm.io/) ORM. Understanding how the packages depend on each other is +essential before contributing backend code. + +## Package design + +### Package layout + +The backend is split into top-level packages, each with a focused responsibility: + +- `build`: helper scripts used at compile time +- `cmd`: subcommands such as `web`, `serv`, `hooks`, `doctor`, and admin utilities +- `models`: data structures and database operations (XORM); keeps external + dependencies to a minimum + - `models/db`: core database operations + - `models/fixtures`: sample data used by tests + - `models/migrations`: schema migration scripts +- `modules`: standalone functionality with few dependencies + - `modules/setting`: configuration handling + - `modules/git`: interaction with the Git command line +- `routers`: request handlers, split into `api`, `web`, `install`, and `private` +- `services`: business logic that ties routers and models together +- `templates`: Go HTML templates +- `public`: compiled frontend assets +- `tests`: integration and end-to-end test helpers + +### Dependency direction + +Dependencies only flow in one direction: + +```text +cmd → routers → services → models → modules +``` + +A package on the left may import a package on its right, but never the reverse. + +### Naming conventions + +- Top-level packages use the plural form: `services`, `models`, `routers`. +- Subpackages use the singular form: `services/user`, `models/repository`. + +When packages from different layers share a name, use a snake_case import alias to +disambiguate: + +```go +import user_service "gitea.dev/services/user" +``` + +### Database transactions + +Operations that must roll back together should run inside `db.WithTx()` (or +`db.WithTx2()` when a value must be returned), defined in `models/db/context.go`. +Functions that participate in a transaction take a `context.Context` as their first +parameter so the transaction can be propagated. + +### XORM gotchas + +- Never call `x.Update(exemplar)` without an explicit `WHERE` clause — it updates + every row in the table. +- Partial table migrations must use `SyncWithOptions(IgnoreDrop...)` rather than a + plain `Sync`. +- When inserting rows with preset IDs, MSSQL requires `SET IDENTITY_INSERT` to be + enabled and PostgreSQL requires the sequence to be updated afterwards. + +## Dependencies + +Go dependencies are managed with [Go Modules](https://go.dev/ref/mod). + +Pull requests should only modify `go.mod` and `go.sum` where it relates to the +change at hand, be it a bug fix or a new feature. Otherwise, these files should only +be touched by pull requests whose sole purpose is updating dependencies. Run +`make tidy` after any change to `go.mod`. + +Any `go.mod` / `go.sum` update must be justified in the PR description and must be +verified by reviewers and the merger to reference an existing upstream commit. + +## API v1 + +The API is documented with [Swagger](https://gitea.com/api/swagger) and is modelled +on [the GitHub API](https://docs.github.com/en/rest). + +### GitHub API compatibility + +Gitea's API should use the same endpoints and fields as the GitHub API where +possible, unless there is a good reason to deviate. + +- If Gitea offers functionality GitHub does not, a new endpoint may be added. +- If Gitea exposes information the GitHub API does not, a new field may be added as + long as it does not collide with a GitHub field. +- Existing fields should not be removed unless there is a strong reason; the same + applies to status responses. + +If you notice a problem that would require a breaking change, leave a comment in the +code for a future refactor to API v2 (which is currently not planned) rather than +breaking v1. + +### Adding and maintaining API routes + +- All possible results (errors, success, and failure messages) must be documented in + the swagger comments on the route. +- Every JSON request body must be defined as a struct in `modules/structs/` and + registered in [`routers/api/v1/swagger/options.go`](../routers/api/v1/swagger/options.go). +- Every JSON response must be defined as a struct in `modules/structs/` and + registered with its category under [`routers/api/v1/swagger/`](../routers/api/v1/swagger). + +### HTTP methods and status codes + +In general, choose HTTP methods as follows: + +- **GET** returns the requested object(s) with status **200 OK**. +- **POST** creates a new object (e.g. a user) and returns **201 Created** with the + created object. +- **PUT** adds or assigns an existing object (e.g. a user to a team) and returns + **204 No Content** with no body. +- **PATCH** edits an existing object and returns the changed object with **200 OK**. +- **DELETE** removes an object and returns **204 No Content** with no body. + +### Requirements for API routes + +- All parameters of endpoints that edit an object must be optional, except those + needed to identify the object, which are required. +- Endpoints returning lists must support pagination (`page` and `limit` query + options) and set the `X-Total-Count` header via `ctx.SetTotalCountHeader(...)`. diff --git a/docs/guidelines-frontend.md b/docs/guidelines-frontend.md new file mode 100644 index 0000000000..61a801ba38 --- /dev/null +++ b/docs/guidelines-frontend.md @@ -0,0 +1,98 @@ +# Frontend development guidelines + +This document covers frontend-specific architecture and contribution expectations. +For the general workflow see [CONTRIBUTING.md](../CONTRIBUTING.md), and for building +and testing see [development.md](development.md) and [testing.md](testing.md). + +## Background + +The frontend uses [Vue 3](https://vuejs.org/), [Fomantic-UI](https://fomantic-ui.com/) (built on jQuery) +and [Tailwind CSS](https://tailwindcss.com/). Pages are rendered with Go HTML templates. +Source files live in: + +- `web_src/css/`: CSS styles +- `web_src/js/`: JavaScript and TypeScript +- `web_src/js/components/`: Vue components +- `web_src/js/features/`: feature modules wired up at page load +- `templates/`: Go HTML templates + +## Dependencies + +Frontend dependencies are managed with [pnpm](https://pnpm.io/). The same rules as +for [backend dependencies](guidelines-backend.md#dependencies) apply, except the +relevant files are `package.json` and `pnpm-lock.yaml`, and new versions must always +reference an existing published version. + +## Framework usage + +Mixing frameworks arbitrarily makes code hard to maintain. Recommended combinations: + +- Vue3 +- Vanilla JavaScript +- Fomantic-UI (jQuery), deprecated, we vendored a specific version with a lot of changes. + +Avoid combinations such as Vue with Fomantic-UI. +Vue components may reuse Fomantic-UI CSS classes for visual consistency. +Use Go templates for simple or SEO-relevant pages and Vue for complex, interactive pages. +Gitea uses Vue 3 **without** JSX to keep HTML and JavaScript separate. + +> [!NOTE] +> Fomantic-UI is not an accessibility-friendly framework. Gitea patches some ARIA +> behavior, but accessibility work is ongoing — prefer semantic HTML and test +> keyboard/screen-reader behavior where you can. + +## Gitea-specific conventions + +- Keep features in their own files or directories. +- Use kebab-case for HTML `id`s and classes, ideally with 2-3 feature keywords. +- Prefix classes to avoid short-name conflicts between different frameworks. +- Create a new class name when overriding framework styles instead of editing the framework's own classes, + or fix the framework's source to fix all cases. +- Prefer semantic elements such as `