From 7c9d9cff5ef6c088de81f7b1dec801cbc72464ce Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 24 Jan 2025 12:23:15 +0000 Subject: [PATCH 001/669] fix: load settings for valid user and email check (#6674) - The doctor commands to check the validity of existing usernames and email addresses depend on functionality that have configurable behavior depending on the values of the `[service]` settings, so load them when running the doctor command. - Resolves #6664 - No unit test due to the architecture of doctor commands. 1. Set `[service].ALLOW_DOTS_IN_USERNAMES = true`. 2. Create a user that contains a dot in their username. 3. Run the `check-user-name` doctor command and verify there's no error reported. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6674 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- modules/setting/service.go | 4 ++++ services/doctor/breaking.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/modules/setting/service.go b/modules/setting/service.go index b76c2b46d1..7a907023c4 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -140,6 +140,10 @@ func CompileEmailGlobList(sec ConfigSection, keys ...string) (globs []glob.Glob) return globs } +// LoadServiceSetting loads the service settings +func LoadServiceSetting() { + loadServiceFrom(CfgProvider) +} func loadServiceFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("service") diff --git a/services/doctor/breaking.go b/services/doctor/breaking.go index 683ec97389..ec8433b8de 100644 --- a/services/doctor/breaking.go +++ b/services/doctor/breaking.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/validation" "xorm.io/builder" @@ -30,6 +31,8 @@ func iterateUserAccounts(ctx context.Context, each func(*user.User) error) error // addresses would be currently facing a error due to their invalid email address. // Ref: https://github.com/go-gitea/gitea/pull/19085 & https://github.com/go-gitea/gitea/pull/17688 func checkUserEmail(ctx context.Context, logger log.Logger, _ bool) error { + setting.LoadServiceSetting() + // We could use quirky SQL to get all users that start without a [a-zA-Z0-9], but that would mean // DB provider-specific SQL and only works _now_. So instead we iterate through all user accounts // and use the validation.ValidateEmail function to be future-proof. @@ -61,6 +64,8 @@ func checkUserEmail(ctx context.Context, logger log.Logger, _ bool) error { // are allowed for various reasons. This check helps with detecting users that, according // to our reserved names, don't have a valid username. func checkUserName(ctx context.Context, logger log.Logger, _ bool) error { + setting.LoadServiceSetting() + var invalidUserCount int64 if err := iterateUserAccounts(ctx, func(u *user.User) error { if err := user.IsUsableUsername(u.Name); err != nil { From 1b36df14c72fca235586027b326ef8b4be8f9311 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 24 Jan 2025 16:45:46 +0000 Subject: [PATCH 002/669] chore: teach `set` module about `iter.Seq` (#6676) - Add a new `Seq` function to the `Set` type, this returns an iterator over the values. - Convert some users of the `Values` method to allow for more optimal code. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6676 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- cmd/web.go | 2 +- models/user/user_test.go | 4 ++-- modules/assetfs/layered.go | 8 +++----- modules/container/set.go | 11 +++++++++++ modules/container/set_test.go | 9 +++++++++ modules/util/slice.go | 8 -------- routers/web/user/package.go | 20 ++++++++++---------- services/packages/alt/repository.go | 2 +- services/release/release.go | 2 +- 9 files changed, 38 insertions(+), 28 deletions(-) diff --git a/cmd/web.go b/cmd/web.go index 3fc64f7748..787411939c 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -195,7 +195,7 @@ func serveInstalled(ctx *cli.Context) error { publicFilesSet.Remove(".well-known") publicFilesSet.Remove("assets") publicFilesSet.Remove("robots.txt") - for _, fn := range publicFilesSet.Values() { + for fn := range publicFilesSet.Seq() { log.Error("Found legacy public asset %q in CustomPath. Please move it to %s/public/assets/%s", fn, setting.CustomPath, fn) } if _, err := os.Stat(filepath.Join(setting.CustomPath, "robots.txt")); err == nil { diff --git a/models/user/user_test.go b/models/user/user_test.go index df0c3856e9..2c8c1609fd 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -715,7 +715,7 @@ func TestDisabledUserFeatures(t *testing.T) { // no features should be disabled with a plain login type assert.LessOrEqual(t, user.LoginType, auth.Plain) assert.Empty(t, user_model.DisabledFeaturesWithLoginType(user).Values()) - for _, f := range testValues.Values() { + for f := range testValues.Seq() { assert.False(t, user_model.IsFeatureDisabledWithLoginType(user, f)) } @@ -724,7 +724,7 @@ func TestDisabledUserFeatures(t *testing.T) { // all features should be disabled assert.NotEmpty(t, user_model.DisabledFeaturesWithLoginType(user).Values()) - for _, f := range testValues.Values() { + for f := range testValues.Seq() { assert.True(t, user_model.IsFeatureDisabledWithLoginType(user, f)) } } diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index 9678d23ad6..9feabc3f8c 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -11,7 +11,7 @@ import ( "net/http" "os" "path/filepath" - "sort" + "slices" "time" "code.gitea.io/gitea/modules/container" @@ -143,8 +143,7 @@ func (l *LayeredFS) ListFiles(name string, fileMode ...bool) ([]string, error) { } } } - files := fileSet.Values() - sort.Strings(files) + files := slices.Sorted(fileSet.Seq()) return files, nil } @@ -184,8 +183,7 @@ func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, err if err := list(name); err != nil { return nil, err } - files := fileSet.Values() - sort.Strings(files) + files := slices.Sorted(fileSet.Seq()) return files, nil } diff --git a/modules/container/set.go b/modules/container/set.go index 2d654d0aee..70f837bc66 100644 --- a/modules/container/set.go +++ b/modules/container/set.go @@ -3,6 +3,11 @@ package container +import ( + "iter" + "maps" +) + type Set[T comparable] map[T]struct{} // SetOf creates a set and adds the specified elements to it. @@ -63,3 +68,9 @@ func (s Set[T]) Values() []T { } return keys } + +// Seq returns a iterator over the elements in the set. +// It returns a single-use iterator. +func (s Set[T]) Seq() iter.Seq[T] { + return maps.Keys(s) +} diff --git a/modules/container/set_test.go b/modules/container/set_test.go index 3cfbf7cc2c..e54e31a052 100644 --- a/modules/container/set_test.go +++ b/modules/container/set_test.go @@ -4,6 +4,7 @@ package container import ( + "slices" "testing" "github.com/stretchr/testify/assert" @@ -29,6 +30,14 @@ func TestSet(t *testing.T) { assert.True(t, s.Contains("key4")) assert.True(t, s.Contains("key5")) + values := s.Values() + called := 0 + for value := range s.Seq() { + called++ + assert.True(t, slices.Contains(values, value)) + } + assert.EqualValues(t, len(values), called) + s = SetOf("key6", "key7") assert.False(t, s.Contains("key1")) assert.True(t, s.Contains("key6")) diff --git a/modules/util/slice.go b/modules/util/slice.go index 9c878c24be..80c8e62f6f 100644 --- a/modules/util/slice.go +++ b/modules/util/slice.go @@ -4,7 +4,6 @@ package util import ( - "cmp" "slices" "strings" ) @@ -47,13 +46,6 @@ func SliceRemoveAll[T comparable](slice []T, target T) []T { return slices.DeleteFunc(slice, func(t T) bool { return t == target }) } -// Sorted returns the sorted slice -// Note: The parameter is sorted inline. -func Sorted[S ~[]E, E cmp.Ordered](values S) S { - slices.Sort(values) - return values -} - // TODO: Replace with "maps.Values" once available, current it only in golang.org/x/exp/maps but not in standard library func ValuesOfMap[K comparable, V any](m map[K]V) []V { values := make([]V, 0, len(m)) diff --git a/routers/web/user/package.go b/routers/web/user/package.go index 707c86db7a..70ea20d388 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -6,6 +6,7 @@ package user import ( "fmt" "net/http" + "slices" "code.gitea.io/gitea/models/db" org_model "code.gitea.io/gitea/models/organization" @@ -23,7 +24,6 @@ import ( debian_module "code.gitea.io/gitea/modules/packages/debian" rpm_module "code.gitea.io/gitea/modules/packages/rpm" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" packages_helper "code.gitea.io/gitea/routers/api/packages/helper" shared_user "code.gitea.io/gitea/routers/web/shared/user" @@ -200,9 +200,9 @@ func ViewPackageVersion(ctx *context.Context) { } } - ctx.Data["Branches"] = util.Sorted(branches.Values()) - ctx.Data["Repositories"] = util.Sorted(repositories.Values()) - ctx.Data["Architectures"] = util.Sorted(architectures.Values()) + ctx.Data["Branches"] = slices.Sorted(branches.Seq()) + ctx.Data["Repositories"] = slices.Sorted(repositories.Seq()) + ctx.Data["Architectures"] = slices.Sorted(architectures.Seq()) case packages_model.TypeArch: ctx.Data["SignMail"] = fmt.Sprintf("%s@noreply.%s", ctx.Package.Owner.Name, setting.Packages.RegistryHost) groups := make(container.Set[string]) @@ -213,7 +213,7 @@ func ViewPackageVersion(ctx *context.Context) { } } } - ctx.Data["Groups"] = util.Sorted(groups.Values()) + ctx.Data["Groups"] = slices.Sorted(groups.Seq()) case packages_model.TypeDebian: distributions := make(container.Set[string]) components := make(container.Set[string]) @@ -232,9 +232,9 @@ func ViewPackageVersion(ctx *context.Context) { } } - ctx.Data["Distributions"] = util.Sorted(distributions.Values()) - ctx.Data["Components"] = util.Sorted(components.Values()) - ctx.Data["Architectures"] = util.Sorted(architectures.Values()) + ctx.Data["Distributions"] = slices.Sorted(distributions.Seq()) + ctx.Data["Components"] = slices.Sorted(components.Seq()) + ctx.Data["Architectures"] = slices.Sorted(architectures.Seq()) case packages_model.TypeRpm, packages_model.TypeAlt: groups := make(container.Set[string]) architectures := make(container.Set[string]) @@ -250,8 +250,8 @@ func ViewPackageVersion(ctx *context.Context) { } } - ctx.Data["Groups"] = util.Sorted(groups.Values()) - ctx.Data["Architectures"] = util.Sorted(architectures.Values()) + ctx.Data["Groups"] = slices.Sorted(groups.Seq()) + ctx.Data["Architectures"] = slices.Sorted(architectures.Seq()) } var ( diff --git a/services/packages/alt/repository.go b/services/packages/alt/repository.go index 7b7951eebb..f49c435e64 100644 --- a/services/packages/alt/repository.go +++ b/services/packages/alt/repository.go @@ -711,7 +711,7 @@ func buildRelease(ctx context.Context, pv *packages_model.PackageVersion, pfs [] architectures.Add(pd.FileMetadata.Architecture) } - for architecture := range architectures { + for architecture := range architectures.Seq() { version := time.Now().Unix() label := setting.AppName data := fmt.Sprintf(`Archive: Alt Linux Team diff --git a/services/release/release.go b/services/release/release.go index 876514beec..b52e4b124e 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -372,7 +372,7 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo return err } - for _, uuid := range delAttachmentUUIDs.Values() { + for uuid := range delAttachmentUUIDs.Seq() { if err := storage.Attachments.Delete(repo_model.AttachmentRelativePath(uuid)); err != nil { // Even delete files failed, but the attachments has been removed from database, so we // should not return error but only record the error on logs. From 6206fdf34e0b3d56cd8abcbc314d6a391206912a Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 24 Jan 2025 13:16:30 +0100 Subject: [PATCH 003/669] fix: add non allowed domain translation - Was added in 2559c80bec27a41967b355d214253a83b9ee5dad and accidentally removed in 5a16c9d9c0fd25b25d5eaf1b0ab00f1c113e6b32. - Reworded for clarity. - Resolves #6661 --- options/locale/locale_en-US.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 2c7dbe69ad..7001c1bf0f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -644,6 +644,7 @@ team_name_been_taken = The team name is already taken. team_no_units_error = Allow access to at least one repository section. email_been_used = The email address is already used. email_invalid = The email address is invalid. +email_domain_is_not_allowed = The domain of the user's email address %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST. Make sure you have set the email address correctly. openid_been_used = The OpenID address "%s" is already used. username_password_incorrect = Username or password is incorrect. password_complexity = Password does not pass complexity requirements: From 315c1450eb98ae3dfa3d633f509864c66810051e Mon Sep 17 00:00:00 2001 From: Robert Wolff Date: Sat, 25 Jan 2025 08:11:37 +0000 Subject: [PATCH 004/669] fix: inline file preview for rendered files (#6572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the inline file preview for rendered files (e.g., markdown). [Here, a live issue in v11](https://v11.next.forgejo.org/mahlzahn/test-inline-file-preview/issues/1) and [the same in v7 (with even more bugs)](https://v7.next.forgejo.org/mahlzahn/test-inline-file-preview/issues/1). It fixes 1. the inline preview for possibly rendered files, when the link is specified with `?display=source`. This happens, e.g., if you are watching a (e.g., markdown) file in source and then want to link some of its lines. 2. the link to the source file inside the inline preview for possible rendered files (currently it links to the rendered version and then the `#L…` cannot point to the correct lines). This is done by always adding `?display=source` to the link. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6572 Reviewed-by: Gusted Co-authored-by: Robert Wolff Co-committed-by: Robert Wolff --- modules/markup/file_preview.go | 8 +- modules/markup/html_test.go | 103 ++++++++++++++++++ .../0b/b53b56d70d253ce75c257d3cd6334a41ef2b6c | Bin 0 -> 77 bytes .../35/75ed7948fe86ab56b0a76f796f7995222bec65 | Bin 0 -> 44 bytes .../3c/95f14e5a0ab2c5ba9ee9a47ddc261af4968043 | Bin 0 -> 90 bytes .../72/1f0ce13d83f93d431b849a554a62948b85f573 | 1 + .../72/e0a44ea5761c9055995db18019e459576b3b27 | Bin 0 -> 44 bytes .../72/e1c77b65c7baa0e848557089148833fb54705e | Bin 0 -> 90 bytes .../8b/ccd5176c25898b57da2551e076f769054e0d8e | Bin 0 -> 21 bytes .../c9/8762531dd068cd818300a5f5c7dca5da79b510 | Bin 0 -> 80 bytes .../c9/913120ed2c1e27c1d7752ecdb7a504dc7cf6be | Bin 0 -> 170 bytes .../repo/repo1_filepreview/refs/heads/master | 2 +- 12 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/0b/b53b56d70d253ce75c257d3cd6334a41ef2b6c create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/35/75ed7948fe86ab56b0a76f796f7995222bec65 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/3c/95f14e5a0ab2c5ba9ee9a47ddc261af4968043 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/72/1f0ce13d83f93d431b849a554a62948b85f573 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/72/e0a44ea5761c9055995db18019e459576b3b27 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/72/e1c77b65c7baa0e848557089148833fb54705e create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/8b/ccd5176c25898b57da2551e076f769054e0d8e create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/c9/8762531dd068cd818300a5f5c7dca5da79b510 create mode 100644 modules/markup/tests/repo/repo1_filepreview/objects/c9/913120ed2c1e27c1d7752ecdb7a504dc7cf6be diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 49a5f1e8ba..3caf08f7bb 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -77,6 +77,12 @@ func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca commitSha := node.Data[m[4]:m[5]] filePath := node.Data[m[6]:m[7]] + urlFullSource := urlFull + if strings.HasSuffix(filePath, "?display=source") { + filePath = strings.TrimSuffix(filePath, "?display=source") + } else if Type(filePath) != "" { + urlFullSource = node.Data[m[0]:m[6]] + filePath + "?display=source#" + node.Data[m[8]:m[1]] + } hash := node.Data[m[8]:m[9]] preview.start = m[0] @@ -113,7 +119,7 @@ func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Loca titleBuffer.WriteString(" – ") } - err = html.Render(titleBuffer, createLink(urlFull, filePath, "muted")) + err = html.Render(titleBuffer, createLink(urlFullSource, filePath, "muted")) if err != nil { log.Error("failed to render filepathLink: %v", err) } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 50ea70905c..702c5a716d 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -1026,4 +1026,107 @@ func TestRender_FilePreview(t *testing.T) { localMetas, ) }) + + commitFileURL := util.URLJoin(markup.TestRepoURL, "src", "commit", "c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be", "path", "to", "file.md") + + t.Run("rendered file with ?display=source", func(t *testing.T) { + testRender( + commitFileURL+"?display=source"+"#L1-L2", + `

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.md`+ + `
`+ + ``+ + `Lines 1 to 2 in c991312`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
# A`+"\n"+`
B`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + }) + + t.Run("rendered file without ?display=source", func(t *testing.T) { + testRender( + commitFileURL+"#L1-L2", + `

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.md`+ + `
`+ + ``+ + `Lines 1 to 2 in c991312`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
# A`+"\n"+`
B`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + }) + + commitFileURL = util.URLJoin(markup.TestRepoURL, "src", "commit", "190d9492934af498c3f669d6a2431dc5459e5b20", "path", "to", "file.go") + + t.Run("normal file with ?display=source", func(t *testing.T) { + testRender( + commitFileURL+"?display=source"+"#L2-L3", + `

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + }) } diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/0b/b53b56d70d253ce75c257d3cd6334a41ef2b6c b/modules/markup/tests/repo/repo1_filepreview/objects/0b/b53b56d70d253ce75c257d3cd6334a41ef2b6c new file mode 100644 index 0000000000000000000000000000000000000000..1ab268b76c99e8c8617f0db6e89b040ef9510c65 GIT binary patch literal 77 zcmV-T0J8sh0V^p=O;s>AU@$Z=Ff%bxNXyJg)l1K3Xi>VtFH~43w>Rd!is!cjbLGn; jGm(|#rZ9A$xhkHc+Swg`OEvI8+4oFVKi)n7{P-Lc(|IE6 literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/35/75ed7948fe86ab56b0a76f796f7995222bec65 b/modules/markup/tests/repo/repo1_filepreview/objects/35/75ed7948fe86ab56b0a76f796f7995222bec65 new file mode 100644 index 0000000000000000000000000000000000000000..1493caa3dfe2d9f627884805316bb754bc739b4c GIT binary patch literal 44 zcmb)5VqnO?)H-??ybM8{~)M!?Q_e=RB0B$)E A_y7O^ literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/3c/95f14e5a0ab2c5ba9ee9a47ddc261af4968043 b/modules/markup/tests/repo/repo1_filepreview/objects/3c/95f14e5a0ab2c5ba9ee9a47ddc261af4968043 new file mode 100644 index 0000000000000000000000000000000000000000..3e9c0c0d8bf7f7aa61ae01ad1474dad2cb75d81e GIT binary patch literal 90 zcmV-g0HyzU0V^p=O;s>AVK6ZO0)>Lak_?8T2TS~xmdQ*Aof*5aLGnptc(%2=p@D&! wiHSmSW?p(us%}nZUaDS6MF~SsT~u1Vbh(84zm5YkwvSze7j9Aj0FzA}xNOQLJOBUy literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/72/1f0ce13d83f93d431b849a554a62948b85f573 b/modules/markup/tests/repo/repo1_filepreview/objects/72/1f0ce13d83f93d431b849a554a62948b85f573 new file mode 100644 index 0000000000..d781d4d248 --- /dev/null +++ b/modules/markup/tests/repo/repo1_filepreview/objects/72/1f0ce13d83f93d431b849a554a62948b85f573 @@ -0,0 +1 @@ +xK1@]$JazJR@w+s۲"@VL&J3%f-GDq2>FjBOEݹ:g\1ꦒkEM6D,Ÿ\Ǹ:\6Olmȩ;ϭ|!GE6ZzYβ mwٛi.x-o"L \ No newline at end of file diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/72/e0a44ea5761c9055995db18019e459576b3b27 b/modules/markup/tests/repo/repo1_filepreview/objects/72/e0a44ea5761c9055995db18019e459576b3b27 new file mode 100644 index 0000000000000000000000000000000000000000..7b926dc0d8324cfc92001c553b87fe03262fd3f0 GIT binary patch literal 44 zcmV+{0Mq|?0V^p=O;s?mWH2!R0)>)%2JWraVb^(8ZJx)d*4kV%_Hul$odW>PCkzCG C!V+Tu literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/72/e1c77b65c7baa0e848557089148833fb54705e b/modules/markup/tests/repo/repo1_filepreview/objects/72/e1c77b65c7baa0e848557089148833fb54705e new file mode 100644 index 0000000000000000000000000000000000000000..0bbca73af27a469407e7241c2dad9e0a2504d531 GIT binary patch literal 90 zcmV-g0HyzU0V^p=O;s>AVK6ZO0)>Lak_-mZ(zlf!|JqiEZCIXPnO`|oN&8Kzp@D&! wiHSmSW?p(us%}nZUaDS6MF~SsT~u1Vbh(84zm5YkwvSze7j9Aj0M2b5?sYFEz5oCK literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/8b/ccd5176c25898b57da2551e076f769054e0d8e b/modules/markup/tests/repo/repo1_filepreview/objects/8b/ccd5176c25898b57da2551e076f769054e0d8e new file mode 100644 index 0000000000000000000000000000000000000000..394a7bb50d7dcebdf407950c6e579b2a8ff18744 GIT binary patch literal 21 ccmbAVlXr?Ff%bxNXyJgRZ!8(O=0Lhb5%S?wX-|?mTKUGvhSI! me!P81iuBU+8CsOC@Cy~z$?c7Kuj2Xbz+CzA$V>nw>l|8&RVW<* literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/objects/c9/913120ed2c1e27c1d7752ecdb7a504dc7cf6be b/modules/markup/tests/repo/repo1_filepreview/objects/c9/913120ed2c1e27c1d7752ecdb7a504dc7cf6be new file mode 100644 index 0000000000000000000000000000000000000000..9fc2b7c3125937c08e184007dfd05822729a0e55 GIT binary patch literal 170 zcmV;b09F5Z0gaAJ3PLdq0A2SK*$dJp{a6t35PE>TG{u5H`l?>v4<5kPz`(%B^?Ysv zkZ>`&Dv;z*o!7vYCzLR8R?X~FDT2{)^*OGsCv)SjmjPZJa}9BlDObuxY7CVs2AeRh zgJms_q(sB_alCdo%-S7n?jP*tHgwf44?eZB1(zr#au^aUt+Uq1_igAO6(RaxW%fD` YsO_Y1>-uQ=g!gIDuH|dZ3;EhgOutoAssI20 literal 0 HcmV?d00001 diff --git a/modules/markup/tests/repo/repo1_filepreview/refs/heads/master b/modules/markup/tests/repo/repo1_filepreview/refs/heads/master index df25bf45f0..f3d5d39dd5 100644 --- a/modules/markup/tests/repo/repo1_filepreview/refs/heads/master +++ b/modules/markup/tests/repo/repo1_filepreview/refs/heads/master @@ -1 +1 @@ -4c1aaf56bcb9f39dcf65f3f250726850aed13cd6 +c9913120ed2c1e27c1d7752ecdb7a504dc7cf6be From 1430072c8c1246bbf5748d7b22f4988fbeb80d73 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 26 Jan 2025 07:37:50 +0000 Subject: [PATCH 005/669] [skip ci] chore: adjust i18n entries in CODEOWNERS (#6667) Matching non-en files would allow me to catch up with all Weblate PRs, which I sometimes completely miss. And we've got a brand new path for new strings. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6667 Reviewed-by: Otto Reviewed-by: Gusted Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- CODEOWNERS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 6ca34a69df..ff2a4b9fdd 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -33,8 +33,9 @@ models/.* @gusted # for code that lives in here. routers/.* @gusted -# Let new strings be checked by the translation team. -options/locale/locale_en-US.ini @0ko +# Let locale changes be checked by the translation team. +options/locale/.* @0ko +options/locale_next/.* @0ko # Personal interest .*/webhook.* @oliverpool From 63b4ac1e04fcd606ac663a591bb12e2770f918b9 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 12 Jan 2025 02:46:37 -0800 Subject: [PATCH 006/669] Fix mirror bug (#33224) Fix #33200 Co-authored-by: wxiaoguang (cherry picked from commit be4e961240883778c44d9651eaaf9ab8723bbbb0) --- modules/git/ref_test.go | 2 ++ services/mirror/mirror_pull.go | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/git/ref_test.go b/modules/git/ref_test.go index 58f679b7d6..1fd33b5163 100644 --- a/modules/git/ref_test.go +++ b/modules/git/ref_test.go @@ -20,6 +20,8 @@ func TestRefName(t *testing.T) { // Test pull names assert.Equal(t, "1", RefName("refs/pull/1/head").PullName()) + assert.True(t, RefName("refs/pull/1/head").IsPull()) + assert.True(t, RefName("refs/pull/1/merge").IsPull()) assert.Equal(t, "my/pull", RefName("refs/pull/my/pull/head").PullName()) // Test for branch names diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index bce8386f54..5ca18eb5cd 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -96,6 +96,7 @@ type mirrorSyncResult struct { /* // * [new tag] v0.1.8 -> v0.1.8 // * [new branch] master -> origin/master +// * [new ref] refs/pull/2/head -> refs/pull/2/head" // - [deleted] (none) -> origin/test // delete a branch // - [deleted] (none) -> 1 // delete a tag // 957a993..a87ba5f test -> origin/test @@ -126,6 +127,11 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult { refName: git.RefNameFromBranch(refName), oldCommitID: gitShortEmptySha, }) + case strings.HasPrefix(lines[i], " * [new ref]"): // new reference + results = append(results, &mirrorSyncResult{ + refName: git.RefName(refName), + oldCommitID: gitShortEmptySha, + }) case strings.HasPrefix(lines[i], " - "): // Delete reference isTag := !strings.HasPrefix(refName, remoteName+"/") var refFullName git.RefName @@ -168,8 +174,15 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult { log.Error("Expect two SHAs but not what found: %q", lines[i]) continue } + var refFullName git.RefName + if strings.HasPrefix(refName, "refs/") { + refFullName = git.RefName(refName) + } else { + refFullName = git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")) + } + results = append(results, &mirrorSyncResult{ - refName: git.RefNameFromBranch(strings.TrimPrefix(refName, remoteName+"/")), + refName: refFullName, oldCommitID: shas[0], newCommitID: shas[1], }) From 03e9e2ba5e2a9219cfd5550da11eab32b9accbe1 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 19 Jan 2025 10:00:06 +0100 Subject: [PATCH 007/669] Partial revert "Do not report warning when git shows new reference (#6540)" This reverts commit af5d96e2cc8d92f13f3fd17763562f3f1ef4c863. Except for the test case because Fix mirror bug (#33224) implements it now. --- services/mirror/mirror_pull.go | 1 - services/mirror/mirror_test.go | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 5ca18eb5cd..b180cf498f 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -187,7 +187,6 @@ func parseRemoteUpdateOutput(output, remoteName string) []*mirrorSyncResult { newCommitID: shas[1], }) - case strings.HasPrefix(lines[i], " * [new ref]"): // new reference - nothing to do default: log.Warn("parseRemoteUpdateOutput: unexpected update line %q", lines[i]) } diff --git a/services/mirror/mirror_test.go b/services/mirror/mirror_test.go index 860470522e..e26204e2ec 100644 --- a/services/mirror/mirror_test.go +++ b/services/mirror/mirror_test.go @@ -21,7 +21,7 @@ func Test_parseRemoteUpdateOutput(t *testing.T) { * [new ref] refs/pull/516/head -> refs/pull/516/head ` results := parseRemoteUpdateOutput(output, "origin") - assert.Len(t, results, 6) + assert.Len(t, results, 8) assert.EqualValues(t, "refs/tags/v0.1.8", results[0].refName.String()) assert.EqualValues(t, gitShortEmptySha, results[0].oldCommitID) assert.EqualValues(t, "", results[0].newCommitID) @@ -45,4 +45,12 @@ func Test_parseRemoteUpdateOutput(t *testing.T) { assert.EqualValues(t, "refs/heads/test3", results[5].refName.String()) assert.EqualValues(t, "957a993", results[5].oldCommitID) assert.EqualValues(t, "a87ba5f", results[5].newCommitID) + + assert.EqualValues(t, "refs/pull/27/merge", results[6].refName.String()) + assert.EqualValues(t, gitShortEmptySha, results[6].oldCommitID) + assert.EqualValues(t, "", results[6].newCommitID) + + assert.EqualValues(t, "refs/pull/516/head", results[7].refName.String()) + assert.EqualValues(t, gitShortEmptySha, results[7].oldCommitID) + assert.EqualValues(t, "", results[7].newCommitID) } From 55ff7245b67459e2c0cfd0783f8dd1ae660ec00c Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Wed, 15 Jan 2025 21:34:30 +0800 Subject: [PATCH 008/669] Move some Actions related functions from `routers` to `services` (#33280) Move the main logic of `generateTaskContext` and `findTaskNeeds` to the `services` layer. This is a part of #32751, since we need the git context and `needs` to parse the concurrency expressions. --------- Co-authored-by: Lunny Xiao Co-authored-by: wxiaoguang (cherry picked from commit d0962ce3da424f0a1df2acf595b20066d6e55128) Conflicts: routers/api/actions/runner/main_test.go routers/api/actions/runner/utils.go services/actions/context_test.go services/actions/init_test.go tests/integration/actions_job_test.go simple conflicts related to ref_type": string(refName.RefType()), // string, The type of ref that triggered the workflow run. Valid values are branch or tag. Use env GITEA_RUNNER_REGISTRATION_TOKEN as global runner token (#32946) --- routers/api/actions/runner/utils.go | 144 ++-------------- services/actions/context.go | 161 ++++++++++++++++++ .../actions/context_test.go | 7 +- tests/integration/actions_job_test.go | 95 +++++++++++ 4 files changed, 271 insertions(+), 136 deletions(-) create mode 100644 services/actions/context.go rename routers/api/actions/runner/utils_test.go => services/actions/context_test.go (77%) diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index 539be8d889..0fd7ca5c44 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -8,14 +8,8 @@ import ( "fmt" actions_model "code.gitea.io/gitea/models/actions" - "code.gitea.io/gitea/models/db" secret_model "code.gitea.io/gitea/models/secret" - actions_module "code.gitea.io/gitea/modules/actions" - "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/actions" runnerv1 "code.gitea.io/actions-proto-go/runner/v1" @@ -65,82 +59,16 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv } func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { - event := map[string]any{} - _ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event) - - // TriggerEvent is added in https://github.com/go-gitea/gitea/pull/25229 - // This fallback is for the old ActionRun that doesn't have the TriggerEvent field - // and should be removed in 1.22 - eventName := t.Job.Run.TriggerEvent - if eventName == "" { - eventName = t.Job.Run.Event.Event() - } - - baseRef := "" - headRef := "" - ref := t.Job.Run.Ref - sha := t.Job.Run.CommitSHA - if pullPayload, err := t.Job.Run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil { - baseRef = pullPayload.PullRequest.Base.Ref - headRef = pullPayload.PullRequest.Head.Ref - - // if the TriggerEvent is pull_request_target, ref and sha need to be set according to the base of pull request - // In GitHub's documentation, ref should be the branch or tag that triggered workflow. But when the TriggerEvent is pull_request_target, - // the ref will be the base branch. - if t.Job.Run.TriggerEvent == actions_module.GithubEventPullRequestTarget { - ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name - sha = pullPayload.PullRequest.Base.Sha - } - } - - refName := git.RefName(ref) - giteaRuntimeToken, err := actions.CreateAuthorizationToken(t.ID, t.Job.RunID, t.JobID) if err != nil { log.Error("actions.CreateAuthorizationToken failed: %v", err) } - taskContext, err := structpb.NewStruct(map[string]any{ - // standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context - "action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2. - "action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action. - "action_ref": "", // string, For a step executing an action, this is the ref of the action being executed. For example, v2. - "action_repository": "", // string, For a step executing an action, this is the owner and repository name of the action. For example, actions/checkout. - "action_status": "", // string, For a composite action, the current result of the composite action. - "actor": t.Job.Run.TriggerUser.Name, // string, The username of the user that triggered the initial workflow run. If the workflow run is a re-run, this value may differ from github.triggering_actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges. - "api_url": setting.AppURL + "api/v1", // string, The URL of the GitHub REST API. - "base_ref": baseRef, // string, The base_ref or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target. - "env": "", // string, Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions." - "event": event, // object, The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in "Events that trigger workflows." For example, for a workflow run triggered by the push event, this object contains the contents of the push webhook payload. - "event_name": eventName, // string, The name of the event that triggered the workflow run. - "event_path": "", // string, The path to the file on the runner that contains the full event webhook payload. - "graphql_url": "", // string, The URL of the GitHub GraphQL API. - "head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target. - "job": fmt.Sprint(t.JobID), // string, The job_id of the current job. - "ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/, for pull requests it is refs/pull//merge, and for tags it is refs/tags/. For example, refs/heads/feature-branch-1. - "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1. - "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run. - "ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag. - "path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions." - "repository": t.Job.Run.Repo.OwnerName + "/" + t.Job.Run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World. - "repository_owner": t.Job.Run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat. - "repositoryUrl": t.Job.Run.Repo.HTMLURL(), // string, The Git URL to the repository. For example, git://github.com/codertocat/hello-world.git. - "retention_days": "", // string, The number of days that workflow run logs and artifacts are kept. - "run_id": fmt.Sprint(t.Job.RunID), // string, A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run. - "run_number": fmt.Sprint(t.Job.Run.Index), // string, A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run. - "run_attempt": fmt.Sprint(t.Job.Attempt), // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run. - "secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces. - "server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com. - "sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53. - "token": t.Token, // string, A token to authenticate on behalf of the GitHub App installed on your repository. This is functionally equivalent to the GITHUB_TOKEN secret. For more information, see "Automatic token authentication." - "triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges. - "workflow": t.Job.Run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository. - "workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action. + gitCtx := actions.GenerateGiteaContext(t.Job.Run, t.Job) + gitCtx["token"] = t.Token + gitCtx["gitea_runtime_token"] = giteaRuntimeToken - // additional contexts - "gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(), - "gitea_runtime_token": giteaRuntimeToken, - }) + taskContext, err := structpb.NewStruct(gitCtx) if err != nil { log.Error("structpb.NewStruct failed: %v", err) } @@ -150,68 +78,18 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[string]*runnerv1.TaskNeed, error) { if err := task.LoadAttributes(ctx); err != nil { - return nil, fmt.Errorf("LoadAttributes: %w", err) + return nil, fmt.Errorf("task LoadAttributes: %w", err) } - if len(task.Job.Needs) == 0 { - return nil, nil - } - needs := container.SetOf(task.Job.Needs...) - - jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID}) + taskNeeds, err := actions.FindTaskNeeds(ctx, task.Job) if err != nil { - return nil, fmt.Errorf("FindRunJobs: %w", err) + return nil, err } - - jobIDJobs := make(map[string][]*actions_model.ActionRunJob) - for _, job := range jobs { - jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job) - } - - ret := make(map[string]*runnerv1.TaskNeed, len(needs)) - for jobID, jobsWithSameID := range jobIDJobs { - if !needs.Contains(jobID) { - continue - } - var jobOutputs map[string]string - for _, job := range jobsWithSameID { - if job.TaskID == 0 || !job.Status.IsDone() { - // it shouldn't happen, or the job has been rerun - continue - } - got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID) - if err != nil { - return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err) - } - outputs := make(map[string]string, len(got)) - for _, v := range got { - outputs[v.OutputKey] = v.OutputValue - } - if len(jobOutputs) == 0 { - jobOutputs = outputs - } else { - jobOutputs = mergeTwoOutputs(outputs, jobOutputs) - } - } + ret := make(map[string]*runnerv1.TaskNeed, len(taskNeeds)) + for jobID, taskNeed := range taskNeeds { ret[jobID] = &runnerv1.TaskNeed{ - Outputs: jobOutputs, - Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)), + Outputs: taskNeed.Outputs, + Result: runnerv1.Result(taskNeed.Result), } } - return ret, nil } - -// mergeTwoOutputs merges two outputs from two different ActionRunJobs -// Values with the same output name may be overridden. The user should ensure the output names are unique. -// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job -func mergeTwoOutputs(o1, o2 map[string]string) map[string]string { - ret := make(map[string]string, len(o1)) - for k1, v1 := range o1 { - if len(v1) > 0 { - ret[k1] = v1 - } else { - ret[k1] = o2[k1] - } - } - return ret -} diff --git a/services/actions/context.go b/services/actions/context.go new file mode 100644 index 0000000000..be1c85522b --- /dev/null +++ b/services/actions/context.go @@ -0,0 +1,161 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + "fmt" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + actions_module "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/setting" +) + +// GenerateGiteaContext generate the gitea context without token and gitea_runtime_token +// job can be nil when generating a context for parsing workflow-level expressions +func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.ActionRunJob) map[string]any { + event := map[string]any{} + _ = json.Unmarshal([]byte(run.EventPayload), &event) + + baseRef := "" + headRef := "" + ref := run.Ref + sha := run.CommitSHA + if pullPayload, err := run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil { + baseRef = pullPayload.PullRequest.Base.Ref + headRef = pullPayload.PullRequest.Head.Ref + + // if the TriggerEvent is pull_request_target, ref and sha need to be set according to the base of pull request + // In GitHub's documentation, ref should be the branch or tag that triggered workflow. But when the TriggerEvent is pull_request_target, + // the ref will be the base branch. + if run.TriggerEvent == actions_module.GithubEventPullRequestTarget { + ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name + sha = pullPayload.PullRequest.Base.Sha + } + } + + refName := git.RefName(ref) + + gitContext := map[string]any{ + // standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context + "action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2. + "action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action. + "action_ref": "", // string, For a step executing an action, this is the ref of the action being executed. For example, v2. + "action_repository": "", // string, For a step executing an action, this is the owner and repository name of the action. For example, actions/checkout. + "action_status": "", // string, For a composite action, the current result of the composite action. + "actor": run.TriggerUser.Name, // string, The username of the user that triggered the initial workflow run. If the workflow run is a re-run, this value may differ from github.triggering_actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges. + "api_url": setting.AppURL + "api/v1", // string, The URL of the GitHub REST API. + "base_ref": baseRef, // string, The base_ref or target branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target. + "env": "", // string, Path on the runner to the file that sets environment variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions." + "event": event, // object, The full event webhook payload. You can access individual properties of the event using this context. This object is identical to the webhook payload of the event that triggered the workflow run, and is different for each event. The webhooks for each GitHub Actions event is linked in "Events that trigger workflows." For example, for a workflow run triggered by the push event, this object contains the contents of the push webhook payload. + "event_name": run.TriggerEvent, // string, The name of the event that triggered the workflow run. + "event_path": "", // string, The path to the file on the runner that contains the full event webhook payload. + "graphql_url": "", // string, The URL of the GitHub GraphQL API. + "head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target. + "job": "", // string, The job_id of the current job. + "ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/, for pull requests it is refs/pull//merge, and for tags it is refs/tags/. For example, refs/heads/feature-branch-1. + "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1. + "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run. + "ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag. + "path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions." + "repository": run.Repo.OwnerName + "/" + run.Repo.Name, // string, The owner and repository name. For example, Codertocat/Hello-World. + "repository_owner": run.Repo.OwnerName, // string, The repository owner's name. For example, Codertocat. + "repositoryUrl": run.Repo.HTMLURL(), // string, The Git URL to the repository. For example, git://github.com/codertocat/hello-world.git. + "retention_days": "", // string, The number of days that workflow run logs and artifacts are kept. + "run_id": "", // string, A unique number for each workflow run within a repository. This number does not change if you re-run the workflow run. + "run_number": fmt.Sprint(run.Index), // string, A unique number for each run of a particular workflow in a repository. This number begins at 1 for the workflow's first run, and increments with each new run. This number does not change if you re-run the workflow run. + "run_attempt": "", // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run. + "secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces. + "server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com. + "sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53. + "triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges. + "workflow": run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository. + "workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action. + + // additional contexts + "gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(), + } + + if job != nil { + gitContext["job"] = job.JobID + gitContext["run_id"] = fmt.Sprint(job.RunID) + gitContext["run_attempt"] = fmt.Sprint(job.Attempt) + } + + return gitContext +} + +type TaskNeed struct { + Result actions_model.Status + Outputs map[string]string +} + +// FindTaskNeeds finds the `needs` for the task by the task's job +func FindTaskNeeds(ctx context.Context, job *actions_model.ActionRunJob) (map[string]*TaskNeed, error) { + if len(job.Needs) == 0 { + return nil, nil + } + needs := container.SetOf(job.Needs...) + + jobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: job.RunID}) + if err != nil { + return nil, fmt.Errorf("FindRunJobs: %w", err) + } + + jobIDJobs := make(map[string][]*actions_model.ActionRunJob) + for _, job := range jobs { + jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job) + } + + ret := make(map[string]*TaskNeed, len(needs)) + for jobID, jobsWithSameID := range jobIDJobs { + if !needs.Contains(jobID) { + continue + } + var jobOutputs map[string]string + for _, job := range jobsWithSameID { + if job.TaskID == 0 || !job.Status.IsDone() { + // it shouldn't happen, or the job has been rerun + continue + } + got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID) + if err != nil { + return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err) + } + outputs := make(map[string]string, len(got)) + for _, v := range got { + outputs[v.OutputKey] = v.OutputValue + } + if len(jobOutputs) == 0 { + jobOutputs = outputs + } else { + jobOutputs = mergeTwoOutputs(outputs, jobOutputs) + } + } + ret[jobID] = &TaskNeed{ + Outputs: jobOutputs, + Result: actions_model.AggregateJobStatus(jobsWithSameID), + } + } + return ret, nil +} + +// mergeTwoOutputs merges two outputs from two different ActionRunJobs +// Values with the same output name may be overridden. The user should ensure the output names are unique. +// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job +func mergeTwoOutputs(o1, o2 map[string]string) map[string]string { + ret := make(map[string]string, len(o1)) + for k1, v1 := range o1 { + if len(v1) > 0 { + ret[k1] = v1 + } else { + ret[k1] = o2[k1] + } + } + return ret +} diff --git a/routers/api/actions/runner/utils_test.go b/services/actions/context_test.go similarity index 77% rename from routers/api/actions/runner/utils_test.go rename to services/actions/context_test.go index c8a0a28d65..a80d2d84e3 100644 --- a/routers/api/actions/runner/utils_test.go +++ b/services/actions/context_test.go @@ -1,7 +1,7 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package runner +package actions import ( "context" @@ -14,12 +14,13 @@ import ( "github.com/stretchr/testify/require" ) -func Test_findTaskNeeds(t *testing.T) { +func TestFindTaskNeeds(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 51}) + job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: task.JobID}) - ret, err := findTaskNeeds(context.Background(), task) + ret, err := FindTaskNeeds(context.Background(), job) require.NoError(t, err) assert.Len(t, ret, 1) assert.Contains(t, ret, "job1") diff --git a/tests/integration/actions_job_test.go b/tests/integration/actions_job_test.go index 5af3519b93..545ab37bf6 100644 --- a/tests/integration/actions_job_test.go +++ b/tests/integration/actions_job_test.go @@ -4,22 +4,28 @@ package integration import ( + "context" "encoding/base64" "fmt" "net/http" "net/url" + "reflect" "testing" "time" actions_model "code.gitea.io/gitea/models/actions" auth_model "code.gitea.io/gitea/models/auth" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" runnerv1 "code.gitea.io/actions-proto-go/runner/v1" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestJobWithNeeds(t *testing.T) { @@ -354,6 +360,95 @@ jobs: }) } +func TestActionsGiteaContext(t *testing.T) { + if !setting.Database.Type.IsSQLite3() { + t.Skip() + } + + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + user2Session := loginUser(t, user2.Name) + user2Token := getTokenForLoggedInUser(t, user2Session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser) + + apiBaseRepo := createActionsTestRepo(t, user2Token, "actions-gitea-context", false) + baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: apiBaseRepo.ID}) + user2APICtx := NewAPITestContext(t, baseRepo.OwnerName, baseRepo.Name, auth_model.AccessTokenScopeWriteRepository) + + runner := newMockRunner() + runner.registerAsRepoRunner(t, baseRepo.OwnerName, baseRepo.Name, "mock-runner", []string{"ubuntu-latest"}) + + // init the workflow + wfTreePath := ".gitea/workflows/pull.yml" + wfFileContent := `name: Pull Request +on: pull_request +jobs: + wf1-job: + runs-on: ubuntu-latest + steps: + - run: echo 'test the pull' +` + opts := getWorkflowCreateFileOptions(user2, baseRepo.DefaultBranch, fmt.Sprintf("create %s", wfTreePath), wfFileContent) + createWorkflowFile(t, user2Token, baseRepo.OwnerName, baseRepo.Name, wfTreePath, opts) + // user2 creates a pull request + doAPICreateFile(user2APICtx, "user2-patch.txt", &api.CreateFileOptions{ + FileOptions: api.FileOptions{ + NewBranchName: "user2/patch-1", + Message: "create user2-patch.txt", + Author: api.Identity{ + Name: user2.Name, + Email: user2.Email, + }, + Committer: api.Identity{ + Name: user2.Name, + Email: user2.Email, + }, + Dates: api.CommitDateOptions{ + Author: time.Now(), + Committer: time.Now(), + }, + }, + ContentBase64: base64.StdEncoding.EncodeToString([]byte("user2-fix")), + })(t) + apiPull, err := doAPICreatePullRequest(user2APICtx, baseRepo.OwnerName, baseRepo.Name, baseRepo.DefaultBranch, "user2/patch-1")(t) + require.NoError(t, err) + task := runner.fetchTask(t) + gtCtx := task.Context.GetFields() + actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: task.Id}) + actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID}) + actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID}) + require.NoError(t, actionRun.LoadAttributes(context.Background())) + + assert.Equal(t, user2.Name, gtCtx["actor"].GetStringValue()) + assert.Equal(t, setting.AppURL+"api/v1", gtCtx["api_url"].GetStringValue()) + assert.Equal(t, apiPull.Base.Ref, gtCtx["base_ref"].GetStringValue()) + runEvent := map[string]any{} + require.NoError(t, json.Unmarshal([]byte(actionRun.EventPayload), &runEvent)) + assert.True(t, reflect.DeepEqual(gtCtx["event"].GetStructValue().AsMap(), runEvent)) + assert.Equal(t, actionRun.TriggerEvent, gtCtx["event_name"].GetStringValue()) + assert.Equal(t, apiPull.Head.Ref, gtCtx["head_ref"].GetStringValue()) + assert.Equal(t, actionRunJob.JobID, gtCtx["job"].GetStringValue()) + assert.Equal(t, actionRun.Ref, gtCtx["ref"].GetStringValue()) + assert.Equal(t, (git.RefName(actionRun.Ref)).ShortName(), gtCtx["ref_name"].GetStringValue()) + assert.False(t, gtCtx["ref_protected"].GetBoolValue()) + assert.Equal(t, (git.RefName(actionRun.Ref)).RefType(), gtCtx["ref_type"].GetStringValue()) + assert.Equal(t, actionRun.Repo.OwnerName+"/"+actionRun.Repo.Name, gtCtx["repository"].GetStringValue()) + assert.Equal(t, actionRun.Repo.OwnerName, gtCtx["repository_owner"].GetStringValue()) + assert.Equal(t, actionRun.Repo.HTMLURL(), gtCtx["repositoryUrl"].GetStringValue()) + assert.Equal(t, fmt.Sprint(actionRunJob.RunID), gtCtx["run_id"].GetStringValue()) + assert.Equal(t, fmt.Sprint(actionRun.Index), gtCtx["run_number"].GetStringValue()) + assert.Equal(t, fmt.Sprint(actionRunJob.Attempt), gtCtx["run_attempt"].GetStringValue()) + assert.Equal(t, "Actions", gtCtx["secret_source"].GetStringValue()) + assert.Equal(t, setting.AppURL, gtCtx["server_url"].GetStringValue()) + assert.Equal(t, actionRun.CommitSHA, gtCtx["sha"].GetStringValue()) + assert.Equal(t, actionRun.WorkflowID, gtCtx["workflow"].GetStringValue()) + assert.Equal(t, setting.Actions.DefaultActionsURL.URL(), gtCtx["gitea_default_actions_url"].GetStringValue()) + token := gtCtx["token"].GetStringValue() + assert.Equal(t, actionTask.TokenLastEight, token[len(token)-8:]) + + doAPIDeleteRepository(user2APICtx)(t) + }) +} + func createActionsTestRepo(t *testing.T, authToken, repoName string, isPrivate bool) *api.Repository { req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{ Name: repoName, From 97d7ba5911cece77b570d9cae78c2d48fbd0272b Mon Sep 17 00:00:00 2001 From: hiifong Date: Sun, 19 Jan 2025 10:51:43 +0800 Subject: [PATCH 009/669] Fix parentCommit invalid memory address or nil pointer dereference. (#33204) When the parent Commit does not exist on gitea, an error will be reported when opening the Commit details page: invalid memory address or nil pointer dereference. ![image](https://github.com/user-attachments/assets/4c2a9802-935f-41e9-b5b9-a4f0d745f709) ![image](https://github.com/user-attachments/assets/7b0bc15e-7f5f-4d58-8d24-fee667a799fa) (cherry picked from commit b7614e2d2f2af246e33b8bd6a38ecfd1babc9ecc) --- modules/git/diff.go | 10 ++++++++-- services/gitdiff/gitdiff.go | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/modules/git/diff.go b/modules/git/diff.go index d9f3f6dda9..8374101d2a 100644 --- a/modules/git/diff.go +++ b/modules/git/diff.go @@ -64,7 +64,10 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff } else if commit.ParentCount() == 0 { cmd.AddArguments("show").AddDynamicArguments(endCommit).AddDashesAndList(files...) } else { - c, _ := commit.Parent(0) + c, err := commit.Parent(0) + if err != nil { + return err + } cmd.AddArguments("diff", "-M").AddDynamicArguments(c.ID.String(), endCommit).AddDashesAndList(files...) } case RawDiffPatch: @@ -74,7 +77,10 @@ func GetRepoRawDiffForFile(repo *Repository, startCommit, endCommit string, diff } else if commit.ParentCount() == 0 { cmd.AddArguments("format-patch", "--no-signature", "--stdout", "--root").AddDynamicArguments(endCommit).AddDashesAndList(files...) } else { - c, _ := commit.Parent(0) + c, err := commit.Parent(0) + if err != nil { + return err + } query := fmt.Sprintf("%s...%s", endCommit, c.ID.String()) cmd.AddArguments("format-patch", "--no-signature", "--stdout").AddDynamicArguments(query).AddDashesAndList(files...) } diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 7d137fb214..65f6ac8d12 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -1117,7 +1117,10 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi } else { actualBeforeCommitID := opts.BeforeCommitID if len(actualBeforeCommitID) == 0 { - parentCommit, _ := commit.Parent(0) + parentCommit, err := commit.Parent(0) + if err != nil { + return nil, err + } actualBeforeCommitID = parentCommit.ID.String() } @@ -1126,7 +1129,6 @@ func GetDiff(ctx context.Context, gitRepo *git.Repository, opts *DiffOptions, fi AddDynamicArguments(actualBeforeCommitID, opts.AfterCommitID) opts.BeforeCommitID = actualBeforeCommitID - var err error beforeCommit, err = gitRepo.GetCommit(opts.BeforeCommitID) if err != nil { return nil, err From 227a84fc8fc4f1bc0b12a8c0b3ccb439336e9ddd Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 26 Jan 2025 13:30:00 +0000 Subject: [PATCH 010/669] chore: remove usages of `sort.Sort` (#6689) improve language stats rounding: - Add tests (I had to omit some edge cases as the current method is non-determistic in some cases, due to random order of map access). - Document the algorithm used. - Lower the amount of calculations that need to be done. - Because of the aforementioned non-determistic don't use stable sort and instead regular sort. better sorting for `RepositoryList`: - Add testing - Use `slices.Sortfunc` instead of `sort.Sort`. - Remove the methods needed for `sort.Sort`. better git tag sorter: - Use `slices.SortFunc` instead of `sort.Sort`. - Remove `tagSorter` and its related methods. - Added testing. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6689 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- models/repo/language_stats.go | 34 ++++++++------- models/repo/language_stats_test.go | 66 ++++++++++++++++++++++++++++++ models/repo/repo_list.go | 12 ------ modules/git/repo_tag.go | 5 ++- modules/git/repo_tag_test.go | 3 ++ modules/git/tag.go | 21 ---------- routers/web/user/home.go | 5 ++- routers/web/user/home_test.go | 2 + 8 files changed, 97 insertions(+), 51 deletions(-) create mode 100644 models/repo/language_stats_test.go diff --git a/models/repo/language_stats.go b/models/repo/language_stats.go index 0bc0f1fb40..d44fea5375 100644 --- a/models/repo/language_stats.go +++ b/models/repo/language_stats.go @@ -4,9 +4,10 @@ package repo import ( + "cmp" "context" "math" - "sort" + "slices" "strings" "code.gitea.io/gitea/models/db" @@ -67,34 +68,37 @@ func (stats LanguageStatList) getLanguagePercentages() map[string]float32 { return langPerc } -// Rounds to 1 decimal point, target should be the expected sum of percs +// Use the quota method to round the percentages to one decimal place while +// keeping the sum of the percentages at 100%. func roundByLargestRemainder(percs map[string]float32, target float32) { + // Tracks the difference between the sum of percentage and 100%. leftToDistribute := int(target * 10) - keys := make([]string, 0, len(percs)) + type key struct { + language string + remainder float64 + } + keys := make([]key, 0, len(percs)) for k, v := range percs { - percs[k] = v * 10 - floored := math.Floor(float64(percs[k])) + floored, frac := math.Modf(float64(v * 10)) + percs[k] = float32(floored) leftToDistribute -= int(floored) - keys = append(keys, k) + keys = append(keys, key{language: k, remainder: frac}) } - // Sort the keys by the largest remainder - sort.SliceStable(keys, func(i, j int) bool { - _, remainderI := math.Modf(float64(percs[keys[i]])) - _, remainderJ := math.Modf(float64(percs[keys[j]])) - return remainderI > remainderJ + // Sort the fractional part in an ascending order. + slices.SortFunc(keys, func(b, a key) int { + return cmp.Compare(a.remainder, b.remainder) }) - // Increment the values in order of largest remainder + // As long as the sum of 100% is not reached, add 0.1% percentage. for _, k := range keys { - percs[k] = float32(math.Floor(float64(percs[k]))) if leftToDistribute > 0 { - percs[k]++ + percs[k.language]++ leftToDistribute-- } - percs[k] /= 10 + percs[k.language] /= 10 } } diff --git a/models/repo/language_stats_test.go b/models/repo/language_stats_test.go new file mode 100644 index 0000000000..dcfaeee6c9 --- /dev/null +++ b/models/repo/language_stats_test.go @@ -0,0 +1,66 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package repo + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestLanguagePercentages(t *testing.T) { + testCases := []struct { + input LanguageStatList + output map[string]float32 + }{ + { + []*LanguageStat{{Language: "Go", Size: 500}, {Language: "Rust", Size: 501}}, + map[string]float32{ + "Go": 50.0, + "Rust": 50.0, + }, + }, + { + []*LanguageStat{{Language: "Go", Size: 10}, {Language: "Rust", Size: 91}}, + map[string]float32{ + "Go": 9.9, + "Rust": 90.1, + }, + }, + { + []*LanguageStat{{Language: "Go", Size: 1}, {Language: "Rust", Size: 2}}, + map[string]float32{ + "Go": 33.3, + "Rust": 66.7, + }, + }, + { + []*LanguageStat{{Language: "Go", Size: 1}, {Language: "Rust", Size: 2}, {Language: "Shell", Size: 3}, {Language: "C#", Size: 4}, {Language: "Zig", Size: 5}, {Language: "Coq", Size: 6}, {Language: "Haskell", Size: 7}}, + map[string]float32{ + "Go": 3.6, + "Rust": 7.1, + "Shell": 10.7, + "C#": 14.3, + "Zig": 17.9, + "Coq": 21.4, + "Haskell": 25, + }, + }, + { + []*LanguageStat{{Language: "Go", Size: 1000}, {Language: "PHP", Size: 1}, {Language: "Java", Size: 1}}, + map[string]float32{ + "Go": 99.8, + "other": 0.2, + }, + }, + { + []*LanguageStat{}, + map[string]float32{}, + }, + } + + for _, testCase := range testCases { + assert.Equal(t, testCase.output, testCase.input.getLanguagePercentages()) + } +} diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index fc51f64f6a..693f8f12af 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -36,18 +36,6 @@ const RepositoryListDefaultPageSize = 64 // RepositoryList contains a list of repositories type RepositoryList []*Repository -func (repos RepositoryList) Len() int { - return len(repos) -} - -func (repos RepositoryList) Less(i, j int) bool { - return repos[i].FullName() < repos[j].FullName() -} - -func (repos RepositoryList) Swap(i, j int) { - repos[i], repos[j] = repos[j], repos[i] -} - // ValuesRepository converts a repository map to a list // FIXME: Remove in favor of maps.values when MIN_GO_VERSION >= 1.18 func ValuesRepository(m map[int64]*Repository) []*Repository { diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index 12b0c022cb..3b48b1fb9b 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "slices" "strings" "code.gitea.io/gitea/modules/git/foreachref" @@ -153,7 +154,9 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) { return nil, 0, fmt.Errorf("GetTagInfos: parse output: %w", err) } - sortTagsByTime(tags) + slices.SortFunc(tags, func(b, a *Tag) int { + return a.Tagger.When.Compare(b.Tagger.When) + }) tagsTotal := len(tags) if page != 0 { tags = util.PaginateSlice(tags, page, pageSize).([]*Tag) diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index 1cf420ad63..a4b13bf03d 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -6,6 +6,7 @@ package git import ( "path/filepath" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -30,9 +31,11 @@ func TestRepository_GetTags(t *testing.T) { assert.EqualValues(t, "signed-tag", tags[0].Name) assert.EqualValues(t, "36f97d9a96457e2bab511db30fe2db03893ebc64", tags[0].ID.String()) assert.EqualValues(t, "tag", tags[0].Type) + assert.EqualValues(t, time.Date(2022, time.November, 13, 16, 40, 20, 0, time.FixedZone("", 3600)), tags[0].Tagger.When) assert.EqualValues(t, "test", tags[1].Name) assert.EqualValues(t, "3ad28a9149a2864384548f3d17ed7f38014c9e8a", tags[1].ID.String()) assert.EqualValues(t, "tag", tags[1].Type) + assert.EqualValues(t, time.Date(2018, time.June, 16, 20, 13, 18, 0, time.FixedZone("", -25200)), tags[1].Tagger.When) } func TestRepository_GetTag(t *testing.T) { diff --git a/modules/git/tag.go b/modules/git/tag.go index 04f50e8db8..34ce8f6fc3 100644 --- a/modules/git/tag.go +++ b/modules/git/tag.go @@ -5,7 +5,6 @@ package git import ( "bytes" - "sort" "strings" api "code.gitea.io/gitea/modules/structs" @@ -107,23 +106,3 @@ l: return tag, nil } - -type tagSorter []*Tag - -func (ts tagSorter) Len() int { - return len([]*Tag(ts)) -} - -func (ts tagSorter) Less(i, j int) bool { - return []*Tag(ts)[i].Tagger.When.After([]*Tag(ts)[j].Tagger.When) -} - -func (ts tagSorter) Swap(i, j int) { - []*Tag(ts)[i], []*Tag(ts)[j] = []*Tag(ts)[j], []*Tag(ts)[i] -} - -// sortTagsByTime -func sortTagsByTime(tags []*Tag) { - sorter := tagSorter(tags) - sort.Sort(sorter) -} diff --git a/routers/web/user/home.go b/routers/web/user/home.go index c59dcf5c25..d67af29071 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -10,7 +10,6 @@ import ( "net/http" "regexp" "slices" - "sort" "strconv" "strings" @@ -242,7 +241,9 @@ func Milestones(ctx *context.Context) { ctx.ServerError("SearchRepositoryByCondition", err) return } - sort.Sort(showRepos) + slices.SortFunc(showRepos, func(a, b *repo_model.Repository) int { + return strings.Compare(a.FullName(), b.FullName()) + }) for i := 0; i < len(milestones); { for _, repo := range showRepos { diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go index e1c8ca9a79..c09f609161 100644 --- a/routers/web/user/home_test.go +++ b/routers/web/user/home_test.go @@ -98,6 +98,8 @@ func TestMilestones(t *testing.T) { assert.EqualValues(t, 1, ctx.Data["Total"]) assert.Len(t, ctx.Data["Milestones"], 1) assert.Len(t, ctx.Data["Repos"], 2) // both repo 42 and 1 have milestones and both are owned by user 2 + assert.EqualValues(t, "user2/glob", ctx.Data["Repos"].(repo_model.RepositoryList)[0].FullName()) + assert.EqualValues(t, "user2/repo1", ctx.Data["Repos"].(repo_model.RepositoryList)[1].FullName()) } func TestMilestonesForSpecificRepo(t *testing.T) { From 9e293864338e83c6d5b3ae19f49ccd581a3d44a5 Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Sun, 26 Jan 2025 17:04:44 +0000 Subject: [PATCH 011/669] nit(i18n): update password update instruction (#6686) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6686 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Panagiotis "Ivory" Vasilopoulos Co-committed-by: Panagiotis "Ivory" Vasilopoulos --- options/locale/locale_en-US.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7001c1bf0f..da2457ccb7 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -820,7 +820,7 @@ old_password = Current password new_password = New password retype_new_password = Confirm new password password_incorrect = The current password is incorrect. -change_password_success = Your password has been updated. Sign in using your new password from now on. +change_password_success = Your password has been updated. From now on, use your new password to sign in. password_change_disabled = Non-local users cannot update their password through the Forgejo web interface. manage_emails = Manage email addresses From 31a61506b9bd663488b27a55dadad960aa19f3c4 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Mon, 27 Jan 2025 14:06:17 +0100 Subject: [PATCH 012/669] chore: Update renovate to v39.136.0 --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 632fb82c19..ce9058cc77 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -25,7 +25,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:39.106.0 + image: data.forgejo.org/renovate/renovate:39.136.0 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 5865262d1a..08b1874a66 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.29.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.115.4 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@39.136.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 00e0b8261db70c53990906d23e59d9c3e82a82a1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 27 Jan 2025 16:02:25 +0000 Subject: [PATCH 013/669] Update renovate to v39.136.1 (forgejo) (#6695) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6695 Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index ce9058cc77..f72aaa5803 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -25,7 +25,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:39.136.0 + image: data.forgejo.org/renovate/renovate:39.136.1 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index 08b1874a66..8ea7062f57 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.29.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.17.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.136.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@39.136.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From d87e4124c1d71e496eaa49e129b01a4199817795 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 28 Jan 2025 05:50:40 +0000 Subject: [PATCH 014/669] Update module github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker to v3.2.0 (forgejo) (#6699) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6699 Reviewed-by: Michael Kriese Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8ea7062f57..151e752545 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ endif XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go -EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.1.1 # renovate: datasource=go +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go From 00be5f083c9776901a8dc06669cdff99ca2f45af Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 25 Jan 2025 02:27:36 +0800 Subject: [PATCH 015/669] Do not access GitRepo when a repo is being created (#33380) (cherry picked from commit 06ff9b6256824a2dfee18adff5a8540412b22641) Conflicts: services/context/repo.go trivial context conflict --- services/context/repo.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/services/context/repo.go b/services/context/repo.go index d294c00455..ff03844c03 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -937,6 +937,9 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { // of repository reference func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context) context.CancelFunc { return func(ctx *Context) (cancel context.CancelFunc) { + if ctx.Repo.Repository.IsBeingCreated() { + return nil // no git repo, so do nothing + } // Empty repository does not have reference information. if ctx.Repo.Repository.IsEmpty { // assume the user is viewing the (non-existent) default branch From 65e591bc13aa93d711aee6c5069780d65245f7a3 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Tue, 28 Jan 2025 15:41:11 +0000 Subject: [PATCH 016/669] chore: consistent docker image and action references (#6703) - replace `code.forgejo.org` ->`data.forgejo.org` on docker images - add `https://data.forgejo.org/` to actions where missing Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6703 Reviewed-by: Earl Warren Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- .forgejo/workflows-composite/build-backend/action.yaml | 2 +- .forgejo/workflows-composite/setup-cache-go/action.yaml | 2 +- .forgejo/workflows/build-release-integration.yml | 2 +- .forgejo/workflows/build-release.yml | 2 +- .forgejo/workflows/cascade-setup-end-to-end.yml | 4 ++-- .forgejo/workflows/publish-release.yml | 2 +- .forgejo/workflows/testing.yml | 4 ++-- Dockerfile | 6 +++--- Dockerfile.rootless | 6 +++--- renovate.json | 1 + 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/.forgejo/workflows-composite/build-backend/action.yaml b/.forgejo/workflows-composite/build-backend/action.yaml index ada372b834..68a99ffaf9 100644 --- a/.forgejo/workflows-composite/build-backend/action.yaml +++ b/.forgejo/workflows-composite/build-backend/action.yaml @@ -3,7 +3,7 @@ runs: steps: - run: | su forgejo -c 'make deps-backend' - - uses: actions/cache@v4 + - uses: https://data.forgejo.org/actions/cache@v4 id: cache-backend with: path: ${{github.workspace}}/gitea diff --git a/.forgejo/workflows-composite/setup-cache-go/action.yaml b/.forgejo/workflows-composite/setup-cache-go/action.yaml index 1b1d37bb6b..cc21485d14 100644 --- a/.forgejo/workflows-composite/setup-cache-go/action.yaml +++ b/.forgejo/workflows-composite/setup-cache-go/action.yaml @@ -49,7 +49,7 @@ runs: - name: "Restore Go dependencies from cache or mark for later caching" id: cache-deps - uses: actions/cache@v4 + uses: https://data.forgejo.org/actions/cache@v4 with: key: setup-cache-go-deps-${{ runner.os }}-${{ inputs.username }}-${{ steps.go-version.outputs.go_version }}-${{ hashFiles('go.sum', 'go.mod') }} restore-keys: | diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index 6410915644..1af6d567dd 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -25,7 +25,7 @@ jobs: if: vars.ROLE == 'forgejo-coding' runs-on: lxc-bookworm steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 - id: forgejo uses: https://data.forgejo.org/actions/setup-forgejo@v2.0.4 diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index 9d88cb43dd..0d7f94c5a6 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -33,7 +33,7 @@ jobs: # root is used for testing, allow it if: vars.ROLE == 'forgejo-integration' || github.repository_owner == 'root' steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 with: fetch-depth: 0 diff --git a/.forgejo/workflows/cascade-setup-end-to-end.yml b/.forgejo/workflows/cascade-setup-end-to-end.yml index 710cd27ba4..bcc7821f4f 100644 --- a/.forgejo/workflows/cascade-setup-end-to-end.yml +++ b/.forgejo/workflows/cascade-setup-end-to-end.yml @@ -37,11 +37,11 @@ jobs: container: image: data.forgejo.org/oci/node:20-bookworm steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 with: fetch-depth: '0' show-progress: 'false' - - uses: https://code.forgejo.org/actions/cascading-pr@v2.2.0 + - uses: https://data.forgejo.org/actions/cascading-pr@v2.2.0 with: origin-url: ${{ env.GITHUB_SERVER_URL }} origin-repo: ${{ github.repository }} diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 0e75912a3a..b44d670fc4 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -39,7 +39,7 @@ jobs: runs-on: lxc-bookworm if: vars.DOER != '' && vars.FORGEJO != '' && vars.TO_OWNER != '' && vars.FROM_OWNER != '' && secrets.TOKEN != '' steps: - - uses: actions/checkout@v4 + - uses: https://data.forgejo.org/actions/checkout@v4 - name: copy & sign uses: https://data.forgejo.org/forgejo/forgejo-build-publish/publish@v5.3.1 diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index eb3163d3ae..784bc45736 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -46,7 +46,7 @@ jobs: apt-get update -qq apt-get -q install -qq -y zstd - name: "Cache frontend build for playwright testing" - uses: actions/cache/save@v4 + uses: https://data.forgejo.org/actions/cache/save@v4 with: path: ${{github.workspace}}/public/assets key: frontend-build-${{ github.sha }} @@ -104,7 +104,7 @@ jobs: fetch-depth: 20 - uses: ./.forgejo/workflows-composite/setup-env - name: "Restore frontend build" - uses: actions/cache/restore@v4 + uses: https://data.forgejo.org/actions/cache/restore@v4 id: cache-frontend with: path: ${{github.workspace}}/public/assets diff --git a/Dockerfile b/Dockerfile index 3f7f3e7d1f..2df13c2326 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.21 as build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.21 as build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-direct} @@ -51,7 +51,7 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \ /go/src/code.gitea.io/gitea/environment-to-ini RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete -FROM code.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.21 ARG RELEASE_VERSION LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 129bd6f1ba..8aec4d828f 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -1,6 +1,6 @@ -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/xx AS xx +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/xx AS xx -FROM --platform=$BUILDPLATFORM code.forgejo.org/oci/golang:1.23-alpine3.21 as build-env +FROM --platform=$BUILDPLATFORM data.forgejo.org/oci/golang:1.23-alpine3.21 as build-env ARG GOPROXY ENV GOPROXY=${GOPROXY:-direct} @@ -49,7 +49,7 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \ /go/src/code.gitea.io/gitea/environment-to-ini RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete -FROM code.forgejo.org/oci/alpine:3.21 +FROM data.forgejo.org/oci/alpine:3.21 LABEL maintainer="contact@forgejo.org" \ org.opencontainers.image.authors="Forgejo" \ org.opencontainers.image.url="https://forgejo.org" \ diff --git a/renovate.json b/renovate.json index c34ae1aaba..7efe4123b0 100644 --- a/renovate.json +++ b/renovate.json @@ -62,6 +62,7 @@ "description": "Group nodejs packages", "matchPackageNames": [ "code.forgejo.org/oci/node", + "data.forgejo.org/oci/node", "docker.io/library/node", "docker.io/node", "node" From 3826a8429bb430a5d4f4f68932c93b1d8c71d481 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 28 Jan 2025 15:03:38 +0100 Subject: [PATCH 017/669] fix(i18n): use translate key as fallback - If the translate key is nonsense (not seen in any of the languages) then the translate key as-is should be returned as value, this helps during development. Currently it displays the first entry of the locale store which is "Home". - Regression from forgejo/forgejo#6203. - Added unit test. --- modules/translation/i18n/i18n_test.go | 1 + modules/translation/i18n/localestore.go | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go index 41f85931aa..66f5d5c402 100644 --- a/modules/translation/i18n/i18n_test.go +++ b/modules/translation/i18n/i18n_test.go @@ -158,6 +158,7 @@ commits = fallback value for commits found := lang1.HasKey("no-such") assert.False(t, found) + assert.EqualValues(t, "no-such", lang1.TrString("no-such")) require.NoError(t, ls.Close()) } diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index e80b2592ae..a38e967838 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -225,9 +225,9 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { format = msg } else { // First fallback: old-style translation - idx, ok := l.store.trKeyToIdxMap[trKey] + idx, foundIndex := l.store.trKeyToIdxMap[trKey] found := false - if ok { + if foundIndex { if msg, ok := l.idxToMsgMap[idx]; ok { format = msg // use the found translation found = true @@ -239,7 +239,7 @@ func (l *locale) TrString(trKey string, trArgs ...any) string { if defaultLang, ok := l.store.localeMap[l.store.defaultLang]; ok { if msg := defaultLang.LookupNewStyleMessage(trKey); msg != "" { format = msg - } else { + } else if foundIndex { // Third fallback: old-style default language if msg, ok := defaultLang.idxToMsgMap[idx]; ok { format = msg From 6d944db1d5494dbd9f3d9720aa0b39a3f34a37a7 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 29 Jan 2025 07:44:38 +0000 Subject: [PATCH 018/669] fix: render issue titles consistently (#6715) - Render the issue titles in dashboard feed in consistent manner, by using the existing `RenderIssueTitle`. - Added integration tests (not exhaustive for all comment types, but exhaustive enough for the current code where some comment types are grouped together). - Resolves forgejo/forgejo#6705 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6715 Reviewed-by: Earl Warren Co-authored-by: Gusted Co-committed-by: Gusted --- templates/user/dashboard/feeds.tmpl | 8 ++-- tests/integration/pull_icon_test.go | 15 +++---- tests/integration/pull_review_test.go | 10 +++-- tests/integration/user_dashboard_test.go | 51 ++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/templates/user/dashboard/feeds.tmpl b/templates/user/dashboard/feeds.tmpl index bd2a3800a2..85ae7266d9 100644 --- a/templates/user/dashboard/feeds.tmpl +++ b/templates/user/dashboard/feeds.tmpl @@ -103,11 +103,11 @@ {{ctx.Locale.Tr "action.compare_commits" $push.Len}} » {{end}} {{else if .GetOpType.InActions "create_issue"}} - {{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (index .GetIssueInfos 1) (.Repo.ComposeMetas ctx)}} {{else if .GetOpType.InActions "create_pull_request"}} - {{index .GetIssueInfos 1 | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (index .GetIssueInfos 1) (.Repo.ComposeMetas ctx)}} {{else if .GetOpType.InActions "comment_issue" "approve_pull_request" "reject_pull_request" "comment_pull"}} - {{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (.GetIssueTitle ctx) (.Repo.ComposeMetas ctx)}} {{$comment := index .GetIssueInfos 1}} {{if $comment}}
{{RenderMarkdownToHtml ctx $comment}}
@@ -115,7 +115,7 @@ {{else if .GetOpType.InActions "merge_pull_request"}}
{{index .GetIssueInfos 1}}
{{else if .GetOpType.InActions "close_issue" "reopen_issue" "close_pull_request" "reopen_pull_request"}} - {{(.GetIssueTitle ctx) | RenderEmoji $.Context | RenderCodeBlock}} + {{RenderIssueTitle ctx (.GetIssueTitle ctx) (.Repo.ComposeMetas ctx)}} {{else if .GetOpType.InActions "pull_review_dismissed"}}
{{ctx.Locale.Tr "action.review_dismissed_reason"}}
{{index .GetIssueInfos 2 | RenderEmoji $.Context}}
diff --git a/tests/integration/pull_icon_test.go b/tests/integration/pull_icon_test.go index 8fde547ce9..b678550c30 100644 --- a/tests/integration/pull_icon_test.go +++ b/tests/integration/pull_icon_test.go @@ -133,7 +133,7 @@ func testPullRequestListIcon(t *testing.T, doc *HTMLDoc, name, expectedColor, ex } func createOpenPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "open") + pull := createPullRequest(t, user, repo, "branch-open", "open") assert.False(t, pull.Issue.IsClosed) assert.False(t, pull.HasMerged) @@ -143,7 +143,7 @@ func createOpenPullRequest(ctx context.Context, t *testing.T, user *user_model.U } func createOpenWipPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "open-wip") + pull := createPullRequest(t, user, repo, "branch-open-wip", "open-wip") err := issue_service.ChangeTitle(ctx, pull.Issue, user, "WIP: "+pull.Issue.Title) require.NoError(t, err) @@ -156,7 +156,7 @@ func createOpenWipPullRequest(ctx context.Context, t *testing.T, user *user_mode } func createClosedPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "closed") + pull := createPullRequest(t, user, repo, "branch-closed", "closed") err := issue_service.ChangeStatus(ctx, pull.Issue, user, "", true) require.NoError(t, err) @@ -169,7 +169,7 @@ func createClosedPullRequest(ctx context.Context, t *testing.T, user *user_model } func createClosedWipPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "closed-wip") + pull := createPullRequest(t, user, repo, "branch-closed-wip", "closed-wip") err := issue_service.ChangeTitle(ctx, pull.Issue, user, "WIP: "+pull.Issue.Title) require.NoError(t, err) @@ -185,7 +185,7 @@ func createClosedWipPullRequest(ctx context.Context, t *testing.T, user *user_mo } func createMergedPullRequest(ctx context.Context, t *testing.T, user *user_model.User, repo *repo_model.Repository) *issues_model.PullRequest { - pull := createPullRequest(t, user, repo, "merged") + pull := createPullRequest(t, user, repo, "branch-merged", "merged") gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) defer gitRepo.Close() @@ -202,10 +202,7 @@ func createMergedPullRequest(ctx context.Context, t *testing.T, user *user_model return pull } -func createPullRequest(t *testing.T, user *user_model.User, repo *repo_model.Repository, name string) *issues_model.PullRequest { - branch := "branch-" + name - title := "Testing " + name - +func createPullRequest(t *testing.T, user *user_model.User, repo *repo_model.Repository, branch, title string) *issues_model.PullRequest { _, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, user, &files_service.ChangeRepoFilesOptions{ Files: []*files_service.ChangeRepoFile{ { diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 1319db29bf..e1db171f16 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -518,7 +518,7 @@ func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) { resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title") elem := strings.Split(test.RedirectURL(resp), "/") assert.EqualValues(t, "pulls", elem[3]) - testIssueClose(t, user1Session, elem[1], elem[2], elem[4]) + testIssueClose(t, user1Session, elem[1], elem[2], elem[4], true) // Get the commit SHA pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ @@ -579,8 +579,12 @@ func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pul return session.MakeRequest(t, req, expectedSubmitStatus) } -func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string) *httptest.ResponseRecorder { - req := NewRequest(t, "GET", path.Join(owner, repo, "pulls", issueNumber)) +func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string, isPull bool) *httptest.ResponseRecorder { + issueType := "issues" + if isPull { + issueType = "pulls" + } + req := NewRequest(t, "GET", path.Join(owner, repo, issueType, issueNumber)) resp := session.MakeRequest(t, req, http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) diff --git a/tests/integration/user_dashboard_test.go b/tests/integration/user_dashboard_test.go index abc3e065d9..0ed5193c48 100644 --- a/tests/integration/user_dashboard_test.go +++ b/tests/integration/user_dashboard_test.go @@ -5,12 +5,21 @@ package integration import ( "net/http" + "net/url" + "strconv" "strings" "testing" + "code.gitea.io/gitea/models/db" + unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/translation" + issue_service "code.gitea.io/gitea/services/issue" + files_service "code.gitea.io/gitea/services/repository/files" + "code.gitea.io/gitea/tests" + "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,3 +37,45 @@ func TestUserDashboardActionLinks(t *testing.T) { assert.EqualValues(t, locale.TrString("new_migrate.link"), strings.TrimSpace(links.Find("a[href='/repo/migrate']").Text())) assert.EqualValues(t, locale.TrString("new_org.link"), strings.TrimSpace(links.Find("a[href='/org/create']").Text())) } + +func TestDashboardTitleRendering(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + sess := loginUser(t, user4.Name) + + repo, _, f := tests.CreateDeclarativeRepo(t, user4, "", + []unit_model.Type{unit_model.TypePullRequests, unit_model.TypeIssues}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "test.txt", + ContentReader: strings.NewReader("Just some text here"), + }, + }, + ) + defer f() + + issue := createIssue(t, user4, repo, "`:exclamation:` not rendered", "Hi there!") + pr := createPullRequest(t, user4, repo, "testing", "`:exclamation:` not rendered") + + _, err := issue_service.CreateIssueComment(db.DefaultContext, user4, repo, issue, "hi", nil) + require.NoError(t, err) + + _, err = issue_service.CreateIssueComment(db.DefaultContext, user4, repo, pr.Issue, "hi", nil) + require.NoError(t, err) + + testIssueClose(t, sess, repo.OwnerName, repo.Name, strconv.Itoa(int(issue.Index)), false) + testIssueClose(t, sess, repo.OwnerName, repo.Name, strconv.Itoa(int(pr.Issue.Index)), true) + + response := sess.MakeRequest(t, NewRequest(t, "GET", "/"), http.StatusOK) + htmlDoc := NewHTMLParser(t, response.Body) + + count := 0 + htmlDoc.doc.Find("#activity-feed .flex-item-main .title").Each(func(i int, s *goquery.Selection) { + count++ + assert.EqualValues(t, ":exclamation: not rendered", s.Text()) + }) + + assert.EqualValues(t, 6, count) + }) +} From 1a646a6dc7f45659b6b8ca10b22fb354a095d462 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 29 Jan 2025 07:45:40 +0000 Subject: [PATCH 019/669] fix(i18n): add forgotten translatable string (#6701) - Bug of 75ce1e2ac12042cf00713353055ab1d40b53b798 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6701 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- options/locale/locale_en-US.ini | 1 + templates/repo/editor/commit_form.tmpl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index da2457ccb7..c23397904a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1431,6 +1431,7 @@ editor.user_no_push_to_branch = User cannot push to branch editor.require_signed_commit = Branch requires a signed commit editor.cherry_pick = Cherry-pick %s onto: editor.revert = Revert %s onto: +editor.commit_email = Commit email commits.desc = Browse source code change history. commits.commits = Commits diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index fc04289b70..c42eed69a5 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -67,7 +67,7 @@ {{end}}
- +
@@ -368,7 +368,7 @@ export default sfc; // activate the IDE's Vue plugin
@@ -405,7 +405,7 @@ export default sfc; // activate the IDE's Vue plugin @@ -424,7 +424,7 @@ export default sfc; // activate the IDE's Vue plugin class="item navigation tw-py-1" :class="{'disabled': page === 1}" @click="changePage(1)" :title="textFirstPage" > - + - + - + @@ -454,7 +454,7 @@ export default sfc; // activate the IDE's Vue plugin diff --git a/web_src/js/components/RepoBranchTagSelector.vue b/web_src/js/components/RepoBranchTagSelector.vue index bfba2037cc..660551b8c7 100644 --- a/web_src/js/components/RepoBranchTagSelector.vue +++ b/web_src/js/components/RepoBranchTagSelector.vue @@ -256,7 +256,7 @@ export default sfc; // activate IDE's Vue plugin {{ refNameText }} - + From d0fcb3d614ae534aad00f963efcfbb82dfe87660 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 16 Feb 2025 19:18:00 -0800 Subject: [PATCH 204/669] Add API to support link package to repository and unlink it (#33481) Fix #21062 --------- Co-authored-by: Zettat123 (cherry picked from commit 5df9fd3e9c6ae7f848da65dbe9b9d321f29c003a) --- models/packages/package.go | 5 + routers/api/v1/api.go | 18 ++-- routers/api/v1/packages/package.go | 122 +++++++++++++++++++++++++ services/packages/package_update.go | 78 ++++++++++++++++ templates/swagger/v1_json.tmpl | 87 ++++++++++++++++++ tests/integration/api_packages_test.go | 38 ++++++-- 6 files changed, 334 insertions(+), 14 deletions(-) create mode 100644 services/packages/package_update.go diff --git a/models/packages/package.go b/models/packages/package.go index fd408f8bef..874c7c2847 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -242,6 +242,11 @@ func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error { return err } +func UnlinkRepository(ctx context.Context, packageID int64) error { + _, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: 0}) + return err +} + // UnlinkRepositoryFromAllPackages unlinks every package from the repository func UnlinkRepositoryFromAllPackages(ctx context.Context, repoID int64) error { _, err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Cols("repo_id").Update(&Package{}) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 684686b1e2..3791a6fbba 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1485,13 +1485,19 @@ func Routes() *web.Route { // NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs m.Group("/packages/{username}", func() { - m.Group("/{type}/{name}/{version}", func() { - m.Get("", reqToken(), packages.GetPackage) - m.Delete("", reqToken(), reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) - m.Get("/files", reqToken(), packages.ListPackageFiles) + m.Group("/{type}/{name}", func() { + m.Group("/{version}", func() { + m.Get("", packages.GetPackage) + m.Delete("", reqPackageAccess(perm.AccessModeWrite), packages.DeletePackage) + m.Get("/files", packages.ListPackageFiles) + }) + + m.Post("/-/link/{repo_name}", reqPackageAccess(perm.AccessModeWrite), packages.LinkPackage) + m.Post("/-/unlink", reqPackageAccess(perm.AccessModeWrite), packages.UnlinkPackage) }) - m.Get("/", reqToken(), packages.ListPackages) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly()) + + m.Get("/", packages.ListPackages) + }, reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly()) // Organizations m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs) diff --git a/routers/api/v1/packages/package.go b/routers/api/v1/packages/package.go index b38aa13167..a98b5dbc69 100644 --- a/routers/api/v1/packages/package.go +++ b/routers/api/v1/packages/package.go @@ -4,11 +4,14 @@ package packages import ( + "errors" "net/http" "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" @@ -213,3 +216,122 @@ func ListPackageFiles(ctx *context.APIContext) { ctx.JSON(http.StatusOK, apiPackageFiles) } + +// LinkPackage sets a repository link for a package +func LinkPackage(ctx *context.APIContext) { + // swagger:operation POST /packages/{owner}/{type}/{name}/-/link/{repo_name} package linkPackage + // --- + // summary: Link a package to a repository + // parameters: + // - name: owner + // in: path + // description: owner of the package + // type: string + // required: true + // - name: type + // in: path + // description: type of the package + // type: string + // required: true + // - name: name + // in: path + // description: name of the package + // type: string + // required: true + // - name: repo_name + // in: path + // description: name of the repository to link. + // type: string + // required: true + // responses: + // "201": + // "$ref": "#/responses/empty" + // "404": + // "$ref": "#/responses/notFound" + + pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParamRaw("type")), ctx.PathParamRaw("name")) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, "GetPackageByName", err) + } else { + ctx.Error(http.StatusInternalServerError, "GetPackageByName", err) + } + return + } + + repo, err := repo_model.GetRepositoryByName(ctx, ctx.ContextUser.ID, ctx.PathParamRaw("repo_name")) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, "GetRepositoryByName", err) + } else { + ctx.Error(http.StatusInternalServerError, "GetRepositoryByName", err) + } + return + } + + err = packages_service.LinkToRepository(ctx, pkg, repo, ctx.Doer) + if err != nil { + switch { + case errors.Is(err, util.ErrInvalidArgument): + ctx.Error(http.StatusBadRequest, "LinkToRepository", err) + case errors.Is(err, util.ErrPermissionDenied): + ctx.Error(http.StatusForbidden, "LinkToRepository", err) + default: + ctx.Error(http.StatusInternalServerError, "LinkToRepository", err) + } + return + } + ctx.Status(http.StatusCreated) +} + +// UnlinkPackage sets a repository link for a package +func UnlinkPackage(ctx *context.APIContext) { + // swagger:operation POST /packages/{owner}/{type}/{name}/-/unlink package unlinkPackage + // --- + // summary: Unlink a package from a repository + // parameters: + // - name: owner + // in: path + // description: owner of the package + // type: string + // required: true + // - name: type + // in: path + // description: type of the package + // type: string + // required: true + // - name: name + // in: path + // description: name of the package + // type: string + // required: true + // responses: + // "201": + // "$ref": "#/responses/empty" + // "404": + // "$ref": "#/responses/notFound" + + pkg, err := packages.GetPackageByName(ctx, ctx.ContextUser.ID, packages.Type(ctx.PathParamRaw("type")), ctx.PathParamRaw("name")) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.Error(http.StatusNotFound, "GetPackageByName", err) + } else { + ctx.Error(http.StatusInternalServerError, "GetPackageByName", err) + } + return + } + + err = packages_service.UnlinkFromRepository(ctx, pkg, ctx.Doer) + if err != nil { + switch { + case errors.Is(err, util.ErrPermissionDenied): + ctx.Error(http.StatusForbidden, "UnlinkFromRepository", err) + case errors.Is(err, util.ErrInvalidArgument): + ctx.Error(http.StatusBadRequest, "UnlinkFromRepository", err) + default: + ctx.Error(http.StatusInternalServerError, "UnlinkFromRepository", err) + } + return + } + ctx.Status(http.StatusNoContent) +} diff --git a/services/packages/package_update.go b/services/packages/package_update.go new file mode 100644 index 0000000000..8d851fac53 --- /dev/null +++ b/services/packages/package_update.go @@ -0,0 +1,78 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package packages + +import ( + "context" + "fmt" + + org_model "code.gitea.io/gitea/models/organization" + packages_model "code.gitea.io/gitea/models/packages" + access_model "code.gitea.io/gitea/models/perm/access" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/util" +) + +func LinkToRepository(ctx context.Context, pkg *packages_model.Package, repo *repo_model.Repository, doer *user_model.User) error { + if pkg.OwnerID != repo.OwnerID { + return util.ErrPermissionDenied + } + if pkg.RepoID > 0 { + return util.ErrInvalidArgument + } + + perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) + if err != nil { + return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) + } + if !perms.CanWrite(unit.TypePackages) { + return util.ErrPermissionDenied + } + + if err := packages_model.SetRepositoryLink(ctx, pkg.ID, repo.ID); err != nil { + return fmt.Errorf("error while linking package '%v' to repo '%v' : %w", pkg.Name, repo.FullName(), err) + } + return nil +} + +func UnlinkFromRepository(ctx context.Context, pkg *packages_model.Package, doer *user_model.User) error { + if pkg.RepoID == 0 { + return util.ErrInvalidArgument + } + + repo, err := repo_model.GetRepositoryByID(ctx, pkg.RepoID) + if err != nil { + return fmt.Errorf("error getting repository %d: %w", pkg.RepoID, err) + } + + perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) + if err != nil { + return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) + } + if !perms.CanWrite(unit.TypePackages) { + return util.ErrPermissionDenied + } + + user, err := user_model.GetUserByID(ctx, pkg.OwnerID) + if err != nil { + return err + } + if !doer.IsAdmin { + if !user.IsOrganization() { + if doer.ID != pkg.OwnerID { + return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name) + } + } else { + isOrgAdmin, err := org_model.OrgFromUser(user).IsOrgAdmin(ctx, doer.ID) + if err != nil { + return err + } else if !isOrgAdmin { + return fmt.Errorf("no permission to unlink package '%v' from its repository, or packages are disabled", pkg.Name) + } + } + } + return packages_model.UnlinkRepository(ctx, pkg.ID) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 037ee48b8c..7ff9825bbb 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -4159,6 +4159,93 @@ } } }, + "/packages/{owner}/{type}/{name}/-/link/{repo_name}": { + "post": { + "tags": [ + "package" + ], + "summary": "Link a package to a repository", + "operationId": "linkPackage", + "parameters": [ + { + "type": "string", + "description": "owner of the package", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "type of the package", + "name": "type", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the package", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repository to link.", + "name": "repo_name", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "$ref": "#/responses/empty" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, + "/packages/{owner}/{type}/{name}/-/unlink": { + "post": { + "tags": [ + "package" + ], + "summary": "Unlink a package from a repository", + "operationId": "unlinkPackage", + "parameters": [ + { + "type": "string", + "description": "owner of the package", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "type of the package", + "name": "type", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the package", + "name": "name", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "$ref": "#/responses/empty" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/packages/{owner}/{type}/{name}/{version}": { "get": { "produces": [ diff --git a/tests/integration/api_packages_test.go b/tests/integration/api_packages_test.go index 27aed0feb1..c74f40a689 100644 --- a/tests/integration/api_packages_test.go +++ b/tests/integration/api_packages_test.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" container_model "code.gitea.io/gitea/models/packages/container" + unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" @@ -35,7 +36,7 @@ func TestPackageAPI(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) session := loginUser(t, user.Name) tokenReadPackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadPackage) - tokenDeletePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage) + tokenWritePackage := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWritePackage) packageName := "test-package" packageVersion := "1.0.3" @@ -99,8 +100,13 @@ func TestPackageAPI(t *testing.T) { DecodeJSON(t, resp, &ap1) assert.Nil(t, ap1.Repository) + // create a repository + repo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit_model.Type{unit_model.TypeCode}, nil, nil) + defer f() + // link to public repository - require.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 1)) + req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, repo.Name)).AddTokenAuth(tokenWritePackage) + MakeRequest(t, req, http.StatusCreated) req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)). AddTokenAuth(tokenReadPackage) @@ -109,10 +115,15 @@ func TestPackageAPI(t *testing.T) { var ap2 *api.Package DecodeJSON(t, resp, &ap2) assert.NotNil(t, ap2.Repository) - assert.EqualValues(t, 1, ap2.Repository.ID) + assert.EqualValues(t, repo.ID, ap2.Repository.ID) - // link to private repository - require.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, 2)) + // link to repository without write access, should fail + req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/link/%s", user.Name, packageName, "repo3")).AddTokenAuth(tokenWritePackage) + MakeRequest(t, req, http.StatusNotFound) + + // remove link + req = NewRequest(t, "POST", fmt.Sprintf("/api/v1/packages/%s/generic/%s/-/unlink", user.Name, packageName)).AddTokenAuth(tokenWritePackage) + MakeRequest(t, req, http.StatusNoContent) req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)). AddTokenAuth(tokenReadPackage) @@ -122,7 +133,18 @@ func TestPackageAPI(t *testing.T) { DecodeJSON(t, resp, &ap3) assert.Nil(t, ap3.Repository) - require.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(db.DefaultContext, 2)) + // force link to a repository the currently logged-in user doesn't have access to + privateRepoID := int64(6) + require.NoError(t, packages_model.SetRepositoryLink(db.DefaultContext, p.ID, privateRepoID)) + + req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)).AddTokenAuth(tokenReadPackage) + resp = MakeRequest(t, req, http.StatusOK) + + var ap4 *api.Package + DecodeJSON(t, resp, &ap4) + assert.Nil(t, ap4.Repository) + + require.NoError(t, packages_model.UnlinkRepositoryFromAllPackages(db.DefaultContext, privateRepoID)) }) }) @@ -153,11 +175,11 @@ func TestPackageAPI(t *testing.T) { defer tests.PrintCurrentTest(t)() req := NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/dummy/%s/%s", user.Name, packageName, packageVersion)). - AddTokenAuth(tokenDeletePackage) + AddTokenAuth(tokenWritePackage) MakeRequest(t, req, http.StatusNotFound) req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/packages/%s/generic/%s/%s", user.Name, packageName, packageVersion)). - AddTokenAuth(tokenDeletePackage) + AddTokenAuth(tokenWritePackage) MakeRequest(t, req, http.StatusNoContent) }) } From 8d8634bb2e4f0ceedcfeef3abf52cc7628ade660 Mon Sep 17 00:00:00 2001 From: Kerwin Bryant Date: Tue, 18 Feb 2025 19:29:08 +0800 Subject: [PATCH 205/669] Fix Untranslated Text on Actions Page (#33635) Fix the problem of untranslated text on the actions page Co-authored-by: wxiaoguang (cherry picked from commit ce65613690e4564d9961f847ebd6eb2137f0c885) --- models/actions/run_list.go | 5 +++-- routers/web/repo/actions/actions.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 4046c7d369..b9b9324e07 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -10,6 +10,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/translation" webhook_module "code.gitea.io/gitea/modules/webhook" "xorm.io/builder" @@ -112,14 +113,14 @@ type StatusInfo struct { } // GetStatusInfoList returns a slice of StatusInfo -func GetStatusInfoList(ctx context.Context) []StatusInfo { +func GetStatusInfoList(ctx context.Context, lang translation.Locale) []StatusInfo { // same as those in aggregateJobStatus allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning} statusInfoList := make([]StatusInfo, 0, 4) for _, s := range allStatus { statusInfoList = append(statusInfoList, StatusInfo{ Status: int(s), - DisplayedStatus: s.String(), + DisplayedStatus: s.LocaleString(lang), }) } return statusInfoList diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index e5134c1f62..e0ef709ea6 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -240,7 +240,7 @@ func List(ctx *context.Context) { } ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors) - ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx) + ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx, ctx.Locale) pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) pager.SetDefaultParams(ctx) From fc4a89557dc001c5aef4e2782c610cef942d74bd Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 20 Feb 2025 12:39:21 -0800 Subject: [PATCH 206/669] Fix omitempty bug (#33663) Fix #33660 (cherry picked from commit a25081f3801551457fe8ac10eb2817ceca21fbae) --- models/admin/task.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/admin/task.go b/models/admin/task.go index c8bc95f981..5c2f7af49c 100644 --- a/models/admin/task.go +++ b/models/admin/task.go @@ -44,7 +44,7 @@ func init() { // TranslatableMessage represents JSON struct that can be translated with a Locale type TranslatableMessage struct { Format string - Args []any `json:"omitempty"` + Args []any `json:",omitempty"` } // LoadRepo loads repository of the task From 7eca6820d8eb9893c044bfba76a433b8c5bc339f Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 20 Feb 2025 20:17:56 -0800 Subject: [PATCH 207/669] Deleting repository should unlink all related packages (#33653) Fix #33634 --------- Co-authored-by: Giteabot Co-authored-by: wxiaoguang (cherry picked from commit f2fbb897f3bf68a1af1410a2b4ce7a289ef73c1a) --- services/repository/delete.go | 11 +++++++++++ services/repository/repository.go | 12 +----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/services/repository/delete.go b/services/repository/delete.go index 6e84194750..09213e5c65 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -16,6 +16,7 @@ import ( git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" + packages_model "code.gitea.io/gitea/models/packages" access_model "code.gitea.io/gitea/models/perm/access" project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" @@ -28,6 +29,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + federation_service "code.gitea.io/gitea/services/federation" "xorm.io/builder" ) @@ -289,6 +291,15 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID return err } + if err := federation_service.DeleteFollowingRepos(ctx, repo.ID); err != nil { + return err + } + + // unlink packages linked to this repository + if err = packages_model.UnlinkRepositoryFromAllPackages(ctx, repoID); err != nil { + return err + } + if err = committer.Commit(); err != nil { return err } diff --git a/services/repository/repository.go b/services/repository/repository.go index 116e24151b..35bcdfd528 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/organization" - packages_model "code.gitea.io/gitea/models/packages" repo_model "code.gitea.io/gitea/models/repo" system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/models/unit" @@ -22,7 +21,6 @@ import ( repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - federation_service "code.gitea.io/gitea/services/federation" notify_service "code.gitea.io/gitea/services/notify" pull_service "code.gitea.io/gitea/services/pull" ) @@ -64,15 +62,7 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod notify_service.DeleteRepository(ctx, doer, repo) } - if err := DeleteRepositoryDirectly(ctx, doer, repo.ID); err != nil { - return err - } - - if err := federation_service.DeleteFollowingRepos(ctx, repo.ID); err != nil { - return err - } - - return packages_model.UnlinkRepositoryFromAllPackages(ctx, repo.ID) + return DeleteRepositoryDirectly(ctx, doer, repo.ID) } // PushCreateRepo creates a repository when a new repository is pushed to an appropriate namespace From 1339122c7a67ad64448e23ec1b1b6e4dbf41a701 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 28 Feb 2025 15:23:09 +0000 Subject: [PATCH 208/669] Update module github.com/ProtonMail/go-crypto to v1.1.6 (forgejo) (#7068) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [github.com/ProtonMail/go-crypto](https://github.com/ProtonMail/go-crypto) | require | patch | `v1.1.5` -> `v1.1.6` | --- ### Release Notes
ProtonMail/go-crypto (github.com/ProtonMail/go-crypto) ### [`v1.1.6`](https://github.com/ProtonMail/go-crypto/releases/tag/v1.1.6) [Compare Source](https://github.com/ProtonMail/go-crypto/compare/v1.1.5...v1.1.6) #### What's Changed - Fix `PublicKey.KeyIdString` to return a valid key id by [@​lubux](https://github.com/lubux) in https://github.com/ProtonMail/go-crypto/pull/269 - Allow Key Flags override [@​davrux](https://github.com/davrux) in https://github.com/ProtonMail/go-crypto/pull/272 - Only check that message signatures are newer than the key by [@​twiss](https://github.com/twiss) in https://github.com/ProtonMail/go-crypto/pull/275 - openpgp/clearsign: just use rand.Reader in tests by [@​mdosch](https://github.com/mdosch) in https://github.com/ProtonMail/go-crypto/pull/276 - Make Issuer Key ID signature subpacket non-critical by [@​caarlos0](https://github.com/caarlos0) in https://github.com/ProtonMail/go-crypto/pull/266 - v2 API: Improve error messages for encryption key selection by [@​lubux](https://github.com/lubux) in https://github.com/ProtonMail/go-crypto/pull/271 **Full Changelog**: https://github.com/ProtonMail/go-crypto/compare/v1.1.5...v1.1.6
--- ### Configuration 📅 **Schedule**: Branch creation - "* 0-3 * * *" (UTC), Automerge - "* 0-3 * * *" (UTC). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, 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 [Renovate Bot](https://github.com/renovatebot/renovate). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7068 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 60add448e6..3f36ab3c78 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/42wim/httpsig v1.2.2 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 - github.com/ProtonMail/go-crypto v1.1.5 + github.com/ProtonMail/go-crypto v1.1.6 github.com/PuerkitoBio/goquery v1.10.1 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 github.com/alecthomas/chroma/v2 v2.15.0 diff --git a/go.sum b/go.sum index 3863a76b9a..16b0f18edb 100644 --- a/go.sum +++ b/go.sum @@ -676,8 +676,8 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= -github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU= github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY= github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM= From 374616fd87132589b79e081cc51da98ee2955a06 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 28 Feb 2025 15:23:58 +0000 Subject: [PATCH 209/669] chore(upgrade): switch to code.forgejo.org/forgejo/levelqueue (#7074) - no change except CI and dependency upgrades - release v1.0.0 The initial motivation for moving this dependency into Forgejo space was to fix a rare queue corruption, which turns out to not really be fixable, because leveldb has no transactions. Moving the package to Forgejo where it is upgraded via renovate makes for a better environment for future maintenance. Refs: https://codeberg.org/forgejo/forgejo/issues/7054 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7074 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- assets/go-licenses.json | 10 +++++----- go.mod | 2 +- go.sum | 5 ++--- modules/queue/base_levelqueue.go | 2 +- modules/queue/base_levelqueue_common.go | 2 +- modules/queue/base_levelqueue_test.go | 2 +- modules/queue/base_levelqueue_unique.go | 2 +- 7 files changed, 12 insertions(+), 13 deletions(-) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 7d6f94d755..1bea3fb805 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -19,6 +19,11 @@ "path": "code.forgejo.org/forgejo-contrib/go-libravatar/LICENSE", "licenseText": "Copyright (c) 2016 Sandro Santilli \u003cstrk@kbt.io\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" }, + { + "name": "code.forgejo.org/forgejo/levelqueue", + "path": "code.forgejo.org/forgejo/levelqueue/LICENSE", + "licenseText": "Copyright (c) 2019 Lunny Xiao\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" + }, { "name": "code.forgejo.org/forgejo/reply", "path": "code.forgejo.org/forgejo/reply/LICENSE", @@ -79,11 +84,6 @@ "path": "git.sr.ht/~mariusor/go-xsd-duration/LICENSE", "licenseText": "MIT License\n\nCopyright (c) 2019 Go xsd:duration\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n" }, - { - "name": "gitea.com/lunny/levelqueue", - "path": "gitea.com/lunny/levelqueue/LICENSE", - "licenseText": "Copyright (c) 2019 Lunny Xiao\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n" - }, { "name": "github.com/42wim/httpsig", "path": "github.com/42wim/httpsig/LICENSE", diff --git a/go.mod b/go.mod index 3f36ab3c78..6c1173301e 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ toolchain go1.24.0 require ( code.forgejo.org/f3/gof3/v3 v3.10.2 code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 + code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/reply v1.0.2 code.forgejo.org/go-chi/binding v1.0.0 code.forgejo.org/go-chi/cache v1.0.0 @@ -16,7 +17,6 @@ require ( code.gitea.io/sdk/gitea v0.20.0 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 connectrpc.com/connect v1.17.0 - gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 github.com/42wim/httpsig v1.2.2 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 diff --git a/go.sum b/go.sum index 16b0f18edb..42e70fd8c0 100644 --- a/go.sum +++ b/go.sum @@ -620,6 +620,8 @@ code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEj code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:RArF5AsF9LH4nEoJxqRxcP5r8hhRfWcId84G82YbqzA= code.forgejo.org/forgejo/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= +code.forgejo.org/forgejo/levelqueue v1.0.0 h1:9krYpU6BM+j/1Ntj6m+VCAIu0UNnne1/UfU/XgPpLuE= +code.forgejo.org/forgejo/levelqueue v1.0.0/go.mod h1:fmG6zhVuqim2rxSFOoasgXO8V2W/k9U31VVYqLIRLhQ= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= code.forgejo.org/forgejo/ssh v0.0.0-20241211213324-5fc306ca0616 h1:kEZL84+02jY9RxXM4zHBWZ3Fml0B09cmP1LGkDsCfIA= @@ -647,8 +649,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 h1:IFT+hup2xejHqdhS7keYWioqfmxdnfblFDTGoOwcZ+o= -gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU= github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA= @@ -1031,7 +1031,6 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= diff --git a/modules/queue/base_levelqueue.go b/modules/queue/base_levelqueue.go index efc57c9c9c..06cb5f4819 100644 --- a/modules/queue/base_levelqueue.go +++ b/modules/queue/base_levelqueue.go @@ -10,7 +10,7 @@ import ( "code.gitea.io/gitea/modules/nosql" "code.gitea.io/gitea/modules/queue/lqinternal" - "gitea.com/lunny/levelqueue" + "code.forgejo.org/forgejo/levelqueue" "github.com/syndtr/goleveldb/leveldb" ) diff --git a/modules/queue/base_levelqueue_common.go b/modules/queue/base_levelqueue_common.go index 78d3b85a8a..ee334c4571 100644 --- a/modules/queue/base_levelqueue_common.go +++ b/modules/queue/base_levelqueue_common.go @@ -13,7 +13,7 @@ import ( "code.gitea.io/gitea/modules/nosql" - "gitea.com/lunny/levelqueue" + "code.forgejo.org/forgejo/levelqueue" "github.com/syndtr/goleveldb/leveldb" ) diff --git a/modules/queue/base_levelqueue_test.go b/modules/queue/base_levelqueue_test.go index b65b570c4b..a4dc7a3062 100644 --- a/modules/queue/base_levelqueue_test.go +++ b/modules/queue/base_levelqueue_test.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/modules/queue/lqinternal" "code.gitea.io/gitea/modules/setting" - "gitea.com/lunny/levelqueue" + "code.forgejo.org/forgejo/levelqueue" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/syndtr/goleveldb/leveldb" diff --git a/modules/queue/base_levelqueue_unique.go b/modules/queue/base_levelqueue_unique.go index 968a4e98d4..ac133bdbf4 100644 --- a/modules/queue/base_levelqueue_unique.go +++ b/modules/queue/base_levelqueue_unique.go @@ -11,7 +11,7 @@ import ( "code.gitea.io/gitea/modules/nosql" "code.gitea.io/gitea/modules/queue/lqinternal" - "gitea.com/lunny/levelqueue" + "code.forgejo.org/forgejo/levelqueue" "github.com/syndtr/goleveldb/leveldb" ) From 7f386ea70c39516b12d295fcd3c9a27c37a8be6d Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 28 Feb 2025 16:12:58 +0000 Subject: [PATCH 210/669] chore(ci): ensure the manually cached Go can be run (#7078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` go version go1.24.0 linux/amd64 go env drwx------ 1 root root 4096 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/../../../../.. drwxr-xr-x 4 root root 4096 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/../../../.. drwxr-xr-x 4 root root 4096 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/../../.. drwxr-xr-x 4 root root 4096 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/../.. drwxr-xr-x 3 root root 4096 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/.. dr-xr-xr-x 6 root root 4096 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64 -r-xr-xr-x 1 root root 14314681 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/bin/go -r-xr-xr-x 1 root root 14314681 Feb 28 15:52 /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/bin/go bash: line 1: /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/bin/go: Permission denied bash: line 1: /root/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.0.linux-amd64/bin/go: Permission denied mkdir: cannot create directory ‘’: No such file or directory mkdir: cannot create directory ‘’: No such file or directory ``` Refs: https://codeberg.org/forgejo/forgejo/actions/runs/61591#jobstep-3-22 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7078 Reviewed-by: Michael Kriese Reviewed-by: Otto Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- .forgejo/workflows-composite/setup-cache-go/action.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows-composite/setup-cache-go/action.yaml b/.forgejo/workflows-composite/setup-cache-go/action.yaml index cc21485d14..f2818a7635 100644 --- a/.forgejo/workflows-composite/setup-cache-go/action.yaml +++ b/.forgejo/workflows-composite/setup-cache-go/action.yaml @@ -27,6 +27,7 @@ runs: - name: "Get go environment information" id: go-environment run: | + chmod 755 $HOME # ensure ${RUN_AS_USER} has permission when go is located in $HOME export GOROOT="$(go env GOROOT)" echo "modcache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOMODCACHE')" >> "$GITHUB_OUTPUT" echo "cache=$(su ${RUN_AS_USER} -c '${GOROOT}/bin/go env GOCACHE')" >> "$GITHUB_OUTPUT" From 526ce8ac0ea748296b69f055002663e9131bb777 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 28 Feb 2025 16:52:33 +0000 Subject: [PATCH 211/669] Update module code.forgejo.org/f3/gof3/v3 to v3.10.4 --- go.mod | 10 +++++----- go.sum | 15 ++++++++------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 6c1173301e..10536d119d 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.24 toolchain go1.24.0 require ( - code.forgejo.org/f3/gof3/v3 v3.10.2 + code.forgejo.org/f3/gof3/v3 v3.10.4 code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 code.forgejo.org/forgejo/levelqueue v1.0.0 code.forgejo.org/forgejo/reply v1.0.2 @@ -60,7 +60,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.2.0 github.com/gorilla/sessions v1.4.0 - github.com/hashicorp/go-version v1.6.0 + github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.5.0 github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 @@ -99,7 +99,7 @@ require ( github.com/yohcop/openid-go v1.0.1 github.com/yuin/goldmark v1.7.8 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc - gitlab.com/gitlab-org/api/client-go v0.119.0 + gitlab.com/gitlab-org/api/client-go v0.123.0 go.uber.org/mock v0.4.0 golang.org/x/crypto v0.35.0 golang.org/x/image v0.23.0 @@ -200,7 +200,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/go-tpm v0.9.1 // indirect github.com/google/s2a-go v0.1.8 // indirect @@ -269,7 +269,7 @@ require ( go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/time v0.8.0 // indirect + golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.28.0 // indirect google.golang.org/api v0.203.0 // indirect google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 // indirect diff --git a/go.sum b/go.sum index 42e70fd8c0..affa4a68b3 100644 --- a/go.sum +++ b/go.sum @@ -610,8 +610,8 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -code.forgejo.org/f3/gof3/v3 v3.10.2 h1:EOlv9d8GR7l0BmvZF101O3LUuabb4g5Hw5fKYPiPZlI= -code.forgejo.org/f3/gof3/v3 v3.10.2/go.mod h1:qApIHumpBkFkeBEokviO28+HK2WM11IsmMOhmjvCjFQ= +code.forgejo.org/f3/gof3/v3 v3.10.4 h1:NOkw8lG3PhgYykdR5pJ19KFLdYmRFVII43YBSNKtpvk= +code.forgejo.org/f3/gof3/v3 v3.10.4/go.mod h1:aQSlSGpISIf016g1dBrmm16h53WUKjCR7nSCuoO8dq8= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 h1:HTZl3CBk3ABNYtFI6TPLvJgGKFIhKT5CBk0sbOtkDKU= code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:PphB88CPbx601QrWPMZATeorACeVmQlyv3u+uUMbSaM= code.forgejo.org/forgejo/act v1.23.1 h1:4LU7u3FE380DicHz1oZ4ohsWU9I527OHeslVHnr9eP4= @@ -1055,8 +1055,9 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v64 v64.0.0 h1:4G61sozmY3eiPAjjoOHponXDBONm+utovTKbyUb2Qdg= github.com/google/go-github/v64 v64.0.0/go.mod h1:xB3vqMQNdHzilXBiO2I+M7iEFtHf+DP/omBOv6tQzVo= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -1446,8 +1447,8 @@ github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCR github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -gitlab.com/gitlab-org/api/client-go v0.119.0 h1:YBZyx9XUTtEDBBYtY36cZWz6JmT7om/8HPSk37IS95g= -gitlab.com/gitlab-org/api/client-go v0.119.0/go.mod h1:ygHmS3AU3TpvK+AC6DYO1QuAxLlv6yxYK+/Votr/WFQ= +gitlab.com/gitlab-org/api/client-go v0.123.0 h1:W3LZ5QNyiSCJA0Zchkwz8nQIUzOuDoSWMZtRDT5DjPI= +gitlab.com/gitlab-org/api/client-go v0.123.0/go.mod h1:Jh0qjLILEdbO6z/OY94RD+3NDQRUKiuFSFYozN6cpKM= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1831,8 +1832,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 32a4e9212ec23f2f11f83f63c950b33f167093b6 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 28 Feb 2025 18:03:49 +0100 Subject: [PATCH 212/669] fix: f3: implement the internal references interface --- services/f3/driver/repository.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/services/f3/driver/repository.go b/services/f3/driver/repository.go index da968b4c47..21f826e551 100644 --- a/services/f3/driver/repository.go +++ b/services/f3/driver/repository.go @@ -71,7 +71,7 @@ func (o *repository) upsert(ctx context.Context) generic.NodeID { return generic.NewNodeID(o.f.Name) } -func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination string)) { +func (o *repository) SetFetchFunc(fetchFunc func(ctx context.Context, destination string, internalRefs []string)) { o.f.FetchFunc = fetchFunc } @@ -92,6 +92,10 @@ func (o *repository) GetRepositoryPushURL() string { return o.getURL() } +func (o *repository) GetRepositoryInternalRefs() []string { + return []string{} +} + func newRepository(_ context.Context) generic.NodeDriverInterface { r := &repository{ f: &f3.Repository{}, From 91659f0d657f4f2d56d7691df1b417688267a30f Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 1 Mar 2025 00:35:30 +0000 Subject: [PATCH 213/669] Update dependency @stylistic/stylelint-plugin to v3.1.2 (forgejo) (#7083) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 929f905d50..5e04f4ece0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,7 +66,7 @@ "@playwright/test": "1.50.1", "@stoplight/spectral-cli": "6.14.2", "@stylistic/eslint-plugin-js": "2.12.1", - "@stylistic/stylelint-plugin": "3.1.1", + "@stylistic/stylelint-plugin": "3.1.2", "@typescript-eslint/parser": "8.18.2", "@vitejs/plugin-vue": "5.1.5", "@vitest/coverage-v8": "3.0.5", @@ -4270,9 +4270,9 @@ } }, "node_modules/@stylistic/stylelint-plugin": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.1.tgz", - "integrity": "sha512-XagAHHIa528EvyGybv8EEYGK5zrVW74cHpsjhtovVATbhDRuJYfE+X4HCaAieW9lCkwbX6L+X0I4CiUG3w/hFw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-3.1.2.tgz", + "integrity": "sha512-tylFJGMQo62alGazK74MNxFjMagYOHmBZiePZFOJK2n13JZta0uVkB3Bh5qodUmOLtRH+uxH297EibK14UKm8g==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 865017f69d..95b3791064 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "@playwright/test": "1.50.1", "@stoplight/spectral-cli": "6.14.2", "@stylistic/eslint-plugin-js": "2.12.1", - "@stylistic/stylelint-plugin": "3.1.1", + "@stylistic/stylelint-plugin": "3.1.2", "@typescript-eslint/parser": "8.18.2", "@vitejs/plugin-vue": "5.1.5", "@vitest/coverage-v8": "3.0.5", From e1dc143052a611805ca6c74f2f1eb00ad342a5d2 Mon Sep 17 00:00:00 2001 From: Litchi Pi Date: Sat, 1 Mar 2025 12:39:52 +0000 Subject: [PATCH 214/669] fix(web): forbid blocked users from reopening issues (#7010) Closes https://codeberg.org/forgejo/forgejo/issues/6841. Signed-off-by: Litchi Pi Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7010 Reviewed-by: Gusted Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Litchi Pi Co-committed-by: Litchi Pi --- models/issues/issue_update.go | 4 ++++ options/locale/locale_en-US.ini | 4 +++- routers/web/repo/issue.go | 22 ++++++++++++++++++++-- services/user/block_test.go | 21 +++++++++++++++++++++ templates/repo/issue/view_content.tmpl | 10 +++++++++- tests/integration/block_test.go | 12 ++++++++++++ 6 files changed, 69 insertions(+), 4 deletions(-) diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 775ebfadad..ad59b767ab 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -64,6 +64,10 @@ func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, } func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) { + if user_model.IsBlockedMultiple(ctx, []int64{issue.Repo.OwnerID, issue.PosterID}, doer.ID) { + return nil, user_model.ErrBlockedByUser + } + // Check for open dependencies if issue.IsClosed && issue.Repo.IsDependenciesEnabled(ctx) { // only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index dfae2b7b6f..8d27c9f3f8 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1878,7 +1878,8 @@ issues.content_history.delete_from_history_confirm = Delete from history? issues.content_history.options = Options issues.reference_link = Reference: %s issues.blocked_by_user = You cannot create a issue on this repository because you are blocked by the repository owner. -issues.comment.blocked_by_user = You cannot create a comment on this issue because you are blocked by the repository owner or the poster of the issue. +issues.comment.blocked_by_user = You cannot comment on this issue because you are blocked by the repository owner or the poster of the issue. +issues.reopen.blocked_by_user = You cannot reopen this issue because you are blocked by the repository owner or the poster of this issue. issues.summary_card_alt = Summary card of an issue titled "%s" in repository %s compare.compare_base = base @@ -1962,6 +1963,7 @@ pulls.waiting_count_1 = %d waiting review pulls.waiting_count_n = %d waiting reviews pulls.wrong_commit_id = commit id must be a commit id on the target branch pulls.blocked_by_user = You cannot create a pull request on this repository because you are blocked by the repository owner. +pulls.comment.blocked_by_user = You cannot comment on this pull request because you are blocked by the repository owner or the poster of the pull request. pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled. pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually. diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b4c673ffd7..a86d043497 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1260,7 +1260,11 @@ func NewIssuePost(ctx *context.Context) { if err := issue_service.NewIssue(ctx, repo, issue, labelIDs, attachments, assigneeIDs); err != nil { if errors.Is(err, user_model.ErrBlockedByUser) { - ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user")) + if issue.IsPull { + ctx.JSONError(ctx.Tr("repo.pulls.blocked_by_user")) + } else { + ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user")) + } return } else if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) { ctx.Error(http.StatusBadRequest, "UserDoesNotHaveAccessToRepo", err.Error()) @@ -2081,6 +2085,7 @@ func ViewIssue(ctx *context.Context) { ctx.Data["OpenGraphDescription"] = issue.Content ctx.Data["OpenGraphImageURL"] = issue.SummaryCardURL() ctx.Data["OpenGraphImageAltText"] = ctx.Tr("repo.issues.summary_card_alt", issue.Title, issue.Repo.FullName()) + ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlockedMultiple(ctx, []int64{issue.PosterID, issue.Repo.OwnerID}, ctx.Doer.ID) prepareHiddenCommentType(ctx) if ctx.Written() { @@ -3199,6 +3204,15 @@ func NewComment(ctx *context.Context) { } else { isClosed := form.Status == "close" if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", isClosed); err != nil { + if errors.Is(err, user_model.ErrBlockedByUser) { + if issue.IsPull { + ctx.JSONError(ctx.Tr("repo.pulls.blocked_by_user")) + } else { + ctx.JSONError(ctx.Tr("repo.issues.blocked_by_user")) + } + return + } + log.Error("ChangeStatus: %v", err) if issues_model.IsErrDependenciesLeft(err) { @@ -3240,7 +3254,11 @@ func NewComment(ctx *context.Context) { comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments) if err != nil { if errors.Is(err, user_model.ErrBlockedByUser) { - ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_by_user")) + if issue.IsPull { + ctx.JSONError(ctx.Tr("repo.pulls.comment.blocked_by_user")) + } else { + ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_by_user")) + } } else { ctx.ServerError("CreateIssueComment", err) } diff --git a/services/user/block_test.go b/services/user/block_test.go index f9e95ed7f7..13959e56b4 100644 --- a/services/user/block_test.go +++ b/services/user/block_test.go @@ -8,6 +8,7 @@ import ( model "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -89,4 +90,24 @@ func TestBlockUser(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3, OwnerID: blockedUser.ID}) assert.Equal(t, repo_model.RepositoryReady, repo.Status) }) + + t.Run("Issues", func(t *testing.T) { + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + blockedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + defer user_model.UnblockUser(db.DefaultContext, doer.ID, blockedUser.ID) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: doer.ID}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4, RepoID: repo.ID}, "is_closed = true") + + _, err := issues_model.ChangeIssueStatus(db.DefaultContext, issue, blockedUser, false) + require.NoError(t, err) + + _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue, doer, true) + require.NoError(t, err) + + require.NoError(t, BlockUser(db.DefaultContext, doer.ID, blockedUser.ID)) + + _, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue, blockedUser, false) + require.Error(t, err) + }) } diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index 8eac2d78cf..8ede0f36e5 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -79,7 +79,7 @@ {{template "repo/issue/view_content/pull".}} {{end}} - {{if .IsSigned}} + {{if and .IsSigned (not .IsBlocked)}} {{if and (or .IsRepoAdmin .HasIssuesOrPullsWritePermission (not .Issue.IsLocked)) (not .Repository.IsArchived)}} {{end}} + {{else if .IsBlocked}} +
+ {{if .Issue.IsPull}} + {{ctx.Locale.Tr "repo.pulls.comment.blocked_by_user"}} + {{else}} + {{ctx.Locale.Tr "repo.issues.comment.blocked_by_user"}} + {{end}} +
{{else}} {{/* not .IsSigned */}} {{if .Repository.IsArchived}}
diff --git a/tests/integration/block_test.go b/tests/integration/block_test.go index 1fa201a0be..8eda4ffa54 100644 --- a/tests/integration/block_test.go +++ b/tests/integration/block_test.go @@ -253,6 +253,12 @@ func TestBlockActions(t *testing.T) { DecodeJSON(t, resp, &errorResp) assert.EqualValues(t, expectedMessage, errorResp.Error) + + req = NewRequest(t, "GET", issue10URL) + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + msg := htmlDoc.doc.Find("div .warning").Text() + assert.Contains(t, msg, "You cannot comment on this issue because you are blocked") }) t.Run("Blocked by issue poster", func(t *testing.T) { @@ -274,6 +280,12 @@ func TestBlockActions(t *testing.T) { DecodeJSON(t, resp, &errorResp) assert.EqualValues(t, expectedMessage, errorResp.Error) + + req = NewRequest(t, "GET", issue10URL) + resp = session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + msg := htmlDoc.doc.Find("div .warning").Text() + assert.Contains(t, msg, "You cannot comment on this issue because you are blocked") }) }) From 52331d3174698690b34ca910fd7075e63f6043b7 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 1 Mar 2025 14:04:28 +0000 Subject: [PATCH 215/669] Update module github.com/PuerkitoBio/goquery to v1.10.2 (forgejo) (#7086) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 10536d119d..a61740d622 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/ProtonMail/go-crypto v1.1.6 - github.com/PuerkitoBio/goquery v1.10.1 + github.com/PuerkitoBio/goquery v1.10.2 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 github.com/alecthomas/chroma/v2 v2.15.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb diff --git a/go.sum b/go.sum index affa4a68b3..c145fb9429 100644 --- a/go.sum +++ b/go.sum @@ -678,8 +678,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU= -github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY= +github.com/PuerkitoBio/goquery v1.10.2 h1:7fh2BdHcG6VFZsK7toXBT/Bh1z5Wmy8Q9MV9HqT2AM8= +github.com/PuerkitoBio/goquery v1.10.2/go.mod h1:0guWGjcLu9AYC7C1GHnpysHy056u9aEkUHwhdnePMCU= github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM= github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90= github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 h1:cSXom2MoKJ9KPPw29RoZtHvUETY4F4n/kXl8m9btnQ0= From 74565d395ec288fcf7a995a8826cec3c0633fd6a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 1 Mar 2025 23:43:46 +0000 Subject: [PATCH 216/669] Update dependency happy-dom to v17.1.8 (forgejo) (#7085) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5e04f4ece0..0011f511e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,7 +88,7 @@ "eslint-plugin-vue-scoped-css": "2.9.0", "eslint-plugin-wc": "2.2.0", "globals": "15.15.0", - "happy-dom": "17.1.0", + "happy-dom": "17.1.8", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.44.0", "postcss-html": "1.8.0", @@ -9337,9 +9337,9 @@ } }, "node_modules/happy-dom": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.1.0.tgz", - "integrity": "sha512-9tUhXyePCjzUMycaHS/IzrIpF69xiq/laAT7golk4MtZ6t8ft5+Rv7U3lfrs2b4NMH0JTL3EhZzjfahrPmOnaQ==", + "version": "17.1.8", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.1.8.tgz", + "integrity": "sha512-Yxbq/FG79z1rhAf/iB6YM8wO2JB/JDQBy99RiLSs+2siEAi5J05x9eW1nnASHZJbpldjJE2KuFLsLZ+AzX/IxA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 95b3791064..0ebb741087 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "eslint-plugin-vue-scoped-css": "2.9.0", "eslint-plugin-wc": "2.2.0", "globals": "15.15.0", - "happy-dom": "17.1.0", + "happy-dom": "17.1.8", "license-checker-rseidelsohn": "4.4.2", "markdownlint-cli": "0.44.0", "postcss-html": "1.8.0", From dd7c0036277753a53c4c14b6afe051af4292f097 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 2 Mar 2025 00:16:29 +0000 Subject: [PATCH 217/669] i18n(en): a few source fixes (#7089) * use en-US quotes: _there are no quotes in the part of the text it refers to_ * small reword to fix `a issue` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7089 Reviewed-by: floss4good Reviewed-by: Gusted Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- options/locale/locale_en-US.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8d27c9f3f8..5b1e04566c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1877,7 +1877,7 @@ issues.content_history.delete_from_history = Delete from history issues.content_history.delete_from_history_confirm = Delete from history? issues.content_history.options = Options issues.reference_link = Reference: %s -issues.blocked_by_user = You cannot create a issue on this repository because you are blocked by the repository owner. +issues.blocked_by_user = You cannot create issues in this repository because you are blocked by the repository owner. issues.comment.blocked_by_user = You cannot comment on this issue because you are blocked by the repository owner or the poster of the issue. issues.reopen.blocked_by_user = You cannot reopen this issue because you are blocked by the repository owner or the poster of this issue. issues.summary_card_alt = Summary card of an issue titled "%s" in repository %s @@ -3765,7 +3765,7 @@ rpm.repository.multiple_groups = This package is available in multiple groups. alt.registry = Setup this registry from the command line: alt.registry.install = To install the package, run the following command: alt.install = Install package -alt.setup = Add a repository to the list of connected repositories (choose the necessary architecture instead of '_arch_'): +alt.setup = Add a repository to the list of connected repositories (choose the necessary architecture instead of "_arch_"): alt.repository = Repository info alt.repository.architectures = Architectures alt.repository.multiple_groups = This package is available in multiple groups. From d360e8a88be2342a1e78238496df773846fc36ce Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 2 Mar 2025 13:11:57 +0000 Subject: [PATCH 218/669] fix(ui): add header to org settings / blocked users page (#7101) Missing header copied from here: https://codeberg.org/forgejo/forgejo/src/commit/ed5af44c0645e6f7b156402574e19a731237baee/templates/user/settings/blocked_users.tmpl#L3-L5 Currently not possible to place it in the shared template because only org settings have the separate UI for blocking user. Preview: https://codeberg.org/attachments/1f829a11-cb0a-491d-9833-f5984e54301e Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7101 Reviewed-by: Beowulf Reviewed-by: Gusted --- templates/org/settings/blocked_users.tmpl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/org/settings/blocked_users.tmpl b/templates/org/settings/blocked_users.tmpl index f685a1b998..d139276b55 100644 --- a/templates/org/settings/blocked_users.tmpl +++ b/templates/org/settings/blocked_users.tmpl @@ -1,5 +1,8 @@ {{template "org/settings/layout_head" (dict "ctxData" . "pageClass" "organization settings blocked-users")}}
+

+ {{ctx.Locale.Tr "settings.blocked_users"}} +

{{.CsrfTokenHtml}} From 6d936d2da61adac82db79d0ec8ae86177a9ceb2b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 2 Mar 2025 14:47:31 +0000 Subject: [PATCH 219/669] Update module github.com/jhillyerd/enmime/v2 to v2.1.0 (forgejo) (#7096) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7096 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a61740d622..3942b73504 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.5.0 github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 - github.com/jhillyerd/enmime/v2 v2.0.0 + github.com/jhillyerd/enmime/v2 v2.1.0 github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/klauspost/compress v1.17.11 diff --git a/go.sum b/go.sum index c145fb9429..9667d0a87a 100644 --- a/go.sum +++ b/go.sum @@ -1179,8 +1179,8 @@ github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQykt github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jhillyerd/enmime/v2 v2.0.0 h1:I39PYf0peLGroKq+uX2yGB1ExH/78HcRJy4VmERQAVk= -github.com/jhillyerd/enmime/v2 v2.0.0/go.mod h1:wQkz7BochDzSukAz5ajAQaXOB7pEg5Vh5QWs7m1uAPw= +github.com/jhillyerd/enmime/v2 v2.1.0 h1:c8Qwi5Xq5EdtMN6byQWoZ/8I2RMTo6OJ7Xay+s1oPO0= +github.com/jhillyerd/enmime/v2 v2.1.0/go.mod h1:EJ74dcRbBcqHSP2TBu08XRoy6y3Yx0cevwb1YkGMEmQ= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= From 0b02d234fbdedb0ed93d695257ef7ac4339c5584 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 3 Mar 2025 10:47:18 +0000 Subject: [PATCH 220/669] Lock file maintenance (forgejo) (#7106) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7106 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 380 ++++++++++++++--------------- poetry.lock | 12 +- web_src/fomantic/package-lock.json | 76 +++--- 3 files changed, 231 insertions(+), 237 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0011f511e2..31c6718ebd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2755,9 +2755,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2860,13 +2860,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.12.0", "levn": "^0.4.1" }, "engines": { @@ -2874,9 +2874,9 @@ } }, "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2973,9 +2973,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3469,9 +3469,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.7.tgz", - "integrity": "sha512-l6CtzHYo8D2TQ3J7qJNpp3Q1Iye56ssIAtqbM2H8axxCEEwvN7o8Ze9PuIapbxFL3OHrJU2JBX6FIIVnP/rYyw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", + "integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==", "cpu": [ "arm" ], @@ -3483,9 +3483,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.7.tgz", - "integrity": "sha512-KvyJpFUueUnSp53zhAa293QBYqwm94TgYTIfXyOTtidhm5V0LbLCJQRGkQClYiX3FXDQGSvPxOTD/6rPStMMDg==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz", + "integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==", "cpu": [ "arm64" ], @@ -3497,9 +3497,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.7.tgz", - "integrity": "sha512-jq87CjmgL9YIKvs8ybtIC98s/M3HdbqXhllcy9EdLV0yMg1DpxES2gr65nNy7ObNo/vZ/MrOTxt0bE5LinL6mA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", "cpu": [ "arm64" ], @@ -3511,9 +3511,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.7.tgz", - "integrity": "sha512-rSI/m8OxBjsdnMMg0WEetu/w+LhLAcCDEiL66lmMX4R3oaml3eXz3Dxfvrxs1FbzPbJMaItQiksyMfv1hoIxnA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", "cpu": [ "x64" ], @@ -3525,9 +3525,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.7.tgz", - "integrity": "sha512-oIoJRy3ZrdsXpFuWDtzsOOa/E/RbRWXVokpVrNnkS7npz8GEG++E1gYbzhYxhxHbO2om1T26BZjVmdIoyN2WtA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz", + "integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==", "cpu": [ "arm64" ], @@ -3539,9 +3539,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.7.tgz", - "integrity": "sha512-X++QSLm4NZfZ3VXGVwyHdRf58IBbCu9ammgJxuWZYLX0du6kZvdNqPwrjvDfwmi6wFdvfZ/s6K7ia0E5kI7m8Q==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz", + "integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==", "cpu": [ "x64" ], @@ -3553,9 +3553,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.7.tgz", - "integrity": "sha512-Z0TzhrsNqukTz3ISzrvyshQpFnFRfLunYiXxlCRvcrb3nvC5rVKI+ZXPFG/Aa4jhQa1gHgH3A0exHaRRN4VmdQ==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz", + "integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==", "cpu": [ "arm" ], @@ -3567,9 +3567,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.7.tgz", - "integrity": "sha512-nkznpyXekFAbvFBKBy4nNppSgneB1wwG1yx/hujN3wRnhnkrYVugMTCBXED4+Ni6thoWfQuHNYbFjgGH0MBXtw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz", + "integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==", "cpu": [ "arm" ], @@ -3581,9 +3581,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.7.tgz", - "integrity": "sha512-KCjlUkcKs6PjOcxolqrXglBDcfCuUCTVlX5BgzgoJHw+1rWH1MCkETLkLe5iLLS9dP5gKC7mp3y6x8c1oGBUtA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", "cpu": [ "arm64" ], @@ -3595,9 +3595,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.7.tgz", - "integrity": "sha512-uFLJFz6+utmpbR313TTx+NpPuAXbPz4BhTQzgaP0tozlLnGnQ6rCo6tLwaSa6b7l6gRErjLicXQ1iPiXzYotjw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", "cpu": [ "arm64" ], @@ -3609,9 +3609,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.7.tgz", - "integrity": "sha512-ws8pc68UcJJqCpneDFepnwlsMUFoWvPbWXT/XUrJ7rWUL9vLoIN3GAasgG+nCvq8xrE3pIrd+qLX/jotcLy0Qw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz", + "integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==", "cpu": [ "loong64" ], @@ -3623,9 +3623,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.7.tgz", - "integrity": "sha512-vrDk9JDa/BFkxcS2PbWpr0C/LiiSLxFbNOBgfbW6P8TBe9PPHx9Wqbvx2xgNi1TOAyQHQJ7RZFqBiEohm79r0w==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz", + "integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==", "cpu": [ "ppc64" ], @@ -3637,9 +3637,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.7.tgz", - "integrity": "sha512-rB+ejFyjtmSo+g/a4eovDD1lHWHVqizN8P0Hm0RElkINpS0XOdpaXloqM4FBkF9ZWEzg6bezymbpLmeMldfLTw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz", + "integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==", "cpu": [ "riscv64" ], @@ -3651,9 +3651,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.7.tgz", - "integrity": "sha512-nNXNjo4As6dNqRn7OrsnHzwTgtypfRA3u3AKr0B3sOOo+HkedIbn8ZtFnB+4XyKJojIfqDKmbIzO1QydQ8c+Pw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz", + "integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==", "cpu": [ "s390x" ], @@ -3665,9 +3665,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.7.tgz", - "integrity": "sha512-9kPVf9ahnpOMSGlCxXGv980wXD0zRR3wyk8+33/MXQIpQEOpaNe7dEHm5LMfyRZRNt9lMEQuH0jUKj15MkM7QA==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", "cpu": [ "x64" ], @@ -3679,9 +3679,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.7.tgz", - "integrity": "sha512-7wJPXRWTTPtTFDFezA8sle/1sdgxDjuMoRXEKtx97ViRxGGkVQYovem+Q8Pr/2HxiHp74SSRG+o6R0Yq0shPwQ==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", "cpu": [ "x64" ], @@ -3693,9 +3693,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.7.tgz", - "integrity": "sha512-MN7aaBC7mAjsiMEZcsJvwNsQVNZShgES/9SzWp1HC9Yjqb5OpexYnRjF7RmE4itbeesHMYYQiAtUAQaSKs2Rfw==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", "cpu": [ "arm64" ], @@ -3707,9 +3707,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.7.tgz", - "integrity": "sha512-aeawEKYswsFu1LhDM9RIgToobquzdtSc4jSVqHV8uApz4FVvhFl/mKh92wc8WpFc6aYCothV/03UjY6y7yLgbg==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz", + "integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==", "cpu": [ "ia32" ], @@ -3721,9 +3721,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.7.tgz", - "integrity": "sha512-4ZedScpxxIrVO7otcZ8kCX1mZArtH2Wfj3uFCxRJ9NO80gg1XV0U/b2f/MKaGwj2X3QopHfoWiDQ917FRpwY3w==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", "cpu": [ "x64" ], @@ -4669,9 +4669,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", - "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", + "version": "22.13.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz", + "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -5468,13 +5468,13 @@ "license": "Apache-2.0" }, "node_modules/abbrev": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.0.tgz", - "integrity": "sha512-+/kfrslGQ7TNV2ecmQwMJj/B65g5KVq1/L3SGVZ3tCYGqlzFuFCGBZJtMP99wH3NpEUyAjn0zPdPUg0D+DwrOA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "dev": true, "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/abort-controller": { @@ -6155,9 +6155,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "version": "1.0.30001701", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz", + "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==", "funding": [ { "type": "opencollective", @@ -6565,13 +6565,13 @@ "license": "MIT" }, "node_modules/core-js-compat": { - "version": "3.40.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", - "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", + "version": "3.41.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.41.0.tgz", + "integrity": "sha512-RFsU9LySVue9RTwdDVX/T0e2Y6jRYWXERKElIjpuEOEnxaXffI0X7RUwVzfYLfzuLXSNJDYoRYUAmRUcyln20A==", "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.3" + "browserslist": "^4.24.4" }, "funding": { "type": "opencollective", @@ -6783,9 +6783,9 @@ "license": "MIT" }, "node_modules/cytoscape": { - "version": "3.31.0", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.31.0.tgz", - "integrity": "sha512-zDGn1K/tfZwEnoGOcHc0H4XazqAAXAuDpcYw9mUnUjATjqljyCNGJv8uEvbvxGaGHaVshxMecyl6oc6uKzRfbw==", + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.31.1.tgz", + "integrity": "sha512-Hx5Mtb1+hnmAKaZZ/7zL1Y5HTFYOjdDswZy/jD+1WINRU8KVi1B7+vlHdsTwY+VCFucTreoyu1RDzQJ9u0d2Hw==", "license": "MIT", "engines": { "node": ">=0.10" @@ -7693,9 +7693,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.101", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz", - "integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==", + "version": "1.5.109", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.109.tgz", + "integrity": "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -8710,9 +8710,9 @@ } }, "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.0.tgz", + "integrity": "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -8807,9 +8807,9 @@ } }, "node_modules/fastq": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -8890,9 +8890,9 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, @@ -8913,12 +8913,12 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -9044,18 +9044,18 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -10412,9 +10412,9 @@ "license": "MIT" }, "node_modules/js-beautify": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.3.tgz", - "integrity": "sha512-rKKGuyTxGNlyN4EQKWzNndzXpi0bOl8Gl8YQAW1as/oMz0XhD6sHJO1hTvoBDOSzKuJb9WkwoAb34FfdkKMv2A==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "dev": true, "license": "MIT", "dependencies": { @@ -10422,7 +10422,7 @@ "editorconfig": "^1.0.4", "glob": "^10.4.2", "js-cookie": "^3.0.5", - "nopt": "^8.0.0" + "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", @@ -10795,32 +10795,6 @@ "npm": ">=8" } }, - "node_modules/license-checker-rseidelsohn/node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/license-checker-rseidelsohn/node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -10873,13 +10847,14 @@ } }, "node_modules/local-pkg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.0.0.tgz", - "integrity": "sha512-bbgPw/wmroJsil/GgL4qjDzs5YLTBMQ99weRsok1XCDccQeehbHA/I1oRvk2NPtr7KGZgT/Y5tPRnAtMqeG2Kg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.0.tgz", + "integrity": "sha512-xbZBuX6gYIWrlLmZG43aAVer4ocntYO09vPy9lxd6Ns8DnR4U7N+IIeDkubinqFOHHzoMlPxTxwo0jhE7oYjAw==", "license": "MIT", "dependencies": { - "mlly": "^1.7.3", - "pkg-types": "^1.3.0" + "mlly": "^1.7.4", + "pkg-types": "^1.3.1", + "quansync": "^0.2.1" }, "engines": { "node": ">=14" @@ -11760,9 +11735,9 @@ } }, "node_modules/micromark-util-subtokenize": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.4.tgz", - "integrity": "sha512-N6hXjrin2GTJDe3MVjf5FuXpm12PGm80BrUAeub9XFXca8JZbP+oIwY4LJSVwFUCL1IPm/WwSVUN7goFHmSGGQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", "dev": true, "funding": [ { @@ -12073,19 +12048,19 @@ } }, "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, "license": "ISC", "dependencies": { - "abbrev": "^3.0.0" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-package-data": { @@ -12321,10 +12296,13 @@ "license": "BlueOak-1.0.0" }, "node_modules/package-manager-detector": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.9.tgz", - "integrity": "sha512-+vYvA/Y31l8Zk8dwxHhL3JfTuHPm6tlxM2A3GeQyl7ovYnSp1+mzAxClxaOr0qO1TtPxbQxetI7v5XqKLJZk7Q==", - "license": "MIT" + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-0.2.11.tgz", + "integrity": "sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==", + "license": "MIT", + "dependencies": { + "quansync": "^0.2.7" + } }, "node_modules/parent-module": { "version": "1.0.1", @@ -13180,6 +13158,22 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.7.tgz", + "integrity": "sha512-KZDFlN9/Si3CgKHZsIfLBsrjWKFjqu9KA0zDGJEQoQzPm5HWNDEFc2mkLeYUBBOwEJtxNBSMaNLE/GlvArIEfQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -13721,9 +13715,9 @@ } }, "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -14232,9 +14226,9 @@ } }, "node_modules/solid-js": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.4.tgz", - "integrity": "sha512-ipQl8FJ31bFUoBNScDQTG3BjN6+9Rg+Q+f10bUbnO6EOTTf5NGerJeHc7wyu5I4RMHEl/WwZwUmy/PTRgxxZ8g==", + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.5.tgz", + "integrity": "sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw==", "license": "MIT", "dependencies": { "csstype": "^3.1.0", @@ -15103,9 +15097,9 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.11.tgz", - "integrity": "sha512-RVCsMfuD0+cTt3EwX8hSl2Ks56EbFHWmhluwcqoPKtBnfjiT6olaq7PRIRfhyU8nnC2MrnDrBLfrD/RGE+cVXQ==", + "version": "5.3.12", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.12.tgz", + "integrity": "sha512-jDLYqo7oF8tJIttjXO6jBY5Hk8p3A8W4ttih7cCEq64fQFWmgJ4VqAQjKr7WwIDlmXKEc6QeoRb5ecjZ+2afcg==", "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -15636,9 +15630,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -16281,9 +16275,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.34.7", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.7.tgz", - "integrity": "sha512-8qhyN0oZ4x0H6wmBgfKxJtxM7qS98YJ0k0kNh5ECVtuchIJ7z9IVVvzpmtQyT10PXKMtBxYr1wQ5Apg8RS8kXQ==", + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz", + "integrity": "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==", "dev": true, "license": "MIT", "dependencies": { @@ -16297,25 +16291,25 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.7", - "@rollup/rollup-android-arm64": "4.34.7", - "@rollup/rollup-darwin-arm64": "4.34.7", - "@rollup/rollup-darwin-x64": "4.34.7", - "@rollup/rollup-freebsd-arm64": "4.34.7", - "@rollup/rollup-freebsd-x64": "4.34.7", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.7", - "@rollup/rollup-linux-arm-musleabihf": "4.34.7", - "@rollup/rollup-linux-arm64-gnu": "4.34.7", - "@rollup/rollup-linux-arm64-musl": "4.34.7", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.7", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.7", - "@rollup/rollup-linux-riscv64-gnu": "4.34.7", - "@rollup/rollup-linux-s390x-gnu": "4.34.7", - "@rollup/rollup-linux-x64-gnu": "4.34.7", - "@rollup/rollup-linux-x64-musl": "4.34.7", - "@rollup/rollup-win32-arm64-msvc": "4.34.7", - "@rollup/rollup-win32-ia32-msvc": "4.34.7", - "@rollup/rollup-win32-x64-msvc": "4.34.7", + "@rollup/rollup-android-arm-eabi": "4.34.9", + "@rollup/rollup-android-arm64": "4.34.9", + "@rollup/rollup-darwin-arm64": "4.34.9", + "@rollup/rollup-darwin-x64": "4.34.9", + "@rollup/rollup-freebsd-arm64": "4.34.9", + "@rollup/rollup-freebsd-x64": "4.34.9", + "@rollup/rollup-linux-arm-gnueabihf": "4.34.9", + "@rollup/rollup-linux-arm-musleabihf": "4.34.9", + "@rollup/rollup-linux-arm64-gnu": "4.34.9", + "@rollup/rollup-linux-arm64-musl": "4.34.9", + "@rollup/rollup-linux-loongarch64-gnu": "4.34.9", + "@rollup/rollup-linux-powerpc64le-gnu": "4.34.9", + "@rollup/rollup-linux-riscv64-gnu": "4.34.9", + "@rollup/rollup-linux-s390x-gnu": "4.34.9", + "@rollup/rollup-linux-x64-gnu": "4.34.9", + "@rollup/rollup-linux-x64-musl": "4.34.9", + "@rollup/rollup-win32-arm64-msvc": "4.34.9", + "@rollup/rollup-win32-ia32-msvc": "4.34.9", + "@rollup/rollup-win32-x64-msvc": "4.34.9", "fsevents": "~2.3.2" } }, @@ -16480,9 +16474,9 @@ } }, "node_modules/vue-component-type-helpers": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.2.tgz", - "integrity": "sha512-6lLY+n2xz2kCYshl59mL6gy8OUUTmkscmDFMO8i7Lj+QKwgnIFUZmM1i/iTYObtrczZVdw7UakPqDTGwVSGaRg==", + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.8.tgz", + "integrity": "sha512-4bjIsC284coDO9om4HPA62M7wfsTvcmZyzdfR0aUlFXqq4tXxM1APyXpNVxPC8QazKw9OhmZNHBVDA6ODaZsrA==", "dev": true, "license": "MIT" }, diff --git a/poetry.lock b/poetry.lock index 72118b2a01..707a276df7 100644 --- a/poetry.lock +++ b/poetry.lock @@ -44,13 +44,13 @@ files = [ [[package]] name = "cssbeautifier" -version = "1.15.3" +version = "1.15.4" description = "CSS unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "cssbeautifier-1.15.3-py3-none-any.whl", hash = "sha256:0dcaf5ce197743a79b3a160b84ea58fcbd9e3e767c96df1171e428125b16d410"}, - {file = "cssbeautifier-1.15.3.tar.gz", hash = "sha256:406b04d09e7d62c0be084fbfa2cba5126fe37359ea0d8d9f7b963a6354fc8303"}, + {file = "cssbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:78c84d5e5378df7d08622bbd0477a1abdbd209680e95480bf22f12d5701efc98"}, + {file = "cssbeautifier-1.15.4.tar.gz", hash = "sha256:9bb08dc3f64c101a01677f128acf01905914cf406baf87434dcde05b74c0acf5"}, ] [package.dependencies] @@ -115,13 +115,13 @@ files = [ [[package]] name = "jsbeautifier" -version = "1.15.3" +version = "1.15.4" description = "JavaScript unobfuscator and beautifier." optional = false python-versions = "*" files = [ - {file = "jsbeautifier-1.15.3-py3-none-any.whl", hash = "sha256:b207a15ab7529eee4a35ae7790e9ec4e32a2b5026d51e2d0386c3a65e6ecfc91"}, - {file = "jsbeautifier-1.15.3.tar.gz", hash = "sha256:5f1baf3d4ca6a615bb5417ee861b34b77609eeb12875555f8bbfabd9bf2f3457"}, + {file = "jsbeautifier-1.15.4-py3-none-any.whl", hash = "sha256:72f65de312a3f10900d7685557f84cb61a9733c50dcc27271a39f5b0051bf528"}, + {file = "jsbeautifier-1.15.4.tar.gz", hash = "sha256:5bb18d9efb9331d825735fbc5360ee8f1aac5e52780042803943aa7f854f7592"}, ] [package.dependencies] diff --git a/web_src/fomantic/package-lock.json b/web_src/fomantic/package-lock.json index 1f39fad1c0..0487104b96 100644 --- a/web_src/fomantic/package-lock.json +++ b/web_src/fomantic/package-lock.json @@ -494,9 +494,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz", - "integrity": "sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg==", + "version": "22.13.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.8.tgz", + "integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==", "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -513,12 +513,12 @@ } }, "node_modules/abbrev": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.0.tgz", - "integrity": "sha512-+/kfrslGQ7TNV2ecmQwMJj/B65g5KVq1/L3SGVZ3tCYGqlzFuFCGBZJtMP99wH3NpEUyAjn0zPdPUg0D+DwrOA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "license": "ISC", "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/accord": { @@ -1249,9 +1249,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001700", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", - "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", + "version": "1.0.30001701", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz", + "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==", "funding": [ { "type": "opencollective", @@ -2005,9 +2005,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.101", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.101.tgz", - "integrity": "sha512-L0ISiQrP/56Acgu4/i/kfPwWSgrzYZUnQrC0+QPFuhqlLP1Ir7qzPPDVS9BcKIyWTRU8+o6CC8dKw38tSWhYIA==", + "version": "1.5.109", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.109.tgz", + "integrity": "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -2551,12 +2551,12 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -2732,17 +2732,17 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -4998,16 +4998,16 @@ "license": "MIT" }, "node_modules/js-beautify": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.3.tgz", - "integrity": "sha512-rKKGuyTxGNlyN4EQKWzNndzXpi0bOl8Gl8YQAW1as/oMz0XhD6sHJO1hTvoBDOSzKuJb9WkwoAb34FfdkKMv2A==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "license": "MIT", "dependencies": { "config-chain": "^1.1.13", "editorconfig": "^1.0.4", "glob": "^10.4.2", "js-cookie": "^3.0.5", - "nopt": "^8.0.0" + "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", @@ -5850,9 +5850,9 @@ "license": "ISC" }, "node_modules/nan": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", - "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz", + "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==", "license": "MIT", "optional": true }, @@ -5996,18 +5996,18 @@ } }, "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "license": "ISC", "dependencies": { - "abbrev": "^3.0.0" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-package-data": { @@ -8324,9 +8324,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", From 4324ae45bdf441e3a2ec2013741f761ed344247b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 3 Mar 2025 11:33:01 +0000 Subject: [PATCH 221/669] Update renovate to v39.185.0 (forgejo) (#7105) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .forgejo/workflows/renovate.yml | 2 +- Makefile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index e17c77beb0..171c18e323 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -28,7 +28,7 @@ jobs: runs-on: docker container: - image: data.forgejo.org/renovate/renovate:39.178.1 + image: data.forgejo.org/renovate/renovate:39.185.0 steps: - name: Load renovate repo cache diff --git a/Makefile b/Makefile index d7da16d9d4..d8882f113e 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.30.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go -RENOVATE_NPM_PACKAGE ?= renovate@39.178.1 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate +RENOVATE_NPM_PACKAGE ?= renovate@39.185.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate # https://github.com/disposable-email-domains/disposable-email-domains/commits/main/ DISPOSABLE_EMAILS_SHA ?= 0c27e671231d27cf66370034d7f6818037416989 # renovate: ... From 4e1e694edd9eea073d00b102882da44168c77cbb Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 3 Mar 2025 18:05:01 +0000 Subject: [PATCH 222/669] feat: improve incorrect `ROOT_URL` warning (#7103) - In the case that the `ROOT_URL` does not match the site a person is visiting Forgejo gives zero guarantees that any of the functionality will still work. - Make the error i18n, use `local_next`. - Reflect in the error that the any part of the application can break, don't be specific - it is plain wrong and should not be used. - Always check for this case on the login page. This was previously only the case if OAuth2 was enabled, but this code was checking for elements that are always present on the login page regardless if the OAuth2 was enabled or not. Technically nothing changed, but reading the code it is now more clear when this check is being run. - Add E2E testing. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7103 Reviewed-by: Otto Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Gusted Co-committed-by: Gusted --- options/locale_next/locale_en-US.json | 3 ++- templates/base/head_script.tmpl | 1 + tests/e2e/login.test.e2e.ts | 33 +++++++++++++++++++++++++++ web_src/js/features/common-global.js | 4 ++-- web_src/js/features/user-auth.js | 7 ++++-- web_src/js/index.js | 3 ++- 6 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 tests/e2e/login.test.e2e.ts diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index cc06102c46..23e35af318 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -12,5 +12,6 @@ "one": "wants to merge %[1]d commit from %[2]s into %[3]s", "other": "wants to merge %[1]d commits from %[2]s into %[3]s" }, - "search.milestone_kind": "Search milestones..." + "search.milestone_kind": "Search milestones...", + "incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini." } diff --git a/templates/base/head_script.tmpl b/templates/base/head_script.tmpl index 22e08e9c8f..d2774010b6 100644 --- a/templates/base/head_script.tmpl +++ b/templates/base/head_script.tmpl @@ -42,6 +42,7 @@ If you introduce mistakes in it, Gitea JavaScript code wouldn't run correctly. modal_confirm: {{ctx.Locale.Tr "modal.confirm"}}, modal_cancel: {{ctx.Locale.Tr "modal.cancel"}}, more_items: {{ctx.Locale.Tr "more_items"}}, + incorrect_root_url: {{ctx.Locale.Tr "incorrect_root_url" AppUrl}}, }, }; {{/* in case some pages don't render the pageData, we make sure it is an object to prevent null access */}} diff --git a/tests/e2e/login.test.e2e.ts b/tests/e2e/login.test.e2e.ts new file mode 100644 index 0000000000..1ffa0b2e5d --- /dev/null +++ b/tests/e2e/login.test.e2e.ts @@ -0,0 +1,33 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +// @watch start +// templates/user/auth/** +// web_src/js/features/user-** +// web_src/js/features/common-global.js +// @watch end + +import {expect} from '@playwright/test'; +import {test, save_visual, test_context} from './utils_e2e.ts'; + +test('Mismatched ROOT_URL', async ({browser}) => { + const context = await test_context(browser); + const page = await context.newPage(); + + // Ugly hack to override the appUrl of `window.config`. + await page.addInitScript(() => { + setInterval(() => { + if (window.config) { + window.config.appUrl = 'https://example.com'; + } + }, 1); + }); + + const response = await page.goto('/user/login'); + expect(response?.status()).toBe(200); + + await save_visual(page); + const globalError = page.locator('.js-global-error'); + await expect(globalError).toContainText('This Forgejo instance is configured to be served on '); + await expect(globalError).toContainText('You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.'); +}); diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index b05f6e07fe..7848a14b66 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -458,6 +458,6 @@ export function checkAppUrl() { if (curUrl.startsWith(appUrl) || `${curUrl}/` === appUrl) { return; } - showGlobalErrorMessage(`Your ROOT_URL in app.ini is "${appUrl}", it's unlikely matching the site you are visiting. -Mismatched ROOT_URL config causes wrong URL links for web UI/mail content/webhook notification/OAuth2 sign-in.`); + + showGlobalErrorMessage(i18n.incorrect_root_url); } diff --git a/web_src/js/features/user-auth.js b/web_src/js/features/user-auth.js index a871ac471c..64519037c7 100644 --- a/web_src/js/features/user-auth.js +++ b/web_src/js/features/user-auth.js @@ -5,8 +5,6 @@ export function initUserAuthOauth2() { if (!outer) return; const inner = document.getElementById('oauth2-login-navigator-inner'); - checkAppUrl(); - for (const link of outer.querySelectorAll('.oauth-login-link')) { link.addEventListener('click', () => { inner.classList.add('tw-invisible'); @@ -20,3 +18,8 @@ export function initUserAuthOauth2() { }); } } + +export function initUserAuth() { + if (!document.querySelector('.user.signin')) return; + checkAppUrl(); +} diff --git a/web_src/js/index.js b/web_src/js/index.js index 6797617414..7d44b9ff56 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -24,7 +24,7 @@ import {initFindFileInRepo} from './features/repo-findfile.js'; import {initCommentContent, initMarkupContent} from './markup/content.js'; import {initPdfViewer} from './render/pdf.js'; -import {initUserAuthOauth2} from './features/user-auth.js'; +import {initUserAuthOauth2, initUserAuth} from './features/user-auth.js'; import { initRepoIssueDue, initRepoIssueReferenceRepositorySearch, @@ -184,6 +184,7 @@ onDomReady(() => { initUserAuthOauth2(); initUserAuthWebAuthn(); initUserAuthWebAuthnRegister(); + initUserAuth(); initRepoDiffView(); initPdfViewer(); initScopedAccessTokenCategories(); From ced668988ae0459de674e5f129e3bbb31f926a63 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 4 Mar 2025 00:33:22 +0000 Subject: [PATCH 223/669] Update module github.com/opencontainers/image-spec to v1.1.1 (forgejo) (#7112) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7112 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3942b73504..1376d1c2ce 100644 --- a/go.mod +++ b/go.mod @@ -82,7 +82,7 @@ require ( github.com/niklasfasching/go-org v1.7.0 github.com/olivere/elastic/v7 v7.0.32 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.1.0 + github.com/opencontainers/image-spec v1.1.1 github.com/pquerna/otp v1.4.0 github.com/prometheus/client_golang v1.21.0 github.com/redis/go-redis/v9 v9.7.0 diff --git a/go.sum b/go.sum index 9667d0a87a..c8a3577a8a 100644 --- a/go.sum +++ b/go.sum @@ -1304,8 +1304,8 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= From e5c52246e06f42068667a2a989817ff7a8217283 Mon Sep 17 00:00:00 2001 From: jwolvers Date: Tue, 4 Mar 2025 11:26:08 +0000 Subject: [PATCH 224/669] feat(nuget): add missing nuget V2 properties (#7102) - Add several missing nuget V2 properties to the API. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7102 Reviewed-by: Gusted Co-authored-by: jwolvers Co-committed-by: jwolvers --- modules/packages/nuget/metadata.go | 27 ++++++++ modules/packages/nuget/metadata_test.go | 36 +++++++++-- routers/api/packages/nuget/api_v2.go | 22 ++++++- tests/integration/api_packages_nuget_test.go | 66 ++++++++++++++++++-- 4 files changed, 138 insertions(+), 13 deletions(-) diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go index 1e98ddffde..dfb81baf4e 100644 --- a/modules/packages/nuget/metadata.go +++ b/modules/packages/nuget/metadata.go @@ -57,12 +57,21 @@ type Package struct { // Metadata represents the metadata of a Nuget package type Metadata struct { + Title string `json:"title,omitempty"` + Language string `json:"language,omitempty"` Description string `json:"description,omitempty"` ReleaseNotes string `json:"release_notes,omitempty"` Readme string `json:"readme,omitempty"` Authors string `json:"authors,omitempty"` + Owners string `json:"owners,omitempty"` + Copyright string `json:"copyright,omitempty"` ProjectURL string `json:"project_url,omitempty"` RepositoryURL string `json:"repository_url,omitempty"` + LicenseURL string `json:"license_url,omitempty"` + IconURL string `json:"icon_url,omitempty"` + MinClientVersion string `json:"min_client_version,omitempty"` + Tags string `json:"tags,omitempty"` + DevelopmentDependency bool `json:"development_dependency,omitempty"` RequireLicenseAcceptance bool `json:"require_license_acceptance"` Dependencies map[string][]Dependency `json:"dependencies,omitempty"` } @@ -77,13 +86,22 @@ type Dependency struct { type nuspecPackage struct { Metadata struct { ID string `xml:"id"` + Title string `xml:"title"` + Language string `xml:"language"` Version string `xml:"version"` Authors string `xml:"authors"` + Owners string `xml:"owners"` + Copyright string `xml:"copyright"` + DevelopmentDependency bool `xml:"developmentDependency"` RequireLicenseAcceptance bool `xml:"requireLicenseAcceptance"` ProjectURL string `xml:"projectUrl"` + LicenseURL string `xml:"licenseUrl"` + IconURL string `xml:"iconUrl"` Description string `xml:"description"` ReleaseNotes string `xml:"releaseNotes"` Readme string `xml:"readme"` + Tags string `xml:"tags"` + MinClientVersion string `xml:"minClientVersion,attr"` PackageTypes struct { PackageType []struct { Name string `xml:"name,attr"` @@ -167,11 +185,20 @@ func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) { } m := &Metadata{ + Title: p.Metadata.Title, + Language: p.Metadata.Language, Description: p.Metadata.Description, ReleaseNotes: p.Metadata.ReleaseNotes, Authors: p.Metadata.Authors, + Owners: p.Metadata.Owners, + Copyright: p.Metadata.Copyright, ProjectURL: p.Metadata.ProjectURL, RepositoryURL: p.Metadata.Repository.URL, + LicenseURL: p.Metadata.LicenseURL, + IconURL: p.Metadata.IconURL, + MinClientVersion: p.Metadata.MinClientVersion, + Tags: p.Metadata.Tags, + DevelopmentDependency: p.Metadata.DevelopmentDependency, RequireLicenseAcceptance: p.Metadata.RequireLicenseAcceptance, Dependencies: make(map[string][]Dependency), } diff --git a/modules/packages/nuget/metadata_test.go b/modules/packages/nuget/metadata_test.go index ecce052be4..8a34f1a5ae 100644 --- a/modules/packages/nuget/metadata_test.go +++ b/modules/packages/nuget/metadata_test.go @@ -13,14 +13,22 @@ import ( ) const ( - id = "System.Gitea" + id = "System.Forgejo" + title = "Package Title" + language = "Package Language" semver = "1.0.1" - authors = "Gitea Authors" - projectURL = "https://gitea.io" + authors = "Forgejo Authors" + owners = "Package Owners" + copyright = "Package Copyright" + projectURL = "https://forgejo.org" + licenseURL = "https://forgejo.org/docs/latest/license/" + iconURL = "https://codeberg.org/forgejo/governance/raw/branch/main/branding/logo/forgejo.png" description = "Package Description" releaseNotes = "Package Release Notes" readme = "Readme" - repositoryURL = "https://gitea.io/gitea/gitea" + tags = "tag_1 tag_2 tag_3" + minClientVersion = "1.0.0.0" + repositoryURL = "https://codeberg.org/forgejo" targetFramework = ".NETStandard2.1" dependencyID = "System.Text.Json" dependencyVersion = "5.0.0" @@ -28,16 +36,24 @@ const ( const nuspecContent = ` - + ` + id + ` + ` + title + ` + ` + language + ` ` + semver + ` ` + authors + ` + ` + owners + ` + ` + copyright + ` + true true ` + projectURL + ` + ` + licenseURL + ` + ` + iconURL + ` ` + description + ` ` + releaseNotes + ` README.md + ` + tags + ` @@ -142,12 +158,22 @@ func TestParsePackageMetaData(t *testing.T) { assert.Equal(t, DependencyPackage, np.PackageType) assert.Equal(t, id, np.ID) + assert.Equal(t, title, np.Metadata.Title) + assert.Equal(t, language, np.Metadata.Language) assert.Equal(t, semver, np.Version) assert.Equal(t, authors, np.Metadata.Authors) + assert.Equal(t, owners, np.Metadata.Owners) + assert.Equal(t, copyright, np.Metadata.Copyright) + assert.True(t, np.Metadata.DevelopmentDependency) + assert.True(t, np.Metadata.RequireLicenseAcceptance) assert.Equal(t, projectURL, np.Metadata.ProjectURL) + assert.Equal(t, licenseURL, np.Metadata.LicenseURL) + assert.Equal(t, iconURL, np.Metadata.IconURL) assert.Equal(t, description, np.Metadata.Description) assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes) assert.Equal(t, readme, np.Metadata.Readme) + assert.Equal(t, tags, np.Metadata.Tags) + assert.Equal(t, minClientVersion, np.Metadata.MinClientVersion) assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL) assert.Len(t, np.Metadata.Dependencies, 1) assert.Contains(t, np.Metadata.Dependencies, targetFramework) diff --git a/routers/api/packages/nuget/api_v2.go b/routers/api/packages/nuget/api_v2.go index a726065ad0..073b2700ef 100644 --- a/routers/api/packages/nuget/api_v2.go +++ b/routers/api/packages/nuget/api_v2.go @@ -249,6 +249,9 @@ type FeedEntryProperties struct { Version string `xml:"d:Version"` NormalizedVersion string `xml:"d:NormalizedVersion"` Authors string `xml:"d:Authors"` + Owners string `xml:"d:Owners,omitempty"` + Copyright string `xml:"d:Copyright,omitempty"` + Language string `xml:"d:Language,omitempty"` Dependencies string `xml:"d:Dependencies"` Description string `xml:"d:Description"` VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"` @@ -258,9 +261,15 @@ type FeedEntryProperties struct { LastUpdated TypedValue[time.Time] `xml:"d:LastUpdated"` Published TypedValue[time.Time] `xml:"d:Published"` ProjectURL string `xml:"d:ProjectUrl,omitempty"` + LicenseURL string `xml:"d:LicenseUrl,omitempty"` + IconURL string `xml:"d:IconUrl,omitempty"` ReleaseNotes string `xml:"d:ReleaseNotes,omitempty"` RequireLicenseAcceptance TypedValue[bool] `xml:"d:RequireLicenseAcceptance"` - Title string `xml:"d:Title"` + DevelopmentDependency TypedValue[bool] `xml:"d:DevelopmentDependency"` + Title string `xml:"d:Title,omitempty"` + MinClientVersion string `xml:"d:MinClientVersion,omitempty"` + Tags string `xml:"d:Tags,omitempty"` + ID string `xml:"d:Id,omitempty"` } type FeedEntry struct { @@ -356,6 +365,9 @@ func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNames Version: pd.Version.Version, NormalizedVersion: pd.Version.Version, Authors: metadata.Authors, + Owners: metadata.Owners, + Copyright: metadata.Copyright, + Language: metadata.Language, Dependencies: buildDependencyString(metadata), Description: metadata.Description, VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount}, @@ -365,9 +377,15 @@ func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNames LastUpdated: createdValue, Published: createdValue, ProjectURL: metadata.ProjectURL, + LicenseURL: metadata.LicenseURL, + IconURL: metadata.IconURL, ReleaseNotes: metadata.ReleaseNotes, RequireLicenseAcceptance: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.RequireLicenseAcceptance}, - Title: pd.Package.Name, + DevelopmentDependency: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.DevelopmentDependency}, + Title: metadata.Title, + MinClientVersion: metadata.MinClientVersion, + Tags: metadata.Tags, + ID: pd.Package.Name, }, } diff --git a/tests/integration/api_packages_nuget_test.go b/tests/integration/api_packages_nuget_test.go index 03e2176fe5..47b9cfe86c 100644 --- a/tests/integration/api_packages_nuget_test.go +++ b/tests/integration/api_packages_nuget_test.go @@ -49,6 +49,9 @@ func TestPackageNuGet(t *testing.T) { Version string `xml:"Version"` NormalizedVersion string `xml:"NormalizedVersion"` Authors string `xml:"Authors"` + Owners string `xml:"Owners,omitempty"` + Copyright string `xml:"Copyright,omitempty"` + Language string `xml:"Language,omitempty"` Dependencies string `xml:"Dependencies"` Description string `xml:"Description"` VersionDownloadCount nuget.TypedValue[int64] `xml:"VersionDownloadCount"` @@ -58,9 +61,15 @@ func TestPackageNuGet(t *testing.T) { LastUpdated nuget.TypedValue[time.Time] `xml:"LastUpdated"` Published nuget.TypedValue[time.Time] `xml:"Published"` ProjectURL string `xml:"ProjectUrl,omitempty"` + LicenseURL string `xml:"LicenseUrl,omitempty"` + IconURL string `xml:"IconUrl,omitempty"` ReleaseNotes string `xml:"ReleaseNotes,omitempty"` RequireLicenseAcceptance nuget.TypedValue[bool] `xml:"RequireLicenseAcceptance"` + DevelopmentDependency nuget.TypedValue[bool] `xml:"DevelopmentDependency"` Title string `xml:"Title"` + MinClientVersion string `xml:"MinClientVersion,omitempty"` + Tags string `xml:"Tags,omitempty"` + ID string `xml:"Id,omitempty"` } type FeedEntry struct { @@ -87,21 +96,43 @@ func TestPackageNuGet(t *testing.T) { packageName := "test.package" packageVersion := "1.0.3" packageAuthors := "KN4CK3R" - packageDescription := "Gitea Test Package" + packageDescription := "Forgejo Test Package" symbolFilename := "test.pdb" symbolID := "d910bb6948bd4c6cb40155bcf52c3c94" + packageTitle := "Package Title" + packageLanguage := "Package Language" + packageOwners := "Package Owners" + packageCopyright := "Package Copyright" + packageProjectURL := "https://forgejo.org" + packageLicenseURL := "https://forgejo.org/docs/latest/license/" + packageIconURL := "https://codeberg.org/forgejo/governance/raw/branch/main/branding/logo/forgejo.png" + packageReleaseNotes := "Package Release Notes" + packageTags := "tag_1 tag_2 tag_3" + packageMinClientVersion := "1.0.0.0" + createPackage := func(id, version string) io.Reader { var buf bytes.Buffer archive := zip.NewWriter(&buf) w, _ := archive.Create("package.nuspec") w.Write([]byte(` - + ` + id + ` + ` + packageTitle + ` + ` + packageLanguage + ` ` + version + ` ` + packageAuthors + ` + ` + packageOwners + ` + ` + packageCopyright + ` + true + true + ` + packageProjectURL + ` + ` + packageLicenseURL + ` + ` + packageIconURL + ` ` + packageDescription + ` + ` + packageReleaseNotes + ` + ` + packageTags + ` @@ -115,11 +146,22 @@ func TestPackageNuGet(t *testing.T) { nuspec := ` - + ` + packageName + ` + ` + packageTitle + ` + ` + packageLanguage + ` ` + packageVersion + ` ` + packageAuthors + ` + ` + packageOwners + ` + ` + packageCopyright + ` + true + true + ` + packageProjectURL + ` + ` + packageLicenseURL + ` + ` + packageIconURL + ` ` + packageDescription + ` + ` + packageReleaseNotes + ` + ` + packageTags + ` @@ -325,7 +367,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) require.NoError(t, err) - assert.Equal(t, int64(414), pb.Size) + assert.Equal(t, int64(len(content)), pb.Size) case fmt.Sprintf("%s.%s.snupkg", packageName, packageVersion): assert.False(t, pf.IsLead) @@ -337,7 +379,7 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID) require.NoError(t, err) - assert.Equal(t, int64(453), pb.Size) + assert.Equal(t, int64(len([]byte(nuspec))), pb.Size) case symbolFilename: assert.False(t, pf.IsLead) @@ -668,10 +710,22 @@ AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`) var result FeedEntry decodeXML(t, resp, &result) - assert.Equal(t, packageName, result.Properties.Title) + assert.Equal(t, packageName, result.Properties.ID) assert.Equal(t, packageVersion, result.Properties.Version) assert.Equal(t, packageAuthors, result.Properties.Authors) assert.Equal(t, packageDescription, result.Properties.Description) + assert.Equal(t, packageTitle, result.Properties.Title) + assert.Equal(t, packageLanguage, result.Properties.Language) + assert.Equal(t, packageOwners, result.Properties.Owners) + assert.Equal(t, packageCopyright, result.Properties.Copyright) + assert.Equal(t, packageProjectURL, result.Properties.ProjectURL) + assert.Equal(t, packageLicenseURL, result.Properties.LicenseURL) + assert.Equal(t, packageIconURL, result.Properties.IconURL) + assert.Equal(t, packageReleaseNotes, result.Properties.ReleaseNotes) + assert.Equal(t, packageTags, result.Properties.Tags) + assert.Equal(t, packageMinClientVersion, result.Properties.MinClientVersion) + assert.True(t, result.Properties.DevelopmentDependency.Value) + assert.True(t, result.Properties.RequireLicenseAcceptance.Value) assert.Equal(t, "Microsoft.CSharp:4.5.0:.NETStandard2.0", result.Properties.Dependencies) }) From ac84238c0cde38302275eb56eb43c7116485794f Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Tue, 4 Mar 2025 16:44:37 +0000 Subject: [PATCH 225/669] fix: Use correct table in migration v18 (#7114) Fix for https://codeberg.org/forgejo/forgejo/issues/6971 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7114 Reviewed-by: floss4good Reviewed-by: Gusted Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- models/forgejo_migrations/v18.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/forgejo_migrations/v18.go b/models/forgejo_migrations/v18.go index afccfbfe15..e6c1493f0e 100644 --- a/models/forgejo_migrations/v18.go +++ b/models/forgejo_migrations/v18.go @@ -14,5 +14,5 @@ type FollowingRepo struct { } func CreateFollowingRepoTable(x *xorm.Engine) error { - return x.Sync(new(FederatedUser)) + return x.Sync(new(FollowingRepo)) } From e4a083dc534569fca1d04d48283eb376c43b064a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 4 Mar 2025 21:12:48 +0000 Subject: [PATCH 226/669] Update dependency go to v1.24.1 (forgejo) (#7117) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7117 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1376d1c2ce..f9c6a21408 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module code.gitea.io/gitea go 1.24 -toolchain go1.24.0 +toolchain go1.24.1 require ( code.forgejo.org/f3/gof3/v3 v3.10.4 From c6f51e08bad17abf6b710dbe20bbd95ed9943947 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Tue, 4 Mar 2025 21:37:51 +0000 Subject: [PATCH 227/669] fix(ui): use discussions icon in issue list entries (#7099) So it is consistent with the icon used in the tab with comments. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7099 Reviewed-by: Gusted Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- templates/shared/issuelist.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 6318d02139..5a45ba5a71 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -45,7 +45,7 @@ {{if .NumComments}} {{end}} From 4b84e98f46d6aef2e1be965f3439dcc5e76e0154 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Tue, 4 Mar 2025 21:38:35 +0000 Subject: [PATCH 228/669] Update module github.com/golangci/golangci-lint/cmd/golangci-lint to v1.64.6 (forgejo) (#7118) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- .golangci.yml | 2 +- Makefile | 2 +- cmd/hook_test.go | 3 +-- cmd/migrate_storage_test.go | 5 ++-- models/issues/issue_test.go | 7 +++--- models/user/avatar_test.go | 3 +-- models/user/user_test.go | 5 ++-- models/webhook/webhook_test.go | 13 +++++----- modules/cache/context_test.go | 5 ++-- modules/git/blame_sha256_test.go | 2 +- modules/git/blame_test.go | 2 +- modules/git/command_test.go | 17 ++++++------- modules/git/commit_info_test.go | 5 ++-- modules/git/grep_test.go | 25 +++++++++---------- modules/git/notes_test.go | 22 +++++++--------- modules/git/repo_base_test.go | 4 +-- modules/git/repo_test.go | 7 +++--- modules/indexer/code/indexer_test.go | 9 +++---- modules/indexer/issues/indexer_test.go | 23 ++++++++--------- .../indexer/issues/internal/tests/tests.go | 16 ++++++------ modules/indexer/stats/indexer_test.go | 3 +-- modules/lfs/http_client_test.go | 4 +-- modules/lfs/transferadapter_test.go | 7 +++--- modules/log/event_writer_buffer_test.go | 3 +-- modules/log/event_writer_conn_test.go | 3 +-- modules/log/groutinelabel_test.go | 2 +- modules/log/logger_test.go | 9 +++---- modules/markup/html_test.go | 2 +- modules/markup/markdown/markdown_test.go | 2 +- modules/process/manager_stacktraces_test.go | 7 +++--- modules/process/manager_test.go | 8 +++--- modules/queue/base_test.go | 4 +-- modules/queue/manager_test.go | 9 +++---- modules/queue/workerqueue_test.go | 5 ++-- modules/secret/secret.go | 4 +-- modules/templates/util_render_test.go | 22 ++++++++-------- modules/testlogger/testlogger.go | 2 +- routers/api/actions/ping/ping_test.go | 3 +-- routers/common/errpage_test.go | 3 +-- routers/private/hook_verification_test.go | 3 +-- services/actions/context_test.go | 3 +-- services/auth/oauth2_test.go | 5 ++-- services/mailer/mail_admin_new_user_test.go | 2 +- services/mailer/mail_test.go | 18 ++++++------- services/markup/processorhelper_test.go | 9 +++---- services/migrations/codebase_test.go | 3 +-- services/migrations/gitea_downloader_test.go | 3 +-- services/migrations/gitea_uploader_test.go | 9 +++---- services/migrations/github_test.go | 3 +-- services/migrations/gitlab.go | 2 +- services/migrations/gitlab_test.go | 9 +++---- services/migrations/gogs_test.go | 5 ++-- services/migrations/onedev_test.go | 3 +-- services/pull/check_test.go | 3 +-- services/repository/lfs_test.go | 3 +-- services/webhook/default_test.go | 13 +++++----- services/webhook/deliver_test.go | 24 ++++++------------ services/webhook/dingtalk_test.go | 3 +-- services/webhook/discord_test.go | 3 +-- services/webhook/feishu_test.go | 3 +-- services/webhook/matrix_test.go | 3 +-- services/webhook/msteams_test.go | 3 +-- services/webhook/packagist_test.go | 3 +-- services/webhook/slack_test.go | 3 +-- services/webhook/sourcehut/builds_test.go | 5 ++-- services/webhook/telegram_test.go | 3 +-- tests/e2e/utils_e2e_test.go | 2 +- tests/integration/actions_job_test.go | 3 +-- tests/integration/actions_route_test.go | 9 +++---- tests/integration/actions_runner_test.go | 12 ++++----- tests/integration/admin_user_test.go | 5 ++-- .../api_helper_for_declarative_test.go | 3 +-- tests/integration/api_private_serv_test.go | 4 +-- .../integration/api_repo_file_create_test.go | 5 ++-- .../integration/api_repo_file_update_test.go | 3 +-- .../integration/api_repo_files_change_test.go | 3 +-- tests/integration/api_wiki_test.go | 3 +-- tests/integration/auth_ldap_test.go | 17 ++++++------- tests/integration/cmd_forgejo_actions_test.go | 3 +-- tests/integration/cmd_forgejo_f3_test.go | 2 +- tests/integration/codeowner_test.go | 3 +-- tests/integration/dump_restore_test.go | 10 +++----- .../git_helper_for_declarative_test.go | 4 +-- tests/integration/integration_test.go | 2 +- tests/integration/issue_test.go | 3 +-- tests/integration/lfs_view_test.go | 3 +-- tests/integration/linguist_test.go | 3 +-- .../migration-test/migration_test.go | 9 +++---- tests/integration/mirror_pull_test.go | 3 +-- tests/integration/mirror_push_test.go | 3 +-- tests/integration/oauth_test.go | 11 ++++---- tests/integration/pull_merge_test.go | 17 ++++++------- tests/integration/pull_request_task_test.go | 3 +-- tests/integration/pull_review_test.go | 5 ++-- tests/integration/remote_test.go | 15 ++++++----- tests/integration/wiki_test.go | 3 +-- tests/test_utils.go | 4 +-- 97 files changed, 258 insertions(+), 345 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 0678b90bfc..cceb7070e7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,12 +19,12 @@ linters: - revive - staticcheck - stylecheck - - tenv - testifylint - typecheck - unconvert - unused - unparam + - usetesting - wastedassign run: diff --git a/Makefile b/Makefile index d8882f113e..86b1771d01 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.2.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.6 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go diff --git a/cmd/hook_test.go b/cmd/hook_test.go index 1f0ee7087b..4fa249a316 100644 --- a/cmd/hook_test.go +++ b/cmd/hook_test.go @@ -6,7 +6,6 @@ package cmd import ( "bufio" "bytes" - "context" "io" "net/http" "net/http/httptest" @@ -42,7 +41,7 @@ func captureOutput(t *testing.T, stdFD *os.File) (finish func() (output string)) } func TestPktLine(t *testing.T) { - ctx := context.Background() + ctx := t.Context() t.Run("Read", func(t *testing.T) { s := strings.NewReader("0000") diff --git a/cmd/migrate_storage_test.go b/cmd/migrate_storage_test.go index 56745e9a38..245f0cacff 100644 --- a/cmd/migrate_storage_test.go +++ b/cmd/migrate_storage_test.go @@ -4,7 +4,6 @@ package cmd import ( - "context" "io" "os" "strings" @@ -31,7 +30,7 @@ func createLocalStorage(t *testing.T) (storage.ObjectStorage, string) { p := t.TempDir() storage, err := storage.NewLocalStorage( - context.Background(), + t.Context(), &setting.Storage{ Path: p, }) @@ -72,7 +71,7 @@ func TestMigratePackages(t *testing.T) { assert.NotNil(t, v) assert.NotNil(t, f) - ctx := context.Background() + ctx := t.Context() dstStorage, p := createLocalStorage(t) diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go index 580be9663b..d121435f0a 100644 --- a/models/issues/issue_test.go +++ b/models/issues/issue_test.go @@ -4,7 +4,6 @@ package issues_test import ( - "context" "fmt" "sort" "sync" @@ -309,7 +308,7 @@ func TestIssue_ResolveMentions(t *testing.T) { func TestResourceIndex(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) - beforeCount, err := issues_model.CountIssues(context.Background(), &issues_model.IssuesOptions{}) + beforeCount, err := issues_model.CountIssues(t.Context(), &issues_model.IssuesOptions{}) require.NoError(t, err) var wg sync.WaitGroup @@ -326,7 +325,7 @@ func TestResourceIndex(t *testing.T) { t.Parallel() wg.Wait() - afterCount, err := issues_model.CountIssues(context.Background(), &issues_model.IssuesOptions{}) + afterCount, err := issues_model.CountIssues(t.Context(), &issues_model.IssuesOptions{}) require.NoError(t, err) assert.EqualValues(t, 100, afterCount-beforeCount) }) @@ -354,7 +353,7 @@ func TestCorrectIssueStats(t *testing.T) { wg.Wait() // Now we will get all issueID's that match the "Bugs are nasty" query. - issues, err := issues_model.Issues(context.TODO(), &issues_model.IssuesOptions{ + issues, err := issues_model.Issues(t.Context(), &issues_model.IssuesOptions{ Paginator: &db.ListOptions{ PageSize: issueAmount, }, diff --git a/models/user/avatar_test.go b/models/user/avatar_test.go index ea96ab4f97..974a714477 100644 --- a/models/user/avatar_test.go +++ b/models/user/avatar_test.go @@ -4,7 +4,6 @@ package user import ( - "context" "io" "strings" "testing" @@ -37,7 +36,7 @@ func TestUserAvatarGenerate(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) var err error tmpDir := t.TempDir() - storage.Avatars, err = storage.NewLocalStorage(context.Background(), &setting.Storage{Path: tmpDir}) + storage.Avatars, err = storage.NewLocalStorage(t.Context(), &setting.Storage{Path: tmpDir}) require.NoError(t, err) u := unittest.AssertExistsAndLoadBean(t, &User{ID: 2}) diff --git a/models/user/user_test.go b/models/user/user_test.go index 8c554be86c..4f238967c1 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -5,7 +5,6 @@ package user_test import ( - "context" "crypto/rand" "encoding/hex" "fmt" @@ -346,7 +345,7 @@ func TestCreateUserCustomTimestamps(t *testing.T) { err := user_model.CreateUser(db.DefaultContext, user) require.NoError(t, err) - fetched, err := user_model.GetUserByID(context.Background(), user.ID) + fetched, err := user_model.GetUserByID(t.Context(), user.ID) require.NoError(t, err) assert.Equal(t, creationTimestamp, fetched.CreatedUnix) assert.Equal(t, creationTimestamp, fetched.UpdatedUnix) @@ -373,7 +372,7 @@ func TestCreateUserWithoutCustomTimestamps(t *testing.T) { timestampEnd := time.Now().Unix() - fetched, err := user_model.GetUserByID(context.Background(), user.ID) + fetched, err := user_model.GetUserByID(t.Context(), user.ID) require.NoError(t, err) assert.LessOrEqual(t, timestampStart, fetched.CreatedUnix) diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go index 848440b84a..6af9c26c1c 100644 --- a/models/webhook/webhook_test.go +++ b/models/webhook/webhook_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" "time" @@ -262,7 +261,7 @@ func TestCleanupHookTaskTable_PerWebhook_DeletesDelivered(t *testing.T) { require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) - require.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0)) + require.NoError(t, CleanupHookTaskTable(t.Context(), PerWebhook, 168*time.Hour, 0)) unittest.AssertNotExistsBean(t, hookTask) } @@ -278,7 +277,7 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesUndelivered(t *testing.T) { require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) - require.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 0)) + require.NoError(t, CleanupHookTaskTable(t.Context(), PerWebhook, 168*time.Hour, 0)) unittest.AssertExistsAndLoadBean(t, hookTask) } @@ -295,7 +294,7 @@ func TestCleanupHookTaskTable_PerWebhook_LeavesMostRecentTask(t *testing.T) { require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) - require.NoError(t, CleanupHookTaskTable(context.Background(), PerWebhook, 168*time.Hour, 1)) + require.NoError(t, CleanupHookTaskTable(t.Context(), PerWebhook, 168*time.Hour, 1)) unittest.AssertExistsAndLoadBean(t, hookTask) } @@ -312,7 +311,7 @@ func TestCleanupHookTaskTable_OlderThan_DeletesDelivered(t *testing.T) { require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) - require.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0)) + require.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0)) unittest.AssertNotExistsBean(t, hookTask) } @@ -328,7 +327,7 @@ func TestCleanupHookTaskTable_OlderThan_LeavesUndelivered(t *testing.T) { require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) - require.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0)) + require.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0)) unittest.AssertExistsAndLoadBean(t, hookTask) } @@ -345,6 +344,6 @@ func TestCleanupHookTaskTable_OlderThan_LeavesTaskEarlierThanAgeToDelete(t *test require.NoError(t, err) unittest.AssertExistsAndLoadBean(t, hookTask) - require.NoError(t, CleanupHookTaskTable(context.Background(), OlderThan, 168*time.Hour, 0)) + require.NoError(t, CleanupHookTaskTable(t.Context(), OlderThan, 168*time.Hour, 0)) unittest.AssertExistsAndLoadBean(t, hookTask) } diff --git a/modules/cache/context_test.go b/modules/cache/context_test.go index 072c39440f..4f0f06f535 100644 --- a/modules/cache/context_test.go +++ b/modules/cache/context_test.go @@ -4,7 +4,6 @@ package cache import ( - "context" "testing" "time" @@ -13,7 +12,7 @@ import ( ) func TestWithCacheContext(t *testing.T) { - ctx := WithCacheContext(context.Background()) + ctx := WithCacheContext(t.Context()) v := GetContextData(ctx, "empty_field", "my_config1") assert.Nil(t, v) @@ -53,7 +52,7 @@ func TestWithCacheContext(t *testing.T) { } func TestWithNoCacheContext(t *testing.T) { - ctx := context.Background() + ctx := t.Context() const field = "system_setting" diff --git a/modules/git/blame_sha256_test.go b/modules/git/blame_sha256_test.go index eeeeb9fdb5..c37f40775b 100644 --- a/modules/git/blame_sha256_test.go +++ b/modules/git/blame_sha256_test.go @@ -14,7 +14,7 @@ import ( func TestReadingBlameOutputSha256(t *testing.T) { skipIfSHA256NotSupported(t) - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() t.Run("Without .git-blame-ignore-revs", func(t *testing.T) { diff --git a/modules/git/blame_test.go b/modules/git/blame_test.go index 65320c78c0..b8fc59dd9e 100644 --- a/modules/git/blame_test.go +++ b/modules/git/blame_test.go @@ -12,7 +12,7 @@ import ( ) func TestReadingBlameOutput(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() t.Run("Without .git-blame-ignore-revs", func(t *testing.T) { diff --git a/modules/git/command_test.go b/modules/git/command_test.go index d3b8338d02..ace43598fc 100644 --- a/modules/git/command_test.go +++ b/modules/git/command_test.go @@ -4,7 +4,6 @@ package git import ( - "context" "testing" "github.com/stretchr/testify/assert" @@ -12,13 +11,13 @@ import ( ) func TestRunWithContextStd(t *testing.T) { - cmd := NewCommand(context.Background(), "--version") + cmd := NewCommand(t.Context(), "--version") stdout, stderr, err := cmd.RunStdString(&RunOpts{}) require.NoError(t, err) assert.Empty(t, stderr) assert.Contains(t, stdout, "git version") - cmd = NewCommand(context.Background(), "--no-such-arg") + cmd = NewCommand(t.Context(), "--no-such-arg") stdout, stderr, err = cmd.RunStdString(&RunOpts{}) if assert.Error(t, err) { assert.Equal(t, stderr, err.Stderr()) @@ -27,16 +26,16 @@ func TestRunWithContextStd(t *testing.T) { assert.Empty(t, stdout) } - cmd = NewCommand(context.Background()) + cmd = NewCommand(t.Context()) cmd.AddDynamicArguments("-test") require.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand) - cmd = NewCommand(context.Background()) + cmd = NewCommand(t.Context()) cmd.AddDynamicArguments("--test") require.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand) subCmd := "version" - cmd = NewCommand(context.Background()).AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production + cmd = NewCommand(t.Context()).AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production stdout, stderr, err = cmd.RunStdString(&RunOpts{}) require.NoError(t, err) assert.Empty(t, stderr) @@ -55,15 +54,15 @@ func TestGitArgument(t *testing.T) { } func TestCommandString(t *testing.T) { - cmd := NewCommandContextNoGlobals(context.Background(), "a", "-m msg", "it's a test", `say "hello"`) + cmd := NewCommandContextNoGlobals(t.Context(), "a", "-m msg", "it's a test", `say "hello"`) assert.EqualValues(t, cmd.prog+` a "-m msg" "it's a test" "say \"hello\""`, cmd.String()) - cmd = NewCommandContextNoGlobals(context.Background(), "url: https://a:b@c/") + cmd = NewCommandContextNoGlobals(t.Context(), "url: https://a:b@c/") assert.EqualValues(t, cmd.prog+` "url: https://sanitized-credential@c/"`, cmd.toString(true)) } func TestGrepOnlyFunction(t *testing.T) { - cmd := NewCommand(context.Background(), "anything-but-grep") + cmd := NewCommand(t.Context(), "anything-but-grep") assert.Panics(t, func() { cmd.AddGitGrepExpression("whatever") }) diff --git a/modules/git/commit_info_test.go b/modules/git/commit_info_test.go index dbe9ab547d..898ac6b13a 100644 --- a/modules/git/commit_info_test.go +++ b/modules/git/commit_info_test.go @@ -4,7 +4,6 @@ package git import ( - "context" "path/filepath" "testing" "time" @@ -84,7 +83,7 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) { } // FIXME: Context.TODO() - if graceful has started we should use its Shutdown context otherwise use install signals in TestMain. - commitsInfo, treeCommit, err := entries.GetCommitsInfo(context.TODO(), commit, testCase.Path) + commitsInfo, treeCommit, err := entries.GetCommitsInfo(t.Context(), commit, testCase.Path) require.NoError(t, err, "Unable to get commit information for entries of subtree: %s in commit: %s from testcase due to error: %v", testCase.Path, testCase.CommitID, err) if err != nil { t.FailNow() @@ -161,7 +160,7 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) { b.ResetTimer() b.Run(benchmark.name, func(b *testing.B) { for i := 0; i < b.N; i++ { - _, _, err := entries.GetCommitsInfo(context.Background(), commit, "") + _, _, err := entries.GetCommitsInfo(b.Context(), commit, "") if err != nil { b.Fatal(err) } diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index c40b33664a..534468e268 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -5,7 +5,6 @@ package git import ( "bytes" - "context" "os" "path" "path/filepath" @@ -20,7 +19,7 @@ func TestGrepSearch(t *testing.T) { require.NoError(t, err) defer repo.Close() - res, err := GrepSearch(context.Background(), repo, "public", GrepOptions{}) + res, err := GrepSearch(t.Context(), repo, "public", GrepOptions{}) require.NoError(t, err) assert.Equal(t, []*GrepResult{ { @@ -43,7 +42,7 @@ func TestGrepSearch(t *testing.T) { }, }, res) - res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1, ContextLineNumber: 2}) + res, err = GrepSearch(t.Context(), repo, "void", GrepOptions{MaxResultLimit: 1, ContextLineNumber: 2}) require.NoError(t, err) assert.Equal(t, []*GrepResult{ { @@ -60,7 +59,7 @@ func TestGrepSearch(t *testing.T) { }, }, res) - res, err = GrepSearch(context.Background(), repo, "world", GrepOptions{MatchesPerFile: 1}) + res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{MatchesPerFile: 1}) require.NoError(t, err) assert.Equal(t, []*GrepResult{ { @@ -89,7 +88,7 @@ func TestGrepSearch(t *testing.T) { }, }, res) - res, err = GrepSearch(context.Background(), repo, "world", GrepOptions{ + res, err = GrepSearch(t.Context(), repo, "world", GrepOptions{ MatchesPerFile: 1, Filename: "java-hello/", }) @@ -103,11 +102,11 @@ func TestGrepSearch(t *testing.T) { }, }, res) - res, err = GrepSearch(context.Background(), repo, "no-such-content", GrepOptions{}) + res, err = GrepSearch(t.Context(), repo, "no-such-content", GrepOptions{}) require.NoError(t, err) assert.Empty(t, res) - res, err = GrepSearch(context.Background(), &Repository{Path: "no-such-git-repo"}, "no-such-content", GrepOptions{}) + res, err = GrepSearch(t.Context(), &Repository{Path: "no-such-git-repo"}, "no-such-content", GrepOptions{}) require.Error(t, err) assert.Empty(t, res) } @@ -131,7 +130,7 @@ func TestGrepDashesAreFine(t *testing.T) { err = CommitChanges(tmpDir, CommitChangesOptions{Message: "Dashes are cool sometimes"}) require.NoError(t, err) - res, err := GrepSearch(context.Background(), gitRepo, "--", GrepOptions{}) + res, err := GrepSearch(t.Context(), gitRepo, "--", GrepOptions{}) require.NoError(t, err) assert.Len(t, res, 1) assert.Equal(t, "with-dashes", res[0].Filename) @@ -156,7 +155,7 @@ func TestGrepNoBinary(t *testing.T) { err = CommitChanges(tmpDir, CommitChangesOptions{Message: "Binary and text files"}) require.NoError(t, err) - res, err := GrepSearch(context.Background(), gitRepo, "BINARY", GrepOptions{}) + res, err := GrepSearch(t.Context(), gitRepo, "BINARY", GrepOptions{}) require.NoError(t, err) assert.Len(t, res, 1) assert.Equal(t, "TEXT", res[0].Filename) @@ -180,7 +179,7 @@ func TestGrepLongFiles(t *testing.T) { err = CommitChanges(tmpDir, CommitChangesOptions{Message: "Long file"}) require.NoError(t, err) - res, err := GrepSearch(context.Background(), gitRepo, "a", GrepOptions{}) + res, err := GrepSearch(t.Context(), gitRepo, "a", GrepOptions{}) require.NoError(t, err) assert.Len(t, res, 1) assert.Len(t, res[0].LineCodes[0], 65*1024) @@ -210,7 +209,7 @@ func TestGrepRefs(t *testing.T) { err = CommitChanges(tmpDir, CommitChangesOptions{Message: "add BCD"}) require.NoError(t, err) - res, err := GrepSearch(context.Background(), gitRepo, "a", GrepOptions{RefName: "v1"}) + res, err := GrepSearch(t.Context(), gitRepo, "a", GrepOptions{RefName: "v1"}) require.NoError(t, err) assert.Len(t, res, 1) assert.Equal(t, "A", res[0].LineCodes[0]) @@ -236,12 +235,12 @@ func TestGrepCanHazRegexOnDemand(t *testing.T) { require.NoError(t, err) // should find nothing by default... - res, err := GrepSearch(context.Background(), gitRepo, "\\bmatch\\b", GrepOptions{}) + res, err := GrepSearch(t.Context(), gitRepo, "\\bmatch\\b", GrepOptions{}) require.NoError(t, err) assert.Empty(t, res) // ... unless configured explicitly - res, err = GrepSearch(context.Background(), gitRepo, "\\bmatch\\b", GrepOptions{Mode: RegExpGrepMode}) + res, err = GrepSearch(t.Context(), gitRepo, "\\bmatch\\b", GrepOptions{Mode: RegExpGrepMode}) require.NoError(t, err) assert.Len(t, res, 1) assert.Equal(t, "matching", res[0].Filename) diff --git a/modules/git/notes_test.go b/modules/git/notes_test.go index cb9f39b93a..69ed3b8a6a 100644 --- a/modules/git/notes_test.go +++ b/modules/git/notes_test.go @@ -4,8 +4,6 @@ package git_test import ( - "context" - "os" "path/filepath" "testing" @@ -32,7 +30,7 @@ func TestGetNotes(t *testing.T) { defer bareRepo1.Close() note := git.Note{} - err = git.GetNote(context.Background(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) + err = git.GetNote(t.Context(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) require.NoError(t, err) assert.Equal(t, []byte("Note contents\n"), note.Message) assert.Equal(t, "Vladimir Panteleev", note.Commit.Author.Name) @@ -45,10 +43,10 @@ func TestGetNestedNotes(t *testing.T) { defer repo.Close() note := git.Note{} - err = git.GetNote(context.Background(), repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", ¬e) + err = git.GetNote(t.Context(), repo, "3e668dbfac39cbc80a9ff9c61eb565d944453ba4", ¬e) require.NoError(t, err) assert.Equal(t, []byte("Note 2"), note.Message) - err = git.GetNote(context.Background(), repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", ¬e) + err = git.GetNote(t.Context(), repo, "ba0a96fa63532d6c5087ecef070b0250ed72fa47", ¬e) require.NoError(t, err) assert.Equal(t, []byte("Note 1"), note.Message) } @@ -60,7 +58,7 @@ func TestGetNonExistentNotes(t *testing.T) { defer bareRepo1.Close() note := git.Note{} - err = git.GetNote(context.Background(), bareRepo1, "non_existent_sha", ¬e) + err = git.GetNote(t.Context(), bareRepo1, "non_existent_sha", ¬e) require.Error(t, err) assert.IsType(t, git.ErrNotExist{}, err) } @@ -68,19 +66,17 @@ func TestGetNonExistentNotes(t *testing.T) { func TestSetNote(t *testing.T) { bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") - tempDir, err := os.MkdirTemp("", "") - require.NoError(t, err) - defer os.RemoveAll(tempDir) + tempDir := t.TempDir() require.NoError(t, unittest.CopyDir(bareRepo1Path, filepath.Join(tempDir, "repo1"))) bareRepo1, err := openRepositoryWithDefaultContext(filepath.Join(tempDir, "repo1")) require.NoError(t, err) defer bareRepo1.Close() - require.NoError(t, git.SetNote(context.Background(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", "This is a new note", "Test", "test@test.com")) + require.NoError(t, git.SetNote(t.Context(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", "This is a new note", "Test", "test@test.com")) note := git.Note{} - err = git.GetNote(context.Background(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) + err = git.GetNote(t.Context(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) require.NoError(t, err) assert.Equal(t, []byte("This is a new note\n"), note.Message) assert.Equal(t, "Test", note.Commit.Author.Name) @@ -97,10 +93,10 @@ func TestRemoveNote(t *testing.T) { require.NoError(t, err) defer bareRepo1.Close() - require.NoError(t, git.RemoveNote(context.Background(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653")) + require.NoError(t, git.RemoveNote(t.Context(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653")) note := git.Note{} - err = git.GetNote(context.Background(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) + err = git.GetNote(t.Context(), bareRepo1, "95bb4d39648ee7e325106df01a621c530863a653", ¬e) require.Error(t, err) assert.IsType(t, git.ErrNotExist{}, err) } diff --git a/modules/git/repo_base_test.go b/modules/git/repo_base_test.go index 323b28f476..c9ac6a8559 100644 --- a/modules/git/repo_base_test.go +++ b/modules/git/repo_base_test.go @@ -14,7 +14,7 @@ import ( // This unit test relies on the implementation detail of CatFileBatch. func TestCatFileBatch(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() repo, err := OpenRepository(ctx, "./tests/repos/repo1_bare") @@ -89,7 +89,7 @@ func TestCatFileBatch(t *testing.T) { // This unit test relies on the implementation detail of CatFileBatchCheck. func TestCatFileBatchCheck(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() repo, err := OpenRepository(ctx, "./tests/repos/repo1_bare") diff --git a/modules/git/repo_test.go b/modules/git/repo_test.go index 8fb19a5043..c4ef9dbe96 100644 --- a/modules/git/repo_test.go +++ b/modules/git/repo_test.go @@ -4,7 +4,6 @@ package git import ( - "context" "path/filepath" "testing" @@ -34,21 +33,21 @@ func TestRepoIsEmpty(t *testing.T) { func TestRepoGetDivergingCommits(t *testing.T) { bareRepo1Path := filepath.Join(testReposDir, "repo1_bare") - do, err := GetDivergingCommits(context.Background(), bareRepo1Path, "master", "branch2") + do, err := GetDivergingCommits(t.Context(), bareRepo1Path, "master", "branch2") require.NoError(t, err) assert.Equal(t, DivergeObject{ Ahead: 1, Behind: 5, }, do) - do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "master") + do, err = GetDivergingCommits(t.Context(), bareRepo1Path, "master", "master") require.NoError(t, err) assert.Equal(t, DivergeObject{ Ahead: 0, Behind: 0, }, do) - do, err = GetDivergingCommits(context.Background(), bareRepo1Path, "master", "test") + do, err = GetDivergingCommits(t.Context(), bareRepo1Path, "master", "test") require.NoError(t, err) assert.Equal(t, DivergeObject{ Ahead: 0, diff --git a/modules/indexer/code/indexer_test.go b/modules/indexer/code/indexer_test.go index 9ef16f3412..559b85626f 100644 --- a/modules/indexer/code/indexer_test.go +++ b/modules/indexer/code/indexer_test.go @@ -4,7 +4,6 @@ package code import ( - "context" "os" "testing" @@ -94,7 +93,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) { for _, kw := range keywords { t.Run(kw.Keyword, func(t *testing.T) { - total, res, langs, err := indexer.Search(context.TODO(), &internal.SearchOptions{ + total, res, langs, err := indexer.Search(t.Context(), &internal.SearchOptions{ RepoIDs: kw.RepoIDs, Keyword: kw.Keyword, Paginator: &db.ListOptions{ @@ -117,7 +116,7 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) { }) } - require.NoError(t, indexer.Delete(context.Background(), repoID)) + require.NoError(t, indexer.Delete(t.Context(), repoID)) }) } @@ -127,7 +126,7 @@ func TestBleveIndexAndSearch(t *testing.T) { dir := t.TempDir() idx := bleve.NewIndexer(dir) - _, err := idx.Init(context.Background()) + _, err := idx.Init(t.Context()) if err != nil { if idx != nil { idx.Close() @@ -149,7 +148,7 @@ func TestESIndexAndSearch(t *testing.T) { } indexer := elasticsearch.NewIndexer(u, "gitea_codes") - if _, err := indexer.Init(context.Background()); err != nil { + if _, err := indexer.Init(t.Context()); err != nil { if indexer != nil { indexer.Close() } diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index b96fecc6bc..9f80d70696 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -4,7 +4,6 @@ package issues import ( - "context" "testing" "code.gitea.io/gitea/models/db" @@ -82,7 +81,7 @@ func searchIssueWithKeyword(t *testing.T) { } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -127,7 +126,7 @@ func searchIssueInRepo(t *testing.T) { } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -198,7 +197,7 @@ func searchIssueByID(t *testing.T) { } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) } @@ -223,7 +222,7 @@ func searchIssueIsPull(t *testing.T) { }, } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -249,7 +248,7 @@ func searchIssueIsClosed(t *testing.T) { }, } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) } @@ -274,7 +273,7 @@ func searchIssueByMilestoneID(t *testing.T) { }, } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -306,7 +305,7 @@ func searchIssueByLabelID(t *testing.T) { }, } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -326,7 +325,7 @@ func searchIssueByTime(t *testing.T) { }, } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -346,7 +345,7 @@ func searchIssueWithOrder(t *testing.T) { }, } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -378,7 +377,7 @@ func searchIssueInProject(t *testing.T) { }, } for _, test := range tests { - issueIDs, _, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, _, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) @@ -402,7 +401,7 @@ func searchIssueWithPaginator(t *testing.T) { }, } for _, test := range tests { - issueIDs, total, err := SearchIssues(context.TODO(), &test.opts) + issueIDs, total, err := SearchIssues(t.Context(), &test.opts) require.NoError(t, err) assert.Equal(t, test.expectedIDs, issueIDs) diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 0763cd1297..80ff34713e 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -24,10 +24,10 @@ import ( ) func TestIndexer(t *testing.T, indexer internal.Indexer) { - _, err := indexer.Init(context.Background()) + _, err := indexer.Init(t.Context()) require.NoError(t, err) - require.NoError(t, indexer.Ping(context.Background())) + require.NoError(t, indexer.Ping(t.Context())) var ( ids []int64 @@ -39,32 +39,32 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) { ids = append(ids, v.ID) data[v.ID] = v } - require.NoError(t, indexer.Index(context.Background(), d...)) + require.NoError(t, indexer.Index(t.Context(), d...)) require.NoError(t, waitData(indexer, int64(len(data)))) } defer func() { - require.NoError(t, indexer.Delete(context.Background(), ids...)) + require.NoError(t, indexer.Delete(t.Context(), ids...)) }() for _, c := range cases { t.Run(c.Name, func(t *testing.T) { if len(c.ExtraData) > 0 { - require.NoError(t, indexer.Index(context.Background(), c.ExtraData...)) + require.NoError(t, indexer.Index(t.Context(), c.ExtraData...)) for _, v := range c.ExtraData { data[v.ID] = v } require.NoError(t, waitData(indexer, int64(len(data)))) defer func() { for _, v := range c.ExtraData { - require.NoError(t, indexer.Delete(context.Background(), v.ID)) + require.NoError(t, indexer.Delete(t.Context(), v.ID)) delete(data, v.ID) } require.NoError(t, waitData(indexer, int64(len(data)))) }() } - result, err := indexer.Search(context.Background(), c.SearchOptions) + result, err := indexer.Search(t.Context(), c.SearchOptions) require.NoError(t, err) if c.Expected != nil { @@ -80,7 +80,7 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) { // test counting c.SearchOptions.Paginator = &db.ListOptions{PageSize: 0} - countResult, err := indexer.Search(context.Background(), c.SearchOptions) + countResult, err := indexer.Search(t.Context(), c.SearchOptions) require.NoError(t, err) assert.Empty(t, countResult.Hits) assert.Equal(t, result.Total, countResult.Total) diff --git a/modules/indexer/stats/indexer_test.go b/modules/indexer/stats/indexer_test.go index b7e66f937c..781bb72eaa 100644 --- a/modules/indexer/stats/indexer_test.go +++ b/modules/indexer/stats/indexer_test.go @@ -4,7 +4,6 @@ package stats import ( - "context" "testing" "time" @@ -42,7 +41,7 @@ func TestRepoStatsIndex(t *testing.T) { err = UpdateRepoIndexer(repo) require.NoError(t, err) - require.NoError(t, queue.GetManager().FlushAll(context.Background(), 5*time.Second)) + require.NoError(t, queue.GetManager().FlushAll(t.Context(), 5*time.Second)) status, err := repo_model.GetIndexerStatus(db.DefaultContext, repo, repo_model.RepoIndexerTypeStats) require.NoError(t, err) diff --git a/modules/lfs/http_client_test.go b/modules/lfs/http_client_test.go index d78224a806..e80e4847f8 100644 --- a/modules/lfs/http_client_test.go +++ b/modules/lfs/http_client_test.go @@ -249,7 +249,7 @@ func TestHTTPClientDownload(t *testing.T) { }, } - err := client.Download(context.Background(), []Pointer{p}, func(p Pointer, content io.ReadCloser, objectError error) error { + err := client.Download(t.Context(), []Pointer{p}, func(p Pointer, content io.ReadCloser, objectError error) error { if objectError != nil { return objectError } @@ -349,7 +349,7 @@ func TestHTTPClientUpload(t *testing.T) { }, } - err := client.Upload(context.Background(), []Pointer{p}, func(p Pointer, objectError error) (io.ReadCloser, error) { + err := client.Upload(t.Context(), []Pointer{p}, func(p Pointer, objectError error) (io.ReadCloser, error) { return io.NopCloser(new(bytes.Buffer)), objectError }) if c.expectedError != "" { diff --git a/modules/lfs/transferadapter_test.go b/modules/lfs/transferadapter_test.go index 0d663d71f6..a90ee5c6c0 100644 --- a/modules/lfs/transferadapter_test.go +++ b/modules/lfs/transferadapter_test.go @@ -5,7 +5,6 @@ package lfs import ( "bytes" - "context" "io" "net/http" "strings" @@ -95,7 +94,7 @@ func TestBasicTransferAdapter(t *testing.T) { } for n, c := range cases { - _, err := a.Download(context.Background(), c.link) + _, err := a.Download(t.Context(), c.link) if len(c.expectederror) > 0 { assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) } else { @@ -128,7 +127,7 @@ func TestBasicTransferAdapter(t *testing.T) { } for n, c := range cases { - err := a.Upload(context.Background(), c.link, p, bytes.NewBufferString("dummy")) + err := a.Upload(t.Context(), c.link, p, bytes.NewBufferString("dummy")) if len(c.expectederror) > 0 { assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) } else { @@ -161,7 +160,7 @@ func TestBasicTransferAdapter(t *testing.T) { } for n, c := range cases { - err := a.Verify(context.Background(), c.link, p) + err := a.Verify(t.Context(), c.link, p) if len(c.expectederror) > 0 { assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) } else { diff --git a/modules/log/event_writer_buffer_test.go b/modules/log/event_writer_buffer_test.go index 5c0343b8a8..58c6be1399 100644 --- a/modules/log/event_writer_buffer_test.go +++ b/modules/log/event_writer_buffer_test.go @@ -4,7 +4,6 @@ package log_test import ( - "context" "testing" "code.gitea.io/gitea/modules/log" @@ -23,7 +22,7 @@ func TestBufferLogger(t *testing.T) { Expression: expected, }) - logger := log.NewLoggerWithWriters(context.Background(), "test", bufferWriter) + logger := log.NewLoggerWithWriters(t.Context(), "test", bufferWriter) logger.SendLogEvent(&log.Event{ Level: log.INFO, diff --git a/modules/log/event_writer_conn_test.go b/modules/log/event_writer_conn_test.go index de8694f2c5..0cf447149a 100644 --- a/modules/log/event_writer_conn_test.go +++ b/modules/log/event_writer_conn_test.go @@ -4,7 +4,6 @@ package log import ( - "context" "fmt" "io" "net" @@ -41,7 +40,7 @@ func TestConnLogger(t *testing.T) { level := INFO flags := LstdFlags | LUTC | Lfuncname - logger := NewLoggerWithWriters(context.Background(), "test", NewEventWriterConn("test-conn", WriterMode{ + logger := NewLoggerWithWriters(t.Context(), "test", NewEventWriterConn("test-conn", WriterMode{ Level: level, Prefix: prefix, Flags: FlagsFromBits(flags), diff --git a/modules/log/groutinelabel_test.go b/modules/log/groutinelabel_test.go index 34e99653d6..df8c1e9259 100644 --- a/modules/log/groutinelabel_test.go +++ b/modules/log/groutinelabel_test.go @@ -12,7 +12,7 @@ import ( ) func Test_getGoroutineLabels(t *testing.T) { - pprof.Do(context.Background(), pprof.Labels(), func(ctx context.Context) { + pprof.Do(t.Context(), pprof.Labels(), func(ctx context.Context) { currentLabels := getGoroutineLabels() pprof.ForLabels(ctx, func(key, value string) bool { assert.EqualValues(t, value, currentLabels[key]) diff --git a/modules/log/logger_test.go b/modules/log/logger_test.go index 0de14eb411..e04c9da8b0 100644 --- a/modules/log/logger_test.go +++ b/modules/log/logger_test.go @@ -4,7 +4,6 @@ package log import ( - "context" "sync" "testing" "time" @@ -53,7 +52,7 @@ func newDummyWriter(name string, level Level, delay time.Duration) *dummyWriter } func TestLogger(t *testing.T) { - logger := NewLoggerWithWriters(context.Background(), "test") + logger := NewLoggerWithWriters(t.Context(), "test") dump := logger.DumpWriters() assert.Empty(t, dump) @@ -88,7 +87,7 @@ func TestLogger(t *testing.T) { } func TestLoggerPause(t *testing.T) { - logger := NewLoggerWithWriters(context.Background(), "test") + logger := NewLoggerWithWriters(t.Context(), "test") w1 := newDummyWriter("dummy-1", DEBUG, 0) logger.AddWriters(w1) @@ -117,7 +116,7 @@ func (t testLogString) LogString() string { } func TestLoggerLogString(t *testing.T) { - logger := NewLoggerWithWriters(context.Background(), "test") + logger := NewLoggerWithWriters(t.Context(), "test") w1 := newDummyWriter("dummy-1", DEBUG, 0) w1.Mode.Colorize = true @@ -130,7 +129,7 @@ func TestLoggerLogString(t *testing.T) { } func TestLoggerExpressionFilter(t *testing.T) { - logger := NewLoggerWithWriters(context.Background(), "test") + logger := NewLoggerWithWriters(t.Context(), "test") w1 := newDummyWriter("dummy-1", DEBUG, 0) w1.Mode.Expression = "foo.*" diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 7d954d2a16..905a10c58c 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -680,7 +680,7 @@ func TestRender_FilePreview(t *testing.T) { defer test.MockVariableValue(&setting.StaticRootPath, "../../")() defer test.MockVariableValue(&setting.Names, []string{"english"})() defer test.MockVariableValue(&setting.Langs, []string{"en-US"})() - translation.InitLocales(context.Background()) + translation.InitLocales(t.Context()) setting.AppURL = markup.TestAppURL markup.Init(&markup.ProcessorHelper{ diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go index eb69cd4eec..bb59d72957 100644 --- a/modules/markup/markdown/markdown_test.go +++ b/modules/markup/markdown/markdown_test.go @@ -1190,7 +1190,7 @@ space

} for i, c := range cases { - result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input) + result, err := markdown.RenderString(&markup.RenderContext{Ctx: t.Context(), Links: c.Links, IsWiki: c.IsWiki}, input) require.NoError(t, err, "Unexpected error in testcase: %v", i) assert.Equal(t, template.HTML(c.Expected), result, "Unexpected result in testcase %v", i) } diff --git a/modules/process/manager_stacktraces_test.go b/modules/process/manager_stacktraces_test.go index d00fbc5ec8..509b424d8b 100644 --- a/modules/process/manager_stacktraces_test.go +++ b/modules/process/manager_stacktraces_test.go @@ -4,7 +4,6 @@ package process import ( - "context" "testing" "github.com/stretchr/testify/assert" @@ -12,13 +11,13 @@ import ( ) func TestProcessStacktraces(t *testing.T) { - _, _, finish := GetManager().AddContext(context.Background(), "Normal process") + _, _, finish := GetManager().AddContext(t.Context(), "Normal process") defer finish() - parentCtx, _, finish := GetManager().AddContext(context.Background(), "Children normal process") + parentCtx, _, finish := GetManager().AddContext(t.Context(), "Children normal process") defer finish() _, _, finish = GetManager().AddContext(parentCtx, "Children process") defer finish() - _, _, finish = GetManager().AddTypedContext(context.Background(), "System process", SystemProcessType, true) + _, _, finish = GetManager().AddTypedContext(t.Context(), "System process", SystemProcessType, true) defer finish() t.Run("No flat with no system process", func(t *testing.T) { diff --git a/modules/process/manager_test.go b/modules/process/manager_test.go index 36b2a912ea..0d637c8acc 100644 --- a/modules/process/manager_test.go +++ b/modules/process/manager_test.go @@ -23,7 +23,7 @@ func TestGetManager(t *testing.T) { func TestManager_AddContext(t *testing.T) { pm := Manager{processMap: make(map[IDType]*process), next: 1} - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() p1Ctx, _, finished := pm.AddContext(ctx, "foo") @@ -42,7 +42,7 @@ func TestManager_AddContext(t *testing.T) { func TestManager_Cancel(t *testing.T) { pm := Manager{processMap: make(map[IDType]*process), next: 1} - ctx, _, finished := pm.AddContext(context.Background(), "foo") + ctx, _, finished := pm.AddContext(t.Context(), "foo") defer finished() pm.Cancel(GetPID(ctx)) @@ -54,7 +54,7 @@ func TestManager_Cancel(t *testing.T) { } finished() - ctx, cancel, finished := pm.AddContext(context.Background(), "foo") + ctx, cancel, finished := pm.AddContext(t.Context(), "foo") defer finished() cancel() @@ -70,7 +70,7 @@ func TestManager_Cancel(t *testing.T) { func TestManager_Remove(t *testing.T) { pm := Manager{processMap: make(map[IDType]*process), next: 1} - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() p1Ctx, _, finished := pm.AddContext(ctx, "foo") diff --git a/modules/queue/base_test.go b/modules/queue/base_test.go index a5600fea63..d852a80b16 100644 --- a/modules/queue/base_test.go +++ b/modules/queue/base_test.go @@ -18,7 +18,7 @@ func testQueueBasic(t *testing.T, newFn func(cfg *BaseConfig) (baseQueue, error) q, err := newFn(cfg) require.NoError(t, err) - ctx := context.Background() + ctx := t.Context() _ = q.RemoveAll(ctx) cnt, err := q.Len(ctx) require.NoError(t, err) @@ -122,7 +122,7 @@ func TestBaseDummy(t *testing.T) { q, err := newBaseDummy(&BaseConfig{}, true) require.NoError(t, err) - ctx := context.Background() + ctx := t.Context() require.NoError(t, q.PushItem(ctx, []byte("foo"))) cnt, err := q.Len(ctx) diff --git a/modules/queue/manager_test.go b/modules/queue/manager_test.go index a76c238752..5806cbd6c9 100644 --- a/modules/queue/manager_test.go +++ b/modules/queue/manager_test.go @@ -4,7 +4,6 @@ package queue import ( - "context" "path/filepath" "testing" @@ -81,7 +80,7 @@ MAX_WORKERS = 123 require.NoError(t, err) - q1 := createWorkerPoolQueue[string](context.Background(), "no-such", cfgProvider, nil, false) + q1 := createWorkerPoolQueue[string](t.Context(), "no-such", cfgProvider, nil, false) assert.Equal(t, "no-such", q1.GetName()) assert.Equal(t, "dummy", q1.GetType()) // no handler, so it becomes dummy assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir1"), q1.baseConfig.DataFullDir) @@ -97,7 +96,7 @@ MAX_WORKERS = 123 assert.Equal(t, "string", q1.GetItemTypeName()) qid1 := GetManager().qidCounter - q2 := createWorkerPoolQueue(context.Background(), "sub", cfgProvider, func(s ...int) (unhandled []int) { return nil }, false) + q2 := createWorkerPoolQueue(t.Context(), "sub", cfgProvider, func(s ...int) (unhandled []int) { return nil }, false) assert.Equal(t, "sub", q2.GetName()) assert.Equal(t, "level", q2.GetType()) assert.Equal(t, filepath.Join(setting.AppDataPath, "queues/dir2"), q2.baseConfig.DataFullDir) @@ -119,7 +118,7 @@ MAX_WORKERS = 123 assert.Equal(t, 120, q1.workerMaxNum) stop := runWorkerPoolQueue(q2) - require.NoError(t, GetManager().GetManagedQueue(qid2).FlushWithContext(context.Background(), 0)) - require.NoError(t, GetManager().FlushAll(context.Background(), 0)) + require.NoError(t, GetManager().GetManagedQueue(qid2).FlushWithContext(t.Context(), 0)) + require.NoError(t, GetManager().FlushAll(t.Context(), 0)) stop() } diff --git a/modules/queue/workerqueue_test.go b/modules/queue/workerqueue_test.go index 4cfe8ede97..0060d88ec6 100644 --- a/modules/queue/workerqueue_test.go +++ b/modules/queue/workerqueue_test.go @@ -5,7 +5,6 @@ package queue import ( "bytes" - "context" "runtime" "strconv" "sync" @@ -59,7 +58,7 @@ func TestWorkerPoolQueueUnhandled(t *testing.T) { testRecorder.Record("push:%v", i) require.NoError(t, q.Push(i)) } - require.NoError(t, q.FlushWithContext(context.Background(), 0)) + require.NoError(t, q.FlushWithContext(t.Context(), 0)) stop() ok := true @@ -167,7 +166,7 @@ func testWorkerPoolQueuePersistence(t *testing.T, queueSetting setting.QueueSett q, _ := newWorkerPoolQueueForTest("pr_patch_checker_test", queueSetting, testHandler, true) stop := runWorkerPoolQueue(q) - require.NoError(t, q.FlushWithContext(context.Background(), 0)) + require.NoError(t, q.FlushWithContext(t.Context(), 0)) stop() } diff --git a/modules/secret/secret.go b/modules/secret/secret.go index e3557b91b9..fc63ec521b 100644 --- a/modules/secret/secret.go +++ b/modules/secret/secret.go @@ -27,7 +27,7 @@ func AesEncrypt(key, text []byte) ([]byte, error) { if _, err = io.ReadFull(rand.Reader, iv); err != nil { return nil, fmt.Errorf("AesEncrypt unable to read IV: %w", err) } - cfb := cipher.NewCFBEncrypter(block, iv) + cfb := cipher.NewCFBEncrypter(block, iv) //nolint:staticcheck cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(b)) return ciphertext, nil } @@ -43,7 +43,7 @@ func AesDecrypt(key, text []byte) ([]byte, error) { } iv := text[:aes.BlockSize] text = text[aes.BlockSize:] - cfb := cipher.NewCFBDecrypter(block, iv) + cfb := cipher.NewCFBDecrypter(block, iv) //nolint:staticcheck cfb.XORKeyStream(text, text) data, err := base64.StdEncoding.DecodeString(string(text)) if err != nil { diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 028cd2ba76..25a41f02c0 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -46,12 +46,12 @@ var testMetas = map[string]string{ } func TestApostrophesInMentions(t *testing.T) { - rendered := RenderMarkdownToHtml(context.Background(), "@mention-user's comment") + rendered := RenderMarkdownToHtml(t.Context(), "@mention-user's comment") assert.EqualValues(t, template.HTML("

@mention-user's comment

\n"), rendered) } func TestNonExistantUserMention(t *testing.T) { - rendered := RenderMarkdownToHtml(context.Background(), "@ThisUserDoesNotExist @mention-user") + rendered := RenderMarkdownToHtml(t.Context(), "@ThisUserDoesNotExist @mention-user") assert.EqualValues(t, template.HTML("

@ThisUserDoesNotExist @mention-user

\n"), rendered) } @@ -69,7 +69,7 @@ func TestRenderCommitBody(t *testing.T) { { name: "multiple lines", args: args{ - ctx: context.Background(), + ctx: t.Context(), msg: "first line\nsecond line", }, want: "second line", @@ -77,7 +77,7 @@ func TestRenderCommitBody(t *testing.T) { { name: "multiple lines with leading newlines", args: args{ - ctx: context.Background(), + ctx: t.Context(), msg: "\n\n\n\nfirst line\nsecond line", }, want: "second line", @@ -85,7 +85,7 @@ func TestRenderCommitBody(t *testing.T) { { name: "multiple lines with trailing newlines", args: args{ - ctx: context.Background(), + ctx: t.Context(), msg: "first line\nsecond line\n\n\n", }, want: "second line", @@ -117,19 +117,19 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit #123 space ` + "`code 👍 #123 code`" - assert.EqualValues(t, expected, RenderCommitBody(context.Background(), testInput, testMetas)) + assert.EqualValues(t, expected, RenderCommitBody(t.Context(), testInput, testMetas)) } func TestRenderCommitMessage(t *testing.T) { expected := `space @mention-user ` - assert.EqualValues(t, expected, RenderCommitMessage(context.Background(), testInput, testMetas)) + assert.EqualValues(t, expected, RenderCommitMessage(t.Context(), testInput, testMetas)) } func TestRenderCommitMessageLinkSubject(t *testing.T) { expected := `space @mention-user` - assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(context.Background(), testInput, "https://example.com/link", testMetas)) + assert.EqualValues(t, expected, RenderCommitMessageLinkSubject(t.Context(), testInput, "https://example.com/link", testMetas)) } func TestRenderIssueTitle(t *testing.T) { @@ -155,7 +155,7 @@ mail@domain.com space code :+1: #123 code ` - assert.EqualValues(t, expected, RenderIssueTitle(context.Background(), testInput, testMetas)) + assert.EqualValues(t, expected, RenderIssueTitle(t.Context(), testInput, testMetas)) } func TestRenderRefIssueTitle(t *testing.T) { @@ -181,7 +181,7 @@ mail@domain.com space code :+1: #123 code ` - assert.EqualValues(t, expected, RenderRefIssueTitle(context.Background(), testInput)) + assert.EqualValues(t, expected, RenderRefIssueTitle(t.Context(), testInput)) } func TestRenderMarkdownToHtml(t *testing.T) { @@ -207,7 +207,7 @@ com 88fc37a3c0a4dda553bdcfc80c178a58247f42fb mit space code :+1: #123 code

` - assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput)) + assert.EqualValues(t, expected, RenderMarkdownToHtml(t.Context(), testInput)) } func TestRenderLabels(t *testing.T) { diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index d176ab9e4a..caa8abd07b 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -471,7 +471,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() { _, _ = fmt.Fprintf(os.Stdout, "+++ %s ... still flushing after %v ...\n", t.Name(), SlowFlush) } }) - if err := queue.GetManager().FlushAll(context.Background(), time.Minute); err != nil { + if err := queue.GetManager().FlushAll(t.Context(), time.Minute); err != nil { t.Errorf("Flushing queues failed with error %v", err) } timer.Stop() diff --git a/routers/api/actions/ping/ping_test.go b/routers/api/actions/ping/ping_test.go index 098b003ea2..98d2dcb820 100644 --- a/routers/api/actions/ping/ping_test.go +++ b/routers/api/actions/ping/ping_test.go @@ -4,7 +4,6 @@ package ping import ( - "context" "net/http" "net/http/httptest" "testing" @@ -51,7 +50,7 @@ func MainServiceTest(t *testing.T, h http.Handler) { clients := []pingv1connect.PingServiceClient{connectClient, grpcClient, grpcWebClient} t.Run("ping request", func(t *testing.T) { for _, client := range clients { - result, err := client.Ping(context.Background(), connect.NewRequest(&pingv1.PingRequest{ + result, err := client.Ping(t.Context(), connect.NewRequest(&pingv1.PingRequest{ Data: "foobar", })) require.NoError(t, err) diff --git a/routers/common/errpage_test.go b/routers/common/errpage_test.go index 4fd63ba49e..f15d3f1b35 100644 --- a/routers/common/errpage_test.go +++ b/routers/common/errpage_test.go @@ -4,7 +4,6 @@ package common import ( - "context" "errors" "net/http" "net/http/httptest" @@ -21,7 +20,7 @@ import ( func TestRenderPanicErrorPage(t *testing.T) { w := httptest.NewRecorder() req := &http.Request{URL: &url.URL{}} - req = req.WithContext(middleware.WithContextData(context.Background())) + req = req.WithContext(middleware.WithContextData(t.Context())) RenderPanicErrorPage(w, req, errors.New("fake panic error (for test only)")) respContent := w.Body.String() assert.Contains(t, respContent, `class="page-content status-page-500"`) diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go index 5f0d1d0f4f..47e06245ed 100644 --- a/routers/private/hook_verification_test.go +++ b/routers/private/hook_verification_test.go @@ -4,7 +4,6 @@ package private import ( - "context" "testing" "code.gitea.io/gitea/models/unittest" @@ -18,7 +17,7 @@ var testReposDir = "tests/repos/" func TestVerifyCommits(t *testing.T) { unittest.PrepareTestEnv(t) - gitRepo, err := git.OpenRepository(context.Background(), testReposDir+"repo1_hook_verification") + gitRepo, err := git.OpenRepository(t.Context(), testReposDir+"repo1_hook_verification") defer gitRepo.Close() require.NoError(t, err) diff --git a/services/actions/context_test.go b/services/actions/context_test.go index a80d2d84e3..4cd8825870 100644 --- a/services/actions/context_test.go +++ b/services/actions/context_test.go @@ -4,7 +4,6 @@ package actions import ( - "context" "testing" actions_model "code.gitea.io/gitea/models/actions" @@ -20,7 +19,7 @@ func TestFindTaskNeeds(t *testing.T) { task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 51}) job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: task.JobID}) - ret, err := FindTaskNeeds(context.Background(), job) + ret, err := FindTaskNeeds(t.Context(), job) require.NoError(t, err) assert.Len(t, ret, 1) assert.Contains(t, ret, "job1") diff --git a/services/auth/oauth2_test.go b/services/auth/oauth2_test.go index c9b4ed06cc..90e2fe4517 100644 --- a/services/auth/oauth2_test.go +++ b/services/auth/oauth2_test.go @@ -4,7 +4,6 @@ package auth import ( - "context" "testing" "code.gitea.io/gitea/models/unittest" @@ -27,7 +26,7 @@ func TestUserIDFromToken(t *testing.T) { ds := make(middleware.ContextData) o := OAuth2{} - uid := o.userIDFromToken(context.Background(), token, ds) + uid := o.userIDFromToken(t.Context(), token, ds) assert.Equal(t, int64(user_model.ActionsUserID), uid) assert.Equal(t, true, ds["IsActionsToken"]) assert.Equal(t, ds["ActionsTaskID"], int64(RunningTaskID)) @@ -48,7 +47,7 @@ func TestCheckTaskIsRunning(t *testing.T) { for name := range cases { c := cases[name] t.Run(name, func(t *testing.T) { - actual := CheckTaskIsRunning(context.Background(), c.TaskID) + actual := CheckTaskIsRunning(t.Context(), c.TaskID) assert.Equal(t, c.Expected, actual) }) } diff --git a/services/mailer/mail_admin_new_user_test.go b/services/mailer/mail_admin_new_user_test.go index f7f27832f9..765c8cb6c9 100644 --- a/services/mailer/mail_admin_new_user_test.go +++ b/services/mailer/mail_admin_new_user_test.go @@ -45,7 +45,7 @@ func cleanUpUsers(ctx context.Context, users []*user_model.User) { } func TestAdminNotificationMail_test(t *testing.T) { - ctx := context.Background() + ctx := t.Context() users := getTestUsers(t) diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index b2768a2bad..43e5d83890 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -79,7 +79,7 @@ func TestComposeIssueCommentMessage(t *testing.T) { recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}} msgs, err := composeIssueCommentMessages(&mailCommentContext{ - Context: context.TODO(), // TODO: use a correct context + Context: t.Context(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue, Content: fmt.Sprintf("test @%s %s#%d body", doer.Name, issue.Repo.FullName(), issue.Index), Comment: comment, @@ -123,7 +123,7 @@ func TestComposeIssueMessage(t *testing.T) { recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}} msgs, err := composeIssueCommentMessages(&mailCommentContext{ - Context: context.TODO(), // TODO: use a correct context + Context: t.Context(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue, Content: "test body", }, "en-US", recipients, false, "issue create") @@ -168,7 +168,7 @@ func TestMailerIssueTemplate(t *testing.T) { t.Helper() recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}} - ctx.Context = context.Background() + ctx.Context = t.Context() fromMention := false msgs, err := composeIssueCommentMessages(ctx, "en-US", recipients, fromMention, "TestMailerIssueTemplate") require.NoError(t, err) @@ -266,14 +266,14 @@ func TestTemplateSelection(t *testing.T) { } msg := testComposeIssueCommentMessage(t, &mailCommentContext{ - Context: context.TODO(), // TODO: use a correct context + Context: t.Context(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue, Content: "test body", }, recipients, false, "TestTemplateSelection") expect(t, msg, "issue/new/subject", "issue/new/body") msg = testComposeIssueCommentMessage(t, &mailCommentContext{ - Context: context.TODO(), // TODO: use a correct context + Context: t.Context(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue, Content: "test body", Comment: comment, }, recipients, false, "TestTemplateSelection") @@ -282,14 +282,14 @@ func TestTemplateSelection(t *testing.T) { pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer}) comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull}) msg = testComposeIssueCommentMessage(t, &mailCommentContext{ - Context: context.TODO(), // TODO: use a correct context + Context: t.Context(), // TODO: use a correct context Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull, Content: "test body", Comment: comment, }, recipients, false, "TestTemplateSelection") expect(t, msg, "pull/comment/subject", "pull/comment/body") msg = testComposeIssueCommentMessage(t, &mailCommentContext{ - Context: context.TODO(), // TODO: use a correct context + Context: t.Context(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue, Content: "test body", Comment: comment, }, recipients, false, "TestTemplateSelection") @@ -309,7 +309,7 @@ func TestTemplateServices(t *testing.T) { recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}} msg := testComposeIssueCommentMessage(t, &mailCommentContext{ - Context: context.TODO(), // TODO: use a correct context + Context: t.Context(), // TODO: use a correct context Issue: issue, Doer: doer, ActionType: actionType, Content: "test body", Comment: comment, }, recipients, fromMention, "TestTemplateServices") @@ -353,7 +353,7 @@ func TestGenerateAdditionalHeaders(t *testing.T) { defer MockMailSettings(nil)() doer, _, issue, _ := prepareMailerTest(t) - ctx := &mailCommentContext{Context: context.TODO() /* TODO: use a correct context */, Issue: issue, Doer: doer} + ctx := &mailCommentContext{Context: t.Context() /* TODO: use a correct context */, Issue: issue, Doer: doer} recipient := &user_model.User{Name: "test", Email: "test@gitea.com"} headers := generateAdditionalHeaders(ctx, "dummy-reason", recipient) diff --git a/services/markup/processorhelper_test.go b/services/markup/processorhelper_test.go index fafde746d2..4d103048b5 100644 --- a/services/markup/processorhelper_test.go +++ b/services/markup/processorhelper_test.go @@ -4,7 +4,6 @@ package markup import ( - "context" "net/http" "net/http/httptest" "testing" @@ -33,10 +32,10 @@ func TestProcessorHelper(t *testing.T) { unittest.AssertCount(t, &user.User{Name: userNoSuch}, 0) // when using general context, use user's visibility to check - assert.True(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userPublic)) - assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userLimited)) - assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userPrivate)) - assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch)) + assert.True(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userPublic)) + assert.False(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userLimited)) + assert.False(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userPrivate)) + assert.False(t, ProcessorHelper().IsUsernameMentionable(t.Context(), userNoSuch)) // when using web context, use user.IsUserVisibleToViewer to check req, err := http.NewRequest("GET", "/", nil) diff --git a/services/migrations/codebase_test.go b/services/migrations/codebase_test.go index 23626d16d7..fbd4e70143 100644 --- a/services/migrations/codebase_test.go +++ b/services/migrations/codebase_test.go @@ -4,7 +4,6 @@ package migrations import ( - "context" "net/url" "os" "testing" @@ -33,7 +32,7 @@ func TestCodebaseDownloadRepo(t *testing.T) { } factory := &CodebaseDownloaderFactory{} - downloader, err := factory.New(context.Background(), base.MigrateOptions{ + downloader, err := factory.New(t.Context(), base.MigrateOptions{ CloneAddr: u.String(), AuthUsername: apiUser, AuthPassword: apiPassword, diff --git a/services/migrations/gitea_downloader_test.go b/services/migrations/gitea_downloader_test.go index 48306d7996..b9ddb9b431 100644 --- a/services/migrations/gitea_downloader_test.go +++ b/services/migrations/gitea_downloader_test.go @@ -4,7 +4,6 @@ package migrations import ( - "context" "os" "sort" "testing" @@ -24,7 +23,7 @@ func TestGiteaDownloadRepo(t *testing.T) { server := unittest.NewMockWebServer(t, "https://gitea.com", fixturePath, giteaToken != "") defer server.Close() - downloader, err := NewGiteaDownloader(context.Background(), server.URL, "gitea/test_repo", "", "", giteaToken) + downloader, err := NewGiteaDownloader(t.Context(), server.URL, "gitea/test_repo", "", "", giteaToken) if downloader == nil { t.Fatal("NewGitlabDownloader is nil") } diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index ad193b2253..e01f4664ba 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -5,7 +5,6 @@ package migrations import ( - "context" "fmt" "os" "path/filepath" @@ -40,7 +39,7 @@ func TestGiteaUploadRepo(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) var ( - ctx = context.Background() + ctx = t.Context() downloader = NewGithubDownloaderV3(ctx, "https://github.com", "", "", "", "go-xorm", "builder") repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05") uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName) @@ -133,7 +132,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) repoName := "migrated" - uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName) + uploader := NewGiteaLocalUploader(t.Context(), doer, doer.Name, repoName) // call remapLocalUser uploader.sameApp = true @@ -182,7 +181,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) { doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) repoName := "migrated" - uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName) + uploader := NewGiteaLocalUploader(t.Context(), doer, doer.Name, repoName) uploader.gitServiceType = structs.GiteaService // call remapExternalUser uploader.sameApp = false @@ -301,7 +300,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) { require.NoError(t, err) toRepoName := "migrated" - uploader := NewGiteaLocalUploader(context.Background(), fromRepoOwner, fromRepoOwner.Name, toRepoName) + uploader := NewGiteaLocalUploader(t.Context(), fromRepoOwner, fromRepoOwner.Name, toRepoName) uploader.gitServiceType = structs.GiteaService require.NoError(t, uploader.CreateRepo(&base.Repository{ Description: "description", diff --git a/services/migrations/github_test.go b/services/migrations/github_test.go index 9270d5d4c4..080fd497ca 100644 --- a/services/migrations/github_test.go +++ b/services/migrations/github_test.go @@ -5,7 +5,6 @@ package migrations import ( - "context" "os" "testing" "time" @@ -25,7 +24,7 @@ func TestGitHubDownloadRepo(t *testing.T) { server := unittest.NewMockWebServer(t, "https://api.github.com", fixturePath, token != "") defer server.Close() - downloader := NewGithubDownloaderV3(context.Background(), server.URL, "", "", token, "go-gitea", "test_repo") + downloader := NewGithubDownloaderV3(t.Context(), server.URL, "", "", token, "go-gitea", "test_repo") err := downloader.RefreshRate() require.NoError(t, err) diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index 8ae8fa9a23..78f2a59119 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -21,7 +21,7 @@ import ( base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/structs" - "gitlab.com/gitlab-org/api/client-go" + gitlab "gitlab.com/gitlab-org/api/client-go" ) var ( diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go index 9344311656..f1404d946d 100644 --- a/services/migrations/gitlab_test.go +++ b/services/migrations/gitlab_test.go @@ -4,7 +4,6 @@ package migrations import ( - "context" "fmt" "net/http" "net/http/httptest" @@ -19,7 +18,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gitlab.com/gitlab-org/api/client-go" + gitlab "gitlab.com/gitlab-org/api/client-go" ) func TestGitlabDownloadRepo(t *testing.T) { @@ -31,7 +30,7 @@ func TestGitlabDownloadRepo(t *testing.T) { server := unittest.NewMockWebServer(t, "https://gitlab.com", fixturePath, gitlabPersonalAccessToken != "") defer server.Close() - downloader, err := NewGitlabDownloader(context.Background(), server.URL, "forgejo/test_repo", "", "", gitlabPersonalAccessToken) + downloader, err := NewGitlabDownloader(t.Context(), server.URL, "forgejo/test_repo", "", "", gitlabPersonalAccessToken) if err != nil { t.Fatalf("NewGitlabDownloader is nil: %v", err) } @@ -331,7 +330,7 @@ func TestGitlabSkippedIssueNumber(t *testing.T) { server := unittest.NewMockWebServer(t, "https://gitlab.com", fixturePath, gitlabPersonalAccessToken != "") defer server.Close() - downloader, err := NewGitlabDownloader(context.Background(), server.URL, "troyengel/archbuild", "", "", gitlabPersonalAccessToken) + downloader, err := NewGitlabDownloader(t.Context(), server.URL, "troyengel/archbuild", "", "", gitlabPersonalAccessToken) if err != nil { t.Fatalf("NewGitlabDownloader is nil: %v", err) } @@ -454,7 +453,7 @@ func TestGitlabGetReviews(t *testing.T) { repoID := 1324 downloader := &GitlabDownloader{ - ctx: context.Background(), + ctx: t.Context(), client: client, repoID: repoID, } diff --git a/services/migrations/gogs_test.go b/services/migrations/gogs_test.go index 6c511a2bb5..450aeab5ef 100644 --- a/services/migrations/gogs_test.go +++ b/services/migrations/gogs_test.go @@ -4,7 +4,6 @@ package migrations import ( - "context" "net/http" "os" "testing" @@ -30,7 +29,7 @@ func TestGogsDownloadRepo(t *testing.T) { return } - downloader := NewGogsDownloader(context.Background(), "https://try.gogs.io", "", "", gogsPersonalAccessToken, "lunnytest", "TESTREPO") + downloader := NewGogsDownloader(t.Context(), "https://try.gogs.io", "", "", gogsPersonalAccessToken, "lunnytest", "TESTREPO") repo, err := downloader.GetRepoInfo() require.NoError(t, err) @@ -207,7 +206,7 @@ func TestGogsDownloaderFactory_New(t *testing.T) { AuthPassword: tt.args.AuthPassword, AuthToken: tt.args.AuthToken, } - got, err := f.New(context.Background(), opts) + got, err := f.New(t.Context(), opts) if (err != nil) != tt.wantErr { t.Errorf("GogsDownloaderFactory.New() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/services/migrations/onedev_test.go b/services/migrations/onedev_test.go index 80c26130cc..46e3eb8d18 100644 --- a/services/migrations/onedev_test.go +++ b/services/migrations/onedev_test.go @@ -4,7 +4,6 @@ package migrations import ( - "context" "net/http" "net/url" "testing" @@ -23,7 +22,7 @@ func TestOneDevDownloadRepo(t *testing.T) { } u, _ := url.Parse("https://code.onedev.io") - downloader := NewOneDevDownloader(context.Background(), u, "", "", "go-gitea-test_repo") + downloader := NewOneDevDownloader(t.Context(), u, "", "", "go-gitea-test_repo") if err != nil { t.Fatalf("NewOneDevDownloader is nil: %v", err) } diff --git a/services/pull/check_test.go b/services/pull/check_test.go index b99cf01ee1..dfb8ff708b 100644 --- a/services/pull/check_test.go +++ b/services/pull/check_test.go @@ -5,7 +5,6 @@ package pull import ( - "context" "strconv" "testing" "time" @@ -34,7 +33,7 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) { cfg, err := setting.GetQueueSettings(setting.CfgProvider, "pr_patch_checker") require.NoError(t, err) - prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(context.Background(), "pr_patch_checker", cfg, testHandler, true) + prPatchCheckerQueue, err = queue.NewWorkerPoolQueueWithContext(t.Context(), "pr_patch_checker", cfg, testHandler, true) require.NoError(t, err) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) diff --git a/services/repository/lfs_test.go b/services/repository/lfs_test.go index a0c01dff8f..838386d845 100644 --- a/services/repository/lfs_test.go +++ b/services/repository/lfs_test.go @@ -5,7 +5,6 @@ package repository_test import ( "bytes" - "context" "testing" "time" @@ -41,7 +40,7 @@ func TestGarbageCollectLFSMetaObjects(t *testing.T) { lfsOid := storeObjectInRepo(t, repo.ID, &lfsContent) // gc - err = repo_service.GarbageCollectLFSMetaObjects(context.Background(), repo_service.GarbageCollectLFSMetaObjectsOptions{ + err = repo_service.GarbageCollectLFSMetaObjects(t.Context(), repo_service.GarbageCollectLFSMetaObjectsOptions{ AutoFix: true, OlderThan: time.Now().Add(7 * 24 * time.Hour).Add(5 * 24 * time.Hour), UpdatedLessRecentlyThan: time.Time{}, // ensure that the models/fixtures/lfs_meta_object.yml objects are considered as well diff --git a/services/webhook/default_test.go b/services/webhook/default_test.go index f3e2848659..7056e77b47 100644 --- a/services/webhook/default_test.go +++ b/services/webhook/default_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -45,7 +44,7 @@ func TestGiteaPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + req, reqBody, err := dh.NewRequest(t.Context(), hook, task) require.NoError(t, err) require.NotNil(t, req) require.NotNil(t, reqBody) @@ -74,7 +73,7 @@ func TestGiteaPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + req, reqBody, err := dh.NewRequest(t.Context(), hook, task) require.NoError(t, err) require.NotNil(t, req) require.NotNil(t, reqBody) @@ -103,7 +102,7 @@ func TestGiteaPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + req, reqBody, err := dh.NewRequest(t.Context(), hook, task) require.NoError(t, err) require.NotNil(t, req) require.NotNil(t, reqBody) @@ -148,7 +147,7 @@ func TestForgejoPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + req, reqBody, err := dh.NewRequest(t.Context(), hook, task) require.NoError(t, err) require.NotNil(t, req) require.NotNil(t, reqBody) @@ -177,7 +176,7 @@ func TestForgejoPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + req, reqBody, err := dh.NewRequest(t.Context(), hook, task) require.NoError(t, err) require.NotNil(t, req) require.NotNil(t, reqBody) @@ -206,7 +205,7 @@ func TestForgejoPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := dh.NewRequest(context.Background(), hook, task) + req, reqBody, err := dh.NewRequest(t.Context(), hook, task) require.NoError(t, err) require.NotNil(t, req) require.NotNil(t, reqBody) diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index 17b7bf7f3e..c6d1cb60dc 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -4,12 +4,10 @@ package webhook import ( - "context" "io" "net/http" "net/http/httptest" "net/url" - "os" "strings" "testing" "time" @@ -19,6 +17,7 @@ import ( webhook_model "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/hostmatcher" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" webhook_module "code.gitea.io/gitea/modules/webhook" "github.com/stretchr/testify/assert" @@ -26,16 +25,9 @@ import ( ) func TestWebhookProxy(t *testing.T) { - oldWebhook := setting.Webhook - oldHTTPProxy := os.Getenv("http_proxy") - oldHTTPSProxy := os.Getenv("https_proxy") - t.Cleanup(func() { - setting.Webhook = oldWebhook - os.Setenv("http_proxy", oldHTTPProxy) - os.Setenv("https_proxy", oldHTTPSProxy) - }) - os.Unsetenv("http_proxy") - os.Unsetenv("https_proxy") + defer test.MockProtect(&setting.Webhook)() + t.Setenv("http_proxy", "") + t.Setenv("https_proxy", "") setting.Webhook.ProxyURL = "http://localhost:8080" setting.Webhook.ProxyURLFixed, _ = url.Parse(setting.Webhook.ProxyURL) @@ -124,7 +116,7 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) { require.NoError(t, err) assert.NotNil(t, hookTask) - require.NoError(t, Deliver(context.Background(), hookTask)) + require.NoError(t, Deliver(t.Context(), hookTask)) select { case <-done: case <-time.After(5 * time.Second): @@ -190,7 +182,7 @@ func TestWebhookDeliverHookTask(t *testing.T) { require.NoError(t, err) assert.NotNil(t, hookTask) - require.NoError(t, Deliver(context.Background(), hookTask)) + require.NoError(t, Deliver(t.Context(), hookTask)) select { case <-done: case <-time.After(5 * time.Second): @@ -216,7 +208,7 @@ func TestWebhookDeliverHookTask(t *testing.T) { require.NoError(t, err) assert.NotNil(t, hookTask) - require.NoError(t, Deliver(context.Background(), hookTask)) + require.NoError(t, Deliver(t.Context(), hookTask)) select { case <-done: case <-time.After(5 * time.Second): @@ -317,7 +309,7 @@ func TestWebhookDeliverSpecificTypes(t *testing.T) { require.NoError(t, err) assert.NotNil(t, hookTask) - require.NoError(t, Deliver(context.Background(), hookTask)) + require.NoError(t, Deliver(t.Context(), hookTask)) select { case gotBody := <-hc.gotBody: assert.NotEqual(t, string(data), string(gotBody), "request body must be different from the event payload") diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go index d0a2d48908..762d29dddc 100644 --- a/services/webhook/dingtalk_test.go +++ b/services/webhook/dingtalk_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "net/url" "testing" @@ -236,7 +235,7 @@ func TestDingTalkJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := dingtalkHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := dingtalkHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go index 4edd06bd76..e0bb2225f7 100644 --- a/services/webhook/discord_test.go +++ b/services/webhook/discord_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -346,7 +345,7 @@ func TestDiscordJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := discordHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := discordHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go index 9744571b39..614e0f1ef4 100644 --- a/services/webhook/feishu_test.go +++ b/services/webhook/feishu_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -177,7 +176,7 @@ func TestFeishuJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := feishuHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := feishuHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index a941486f9f..46e0041a34 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -211,7 +210,7 @@ func TestMatrixJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := matrixHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := matrixHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go index a97e9f3de3..d9a9724e5b 100644 --- a/services/webhook/msteams_test.go +++ b/services/webhook/msteams_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -439,7 +438,7 @@ func TestMSTeamsJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := msteamsHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := msteamsHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go index 320c1c85a1..0f696f1b99 100644 --- a/services/webhook/packagist_test.go +++ b/services/webhook/packagist_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "fmt" "testing" @@ -53,7 +52,7 @@ func TestPackagistPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := packagistHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := packagistHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go index 155b091f9e..ecc11d541f 100644 --- a/services/webhook/slack_test.go +++ b/services/webhook/slack_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -178,7 +177,7 @@ func TestSlackJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := slackHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := slackHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/services/webhook/sourcehut/builds_test.go b/services/webhook/sourcehut/builds_test.go index 5b16f08c09..689c369a7f 100644 --- a/services/webhook/sourcehut/builds_test.go +++ b/services/webhook/sourcehut/builds_test.go @@ -4,7 +4,6 @@ package sourcehut import ( - "context" "strings" "testing" @@ -26,7 +25,7 @@ func gitInit(t testing.TB) { return } t.Cleanup(test.MockVariableValue(&setting.Git.HomePath, t.TempDir())) - require.NoError(t, git.InitSimple(context.Background())) + require.NoError(t, git.InitSimple(t.Context())) } func TestSourcehutBuildsPayload(t *testing.T) { @@ -372,7 +371,7 @@ func TestSourcehutJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := BuildsHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := BuildsHandler{}.NewRequest(t.Context(), hook, task) require.NoError(t, err) require.NotNil(t, req) require.NotNil(t, reqBody) diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go index 4f5663da41..85a62f7615 100644 --- a/services/webhook/telegram_test.go +++ b/services/webhook/telegram_test.go @@ -4,7 +4,6 @@ package webhook import ( - "context" "testing" webhook_model "code.gitea.io/gitea/models/webhook" @@ -194,7 +193,7 @@ func TestTelegramJSONPayload(t *testing.T) { PayloadVersion: 2, } - req, reqBody, err := telegramHandler{}.NewRequest(context.Background(), hook, task) + req, reqBody, err := telegramHandler{}.NewRequest(t.Context(), hook, task) require.NotNil(t, req) require.NotNil(t, reqBody) require.NoError(t, err) diff --git a/tests/e2e/utils_e2e_test.go b/tests/e2e/utils_e2e_test.go index 96fd905363..b96d61fd2c 100644 --- a/tests/e2e/utils_e2e_test.go +++ b/tests/e2e/utils_e2e_test.go @@ -60,7 +60,7 @@ func onForgejoRunTB(t testing.TB, callback func(testing.TB, *url.URL), prepare . defer func() { require.NoError(t, os.WriteFile(setting.CustomConf, conf, 0o644)) - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) + ctx, cancel := context.WithTimeout(t.Context(), 2*time.Minute) s.Shutdown(ctx) cancel() }() diff --git a/tests/integration/actions_job_test.go b/tests/integration/actions_job_test.go index 545ab37bf6..a2ccbf0e3d 100644 --- a/tests/integration/actions_job_test.go +++ b/tests/integration/actions_job_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "encoding/base64" "fmt" "net/http" @@ -416,7 +415,7 @@ jobs: actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: task.Id}) actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID}) actionRun := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID}) - require.NoError(t, actionRun.LoadAttributes(context.Background())) + require.NoError(t, actionRun.LoadAttributes(t.Context())) assert.Equal(t, user2.Name, gtCtx["actor"].GetStringValue()) assert.Equal(t, setting.AppURL+"api/v1", gtCtx["api_url"].GetStringValue()) diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go index 10618c89c7..daeae30b38 100644 --- a/tests/integration/actions_route_test.go +++ b/tests/integration/actions_route_test.go @@ -5,7 +5,6 @@ package integration import ( - "context" "fmt" "net/http" "net/url" @@ -71,12 +70,12 @@ func TestActionsWebRouteLatestWorkflowRun(t *testing.T) { // Verify that each points to the correct workflow. workflowOne := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID, Index: 1}) - err := workflowOne.LoadAttributes(context.Background()) + err := workflowOne.LoadAttributes(t.Context()) require.NoError(t, err) assert.Equal(t, workflowOneURI, workflowOne.HTMLURL()) workflowTwo := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID, Index: 2}) - err = workflowTwo.LoadAttributes(context.Background()) + err = workflowTwo.LoadAttributes(t.Context()) require.NoError(t, err) assert.Equal(t, workflowTwoURI, workflowTwo.HTMLURL()) }) @@ -141,7 +140,7 @@ func TestActionsWebRouteLatestRun(t *testing.T) { // Verify that it redirects to the run we just created workflow := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID}) - err := workflow.LoadAttributes(context.Background()) + err := workflow.LoadAttributes(t.Context()) require.NoError(t, err) assert.Equal(t, workflow.HTMLURL(), resp.Header().Get("Location")) @@ -170,7 +169,7 @@ func TestActionsArtifactDeletion(t *testing.T) { // Load the run we just created run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID}) - err := run.LoadAttributes(context.Background()) + err := run.LoadAttributes(t.Context()) require.NoError(t, err) // Visit it's web view diff --git a/tests/integration/actions_runner_test.go b/tests/integration/actions_runner_test.go index 0ffd97a208..0cc4204567 100644 --- a/tests/integration/actions_runner_test.go +++ b/tests/integration/actions_runner_test.go @@ -61,7 +61,7 @@ func newMockRunnerClient(uuid, token string) *mockRunnerClient { } func (r *mockRunner) doPing(t *testing.T) { - resp, err := r.client.pingServiceClient.Ping(context.Background(), connect.NewRequest(&pingv1.PingRequest{ + resp, err := r.client.pingServiceClient.Ping(t.Context(), connect.NewRequest(&pingv1.PingRequest{ Data: "mock-runner", })) require.NoError(t, err) @@ -70,7 +70,7 @@ func (r *mockRunner) doPing(t *testing.T) { func (r *mockRunner) doRegister(t *testing.T, name, token string, labels []string) { r.doPing(t) - resp, err := r.client.runnerServiceClient.Register(context.Background(), connect.NewRequest(&runnerv1.RegisterRequest{ + resp, err := r.client.runnerServiceClient.Register(t.Context(), connect.NewRequest(&runnerv1.RegisterRequest{ Name: name, Token: token, Version: "mock-runner-version", @@ -104,7 +104,7 @@ func (r *mockRunner) fetchTask(t *testing.T, timeout ...time.Duration) *runnerv1 ddl := time.Now().Add(fetchTimeout) var task *runnerv1.Task for time.Now().Before(ddl) { - resp, err := r.client.runnerServiceClient.FetchTask(context.Background(), connect.NewRequest(&runnerv1.FetchTaskRequest{ + resp, err := r.client.runnerServiceClient.FetchTask(t.Context(), connect.NewRequest(&runnerv1.FetchTaskRequest{ TasksVersion: 0, })) require.NoError(t, err) @@ -127,7 +127,7 @@ type mockTaskOutcome struct { func (r *mockRunner) execTask(t *testing.T, task *runnerv1.Task, outcome *mockTaskOutcome) { for idx, lr := range outcome.logRows { - resp, err := r.client.runnerServiceClient.UpdateLog(context.Background(), connect.NewRequest(&runnerv1.UpdateLogRequest{ + resp, err := r.client.runnerServiceClient.UpdateLog(t.Context(), connect.NewRequest(&runnerv1.UpdateLogRequest{ TaskId: task.Id, Index: int64(idx), Rows: []*runnerv1.LogRow{lr}, @@ -138,7 +138,7 @@ func (r *mockRunner) execTask(t *testing.T, task *runnerv1.Task, outcome *mockTa } sentOutputKeys := make([]string, 0, len(outcome.outputs)) for outputKey, outputValue := range outcome.outputs { - resp, err := r.client.runnerServiceClient.UpdateTask(context.Background(), connect.NewRequest(&runnerv1.UpdateTaskRequest{ + resp, err := r.client.runnerServiceClient.UpdateTask(t.Context(), connect.NewRequest(&runnerv1.UpdateTaskRequest{ State: &runnerv1.TaskState{ Id: task.Id, Result: runnerv1.Result_RESULT_UNSPECIFIED, @@ -150,7 +150,7 @@ func (r *mockRunner) execTask(t *testing.T, task *runnerv1.Task, outcome *mockTa assert.ElementsMatch(t, sentOutputKeys, resp.Msg.SentOutputs) } time.Sleep(outcome.execTime) - resp, err := r.client.runnerServiceClient.UpdateTask(context.Background(), connect.NewRequest(&runnerv1.UpdateTaskRequest{ + resp, err := r.client.runnerServiceClient.UpdateTask(t.Context(), connect.NewRequest(&runnerv1.UpdateTaskRequest{ State: &runnerv1.TaskState{ Id: task.Id, Result: outcome.result, diff --git a/tests/integration/admin_user_test.go b/tests/integration/admin_user_test.go index 6e0499d949..b61c50f4a8 100644 --- a/tests/integration/admin_user_test.go +++ b/tests/integration/admin_user_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "fmt" "net/http" "strconv" @@ -131,7 +130,7 @@ func TestSourceId(t *testing.T) { LoginType: auth_model.Plain, LoginSource: 23, } - defer createUser(context.Background(), t, testUser23)() + defer createUser(t.Context(), t, testUser23)() session := loginUser(t, "user1") token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadAdmin) @@ -163,7 +162,7 @@ func TestAdminViewUsersSorted(t *testing.T) { defer tests.PrepareTestEnv(t)() createTimestamp := time.Now().Unix() - 1000 updateTimestamp := time.Now().Unix() - 500 - sess := db.GetEngine(context.Background()) + sess := db.GetEngine(t.Context()) // Create 10 users with login source 44 for i := int64(1); i <= 10; i++ { diff --git a/tests/integration/api_helper_for_declarative_test.go b/tests/integration/api_helper_for_declarative_test.go index dae71ca8ef..4985f45b9c 100644 --- a/tests/integration/api_helper_for_declarative_test.go +++ b/tests/integration/api_helper_for_declarative_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "fmt" "net/http" "net/http/httptest" @@ -286,7 +285,7 @@ func doAPIMergePullRequestForm(t *testing.T, ctx APITestContext, owner, repo str if err.Message != "Please try again later" { break } - queue.GetManager().FlushAll(context.Background(), 5*time.Second) + queue.GetManager().FlushAll(t.Context(), 5*time.Second) <-time.After(1 * time.Second) } diff --git a/tests/integration/api_private_serv_test.go b/tests/integration/api_private_serv_test.go index 3339fc4430..11eb730915 100644 --- a/tests/integration/api_private_serv_test.go +++ b/tests/integration/api_private_serv_test.go @@ -18,7 +18,7 @@ import ( func TestAPIPrivateNoServ(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() key, user, err := private.ServNoCommand(ctx, 1) require.NoError(t, err) @@ -40,7 +40,7 @@ func TestAPIPrivateNoServ(t *testing.T) { func TestAPIPrivateServ(t *testing.T) { onGiteaRun(t, func(*testing.T, *url.URL) { - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() // Can push to a repo we own diff --git a/tests/integration/api_repo_file_create_test.go b/tests/integration/api_repo_file_create_test.go index c7c30db1ff..18aad34c98 100644 --- a/tests/integration/api_repo_file_create_test.go +++ b/tests/integration/api_repo_file_create_test.go @@ -4,7 +4,6 @@ package integration import ( - stdCtx "context" "encoding/base64" "fmt" "net/http" @@ -172,7 +171,7 @@ func TestAPICreateFile(t *testing.T) { req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &createFileOptions). AddTokenAuth(token2) resp := MakeRequest(t, req, http.StatusCreated) - gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), repo1) + gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1) commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) latestCommit, _ := gitRepo.GetCommitByPath(treePath) expectedFileResponse := getExpectedFileResponseForCreate("user2/repo1", commitID, treePath, latestCommit.ID.String()) @@ -292,7 +291,7 @@ func TestAPICreateFile(t *testing.T) { AddTokenAuth(token2) resp = MakeRequest(t, req, http.StatusCreated) emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user2", Name: reponame}) // public repo - gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), emptyRepo) + gitRepo, _ := gitrepo.OpenRepository(t.Context(), emptyRepo) commitID, _ := gitRepo.GetBranchCommitID(createFileOptions.NewBranchName) latestCommit, _ := gitRepo.GetCommitByPath(treePath) expectedFileResponse := getExpectedFileResponseForCreate("user2/"+reponame, commitID, treePath, latestCommit.ID.String()) diff --git a/tests/integration/api_repo_file_update_test.go b/tests/integration/api_repo_file_update_test.go index ac28e0c0a2..c8ce94a3f5 100644 --- a/tests/integration/api_repo_file_update_test.go +++ b/tests/integration/api_repo_file_update_test.go @@ -4,7 +4,6 @@ package integration import ( - stdCtx "context" "encoding/base64" "fmt" "net/http" @@ -135,7 +134,7 @@ func TestAPIUpdateFile(t *testing.T) { req := NewRequestWithJSON(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s", user2.Name, repo1.Name, treePath), &updateFileOptions). AddTokenAuth(token2) resp := MakeRequest(t, req, http.StatusOK) - gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), repo1) + gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1) commitID, _ := gitRepo.GetBranchCommitID(updateFileOptions.NewBranchName) lasCommit, _ := gitRepo.GetCommitByPath(treePath) expectedFileResponse := getExpectedFileResponseForUpdate(commitID, treePath, lasCommit.ID.String()) diff --git a/tests/integration/api_repo_files_change_test.go b/tests/integration/api_repo_files_change_test.go index fb3ae5e4dd..aca58025d2 100644 --- a/tests/integration/api_repo_files_change_test.go +++ b/tests/integration/api_repo_files_change_test.go @@ -4,7 +4,6 @@ package integration import ( - stdCtx "context" "encoding/base64" "fmt" "net/http" @@ -96,7 +95,7 @@ func TestAPIChangeFiles(t *testing.T) { req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/contents", user2.Name, repo1.Name), &changeFilesOptions). AddTokenAuth(token2) resp := MakeRequest(t, req, http.StatusCreated) - gitRepo, _ := gitrepo.OpenRepository(stdCtx.Background(), repo1) + gitRepo, _ := gitrepo.OpenRepository(t.Context(), repo1) commitID, _ := gitRepo.GetBranchCommitID(changeFilesOptions.NewBranchName) createLasCommit, _ := gitRepo.GetCommitByPath(createTreePath) updateLastCommit, _ := gitRepo.GetCommitByPath(updateTreePath) diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go index 638b90a4aa..a1018e45be 100644 --- a/tests/integration/api_wiki_test.go +++ b/tests/integration/api_wiki_test.go @@ -5,7 +5,6 @@ package integration import ( - "context" "encoding/base64" "fmt" "net/http" @@ -320,7 +319,7 @@ func TestAPIEditOtherWikiPage(t *testing.T) { testCreateWiki(http.StatusForbidden) // Update the repo settings for user2's repo to enable globally writeable wiki - ctx := context.Background() + ctx := t.Context() repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) var units []repo_model.RepoUnit units = append(units, repo_model.RepoUnit{ diff --git a/tests/integration/auth_ldap_test.go b/tests/integration/auth_ldap_test.go index e8b38afeb1..42d19341ba 100644 --- a/tests/integration/auth_ldap_test.go +++ b/tests/integration/auth_ldap_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "net/http" "os" "strings" @@ -244,7 +243,7 @@ func TestLDAPUserSync(t *testing.T) { } defer tests.PrepareTestEnv(t)() addAuthSourceLDAP(t, "", "", "", "") - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) // Check if users exists for _, gitLDAPUser := range gitLDAPUsers { @@ -296,7 +295,7 @@ func TestLDAPUserSyncWithEmptyUsernameAttribute(t *testing.T) { MakeRequest(t, req, http.StatusSeeOther) } - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) authSource := unittest.AssertExistsAndLoadBean(t, &auth_model.Source{ Name: payload["name"], @@ -331,7 +330,7 @@ func TestLDAPUserSyncWithGroupFilter(t *testing.T) { u := otherLDAPUsers[0] testLoginFailed(t, u.UserName, u.Password, translation.NewLocale("en-US").TrString("form.username_password_incorrect")) - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) // Assert members of LDAP group "cn=git" are added for _, gitLDAPUser := range gitLDAPUsers { @@ -354,7 +353,7 @@ func TestLDAPUserSyncWithGroupFilter(t *testing.T) { ldapConfig.GroupFilter = "(cn=ship_crew)" auth_model.UpdateSource(db.DefaultContext, ldapSource) - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) for _, gitLDAPUser := range gitLDAPUsers { if gitLDAPUser.UserName == "fry" || gitLDAPUser.UserName == "leela" || gitLDAPUser.UserName == "bender" { @@ -393,7 +392,7 @@ func TestLDAPUserSSHKeySync(t *testing.T) { defer tests.PrepareTestEnv(t)() addAuthSourceLDAP(t, "sshPublicKey", "", "", "") - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) // Check if users has SSH keys synced for _, u := range gitLDAPUsers { @@ -429,7 +428,7 @@ func TestLDAPGroupTeamSyncAddMember(t *testing.T) { require.NoError(t, err) team, err := organization.GetTeam(db.DefaultContext, org.ID, "team11") require.NoError(t, err) - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) for _, gitLDAPUser := range gitLDAPUsers { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ Name: gitLDAPUser.UserName, @@ -518,7 +517,7 @@ func TestLDAPUserSyncInvalidMail(t *testing.T) { } defer tests.PrepareTestEnv(t)() addAuthSourceLDAP(t, "", "nonexisting", "", "") - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) // Check if users exists for _, gitLDAPUser := range gitLDAPUsers { @@ -544,7 +543,7 @@ func TestLDAPUserSyncInvalidMailDefaultDomain(t *testing.T) { } defer tests.PrepareTestEnv(t)() addAuthSourceLDAP(t, "", "nonexisting", "test.org", "") - auth.SyncExternalUsers(context.Background(), true) + auth.SyncExternalUsers(t.Context(), true) // Check if users exists for _, gitLDAPUser := range gitLDAPUsers { diff --git a/tests/integration/cmd_forgejo_actions_test.go b/tests/integration/cmd_forgejo_actions_test.go index bda69edb80..a458a6437d 100644 --- a/tests/integration/cmd_forgejo_actions_test.go +++ b/tests/integration/cmd_forgejo_actions_test.go @@ -3,7 +3,6 @@ package integration import ( - gocontext "context" "io" "net/url" "os" @@ -189,7 +188,7 @@ func Test_CmdForgejo_Actions(t *testing.T) { require.NoError(t, err) if assert.EqualValues(t, testCase.uuid, uuid) { ownerName, repoName, found := strings.Cut(testCase.scope, "/") - action, err := actions_model.GetRunnerByUUID(gocontext.Background(), uuid) + action, err := actions_model.GetRunnerByUUID(t.Context(), uuid) require.NoError(t, err) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: action.OwnerID}) diff --git a/tests/integration/cmd_forgejo_f3_test.go b/tests/integration/cmd_forgejo_f3_test.go index 9156405220..6c34a71c71 100644 --- a/tests/integration/cmd_forgejo_f3_test.go +++ b/tests/integration/cmd_forgejo_f3_test.go @@ -59,7 +59,7 @@ func TestF3_CmdMirror_LocalForgejo(t *testing.T) { defer tests.PrepareTestEnv(t)() defer test.MockVariableValue(&setting.F3.Enabled, true)() - ctx := context.Background() + ctx := t.Context() mirrorOptions := f3_tests_forge.GetFactory(options.Name)().NewOptions(t) mirrorTree := f3_generic.GetFactory("f3")(ctx, mirrorOptions) diff --git a/tests/integration/codeowner_test.go b/tests/integration/codeowner_test.go index e8200219c4..e1df15426d 100644 --- a/tests/integration/codeowner_test.go +++ b/tests/integration/codeowner_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "fmt" "net/http" "net/url" @@ -48,7 +47,7 @@ func TestCodeOwner(t *testing.T) { r := fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name) cloneURL, _ := url.Parse(r) cloneURL.User = url.UserPassword("user2", userPassword) - require.NoError(t, git.CloneWithArgs(context.Background(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) t.Run("Normal", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/dump_restore_test.go b/tests/integration/dump_restore_test.go index fa65695150..e794c3fb15 100644 --- a/tests/integration/dump_restore_test.go +++ b/tests/integration/dump_restore_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "errors" "fmt" "net/url" @@ -21,7 +20,6 @@ import ( base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/migrations" "github.com/stretchr/testify/assert" @@ -45,9 +43,7 @@ func TestDumpRestore(t *testing.T) { reponame := "repo1" - basePath, err := os.MkdirTemp("", reponame) - require.NoError(t, err) - defer util.RemoveAll(basePath) + basePath := t.TempDir() repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}) repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) @@ -58,7 +54,7 @@ func TestDumpRestore(t *testing.T) { // Phase 1: dump repo1 from the Gitea instance to the filesystem // - ctx := context.Background() + ctx := t.Context() opts := migrations.MigrateOptions{ GitServiceType: structs.GiteaService, Issues: true, @@ -70,7 +66,7 @@ func TestDumpRestore(t *testing.T) { CloneAddr: repo.CloneLink().HTTPS, RepoName: reponame, } - err = migrations.DumpRepository(ctx, basePath, repoOwner.Name, opts) + err := migrations.DumpRepository(ctx, basePath, repoOwner.Name, opts) require.NoError(t, err) // diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go index 83d8177460..d2bd54eecd 100644 --- a/tests/integration/git_helper_for_declarative_test.go +++ b/tests/integration/git_helper_for_declarative_test.go @@ -101,7 +101,7 @@ func onGiteaRun[T testing.TB](t T, callback func(T, *url.URL)) { func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { return func(t *testing.T) { t.Helper() - require.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{})) + require.NoError(t, git.CloneWithArgs(t.Context(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{})) exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md")) require.NoError(t, err) assert.True(t, exist) @@ -111,7 +111,7 @@ func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { func doPartialGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { return func(t *testing.T) { t.Helper() - require.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{ + require.NoError(t, git.CloneWithArgs(t.Context(), git.AllowLFSFiltersArgs(), u.String(), dstLocalPath, git.CloneRepoOptions{ Filter: "blob:none", })) exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md")) diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index 7866d297e2..30bb9b56ae 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -347,7 +347,7 @@ func authSourcePayloadGitHubCustom(name string) map[string]string { } func createRemoteAuthSource(t *testing.T, name, url, matchingSource string) *auth.Source { - require.NoError(t, auth.CreateSource(context.Background(), &auth.Source{ + require.NoError(t, auth.CreateSource(t.Context(), &auth.Source{ Type: auth.Remote, Name: name, IsActive: true, diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 19eddf926f..1209f26f3b 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -5,7 +5,6 @@ package integration import ( - "context" "fmt" "net/http" "net/url" @@ -120,7 +119,7 @@ func TestViewIssuesKeyword(t *testing.T) { RepoID: repo.ID, Index: 1, }) - issues.UpdateIssueIndexer(context.Background(), issue.ID) + issues.UpdateIssueIndexer(t.Context(), issue.ID) time.Sleep(time.Second * 1) const keyword = "first" diff --git a/tests/integration/lfs_view_test.go b/tests/integration/lfs_view_test.go index 06cea0dac2..9b663fa8e7 100644 --- a/tests/integration/lfs_view_test.go +++ b/tests/integration/lfs_view_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "fmt" "net/http" "strings" @@ -158,7 +157,7 @@ func TestLFSLockView(t *testing.T) { defer tests.PrintCurrentTest(t)() // make sure the display names are different, or the test is meaningless - require.NoError(t, repo3.LoadOwner(context.Background())) + require.NoError(t, repo3.LoadOwner(t.Context())) require.NotEqual(t, user2.DisplayName(), repo3.Owner.DisplayName()) req := NewRequest(t, "GET", fmt.Sprintf("/%s/settings/lfs/locks", repo3.FullName())) diff --git a/tests/integration/linguist_test.go b/tests/integration/linguist_test.go index 73423ee6a4..8690401d8a 100644 --- a/tests/integration/linguist_test.go +++ b/tests/integration/linguist_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "net/http" "net/url" "strings" @@ -82,7 +81,7 @@ func TestLinguistSupport(t *testing.T) { err := stats.UpdateRepoIndexer(repo) require.NoError(t, err) - require.NoError(t, queue.GetManager().FlushAll(context.Background(), 10*time.Second)) + require.NoError(t, queue.GetManager().FlushAll(t.Context(), 10*time.Second)) status, err := repo_model.GetIndexerStatus(db.DefaultContext, repo, repo_model.RepoIndexerTypeStats) require.NoError(t, err) diff --git a/tests/integration/migration-test/migration_test.go b/tests/integration/migration-test/migration_test.go index a391296c35..729d8e0dff 100644 --- a/tests/integration/migration-test/migration_test.go +++ b/tests/integration/migration-test/migration_test.go @@ -5,7 +5,6 @@ package migrations import ( "compress/gzip" - "context" "database/sql" "fmt" "io" @@ -87,7 +86,7 @@ func initMigrationTest(t *testing.T) func() { } } - require.NoError(t, git.InitFull(context.Background())) + require.NoError(t, git.InitFull(t.Context())) setting.LoadDBSetting() setting.InitLoggersForTest() return deferFn @@ -279,13 +278,13 @@ func doMigrationTest(t *testing.T, version string) { setting.InitSQLLoggersForCli(log.INFO) - err := db.InitEngineWithMigration(context.Background(), wrappedMigrate) + err := db.InitEngineWithMigration(t.Context(), wrappedMigrate) require.NoError(t, err) currentEngine.Close() beans, _ := db.NamesToBean() - err = db.InitEngineWithMigration(context.Background(), func(x *xorm.Engine) error { + err = db.InitEngineWithMigration(t.Context(), func(x *xorm.Engine) error { currentEngine = x return migrate_base.RecreateTables(beans...)(x) }) @@ -293,7 +292,7 @@ func doMigrationTest(t *testing.T, version string) { currentEngine.Close() // We do this a second time to ensure that there is not a problem with retained indices - err = db.InitEngineWithMigration(context.Background(), func(x *xorm.Engine) error { + err = db.InitEngineWithMigration(t.Context(), func(x *xorm.Engine) error { currentEngine = x return migrate_base.RecreateTables(beans...)(x) }) diff --git a/tests/integration/mirror_pull_test.go b/tests/integration/mirror_pull_test.go index 60fb47e94b..7072f1eb27 100644 --- a/tests/integration/mirror_pull_test.go +++ b/tests/integration/mirror_pull_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "testing" "code.gitea.io/gitea/models/db" @@ -50,7 +49,7 @@ func TestMirrorPull(t *testing.T) { require.NoError(t, err) assert.True(t, mirrorRepo.IsMirror, "expected pull-mirror repo to be marked as a mirror immediately after its creation") - ctx := context.Background() + ctx := t.Context() mirror, err := repo_service.MigrateRepositoryGitData(ctx, user, mirrorRepo, opts, nil) require.NoError(t, err) diff --git a/tests/integration/mirror_push_test.go b/tests/integration/mirror_push_test.go index fa62219707..ae8d289184 100644 --- a/tests/integration/mirror_push_test.go +++ b/tests/integration/mirror_push_test.go @@ -5,7 +5,6 @@ package integration import ( - "context" "fmt" "net" "net/http" @@ -66,7 +65,7 @@ func testMirrorPush(t *testing.T, u *url.URL) { require.NoError(t, err) assert.Len(t, mirrors, 2) - ok := mirror_service.SyncPushMirror(context.Background(), mirrors[0].ID) + ok := mirror_service.SyncPushMirror(t.Context(), mirrors[0].ID) assert.True(t, ok) srcGitRepo, err := gitrepo.OpenRepository(git.DefaultContext, srcRepo) diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index 6b5a1feb16..336d14c418 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -5,7 +5,6 @@ package integration import ( "bytes" - "context" "crypto/sha256" "encoding/base64" "fmt" @@ -518,7 +517,7 @@ func TestSignInOAuthCallbackSignIn(t *testing.T) { LoginSource: gitlab.ID, LoginName: userGitLabUserID, } - defer createUser(context.Background(), t, userGitLab)() + defer createUser(t.Context(), t, userGitLab)() // // A request for user information sent to Goth will return a @@ -556,7 +555,7 @@ func TestSignInOAuthCallbackWithoutPKCEWhenUnsupported(t *testing.T) { LoginSource: gitlab.ID, LoginName: userGitLabUserID, } - defer createUser(context.Background(), t, userGitLab)() + defer createUser(t.Context(), t, userGitLab)() // initial redirection (to generate the code_challenge) session := emptyTestSession(t) @@ -598,7 +597,7 @@ func TestSignInOAuthCallbackPKCE(t *testing.T) { LoginSource: authSource.ID, LoginName: userID, } - defer createUser(context.Background(), t, user)() + defer createUser(t.Context(), t, user)() // initial redirection (to generate the code_challenge) session := emptyTestSession(t) @@ -656,7 +655,7 @@ func TestSignInOAuthCallbackRedirectToEscaping(t *testing.T) { LoginSource: gitlab.ID, LoginName: userGitLabUserID, } - defer createUser(context.Background(), t, userGitLab)() + defer createUser(t.Context(), t, userGitLab)() // // A request for user information sent to Goth will return a @@ -731,7 +730,7 @@ func TestSignInOauthCallbackSyncSSHKeys(t *testing.T) { LoginName: userID, IsActive: true, } - defer createUser(context.Background(), t, user)() + defer createUser(t.Context(), t, user)() for _, tt := range []struct { name string diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index a0ba99f3b9..d583abe226 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -5,7 +5,6 @@ package integration import ( "bytes" - "context" "fmt" "net/http" "net/http/httptest" @@ -303,11 +302,11 @@ func TestCantMergeConflict(t *testing.T) { gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo1) require.NoError(t, err) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT", false) + err = pull.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "CONFLICT", false) require.Error(t, err, "Merge should return an error due to conflict") assert.True(t, models.IsErrMergeConflicts(err), "Merge error is not a conflict error") - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT", false) + err = pull.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleRebase, "", "CONFLICT", false) require.Error(t, err, "Merge should return an error due to conflict") assert.True(t, models.IsErrRebaseConflicts(err), "Merge error is not a conflict error") gitRepo.Close() @@ -402,7 +401,7 @@ func TestCantMergeUnrelated(t *testing.T) { BaseBranch: "base", }) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED", false) + err = pull.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED", false) require.Error(t, err, "Merge should return an error due to unrelated") assert.True(t, models.IsErrMergeUnrelatedHistories(err), "Merge error is not a unrelated histories error") gitRepo.Close() @@ -442,7 +441,7 @@ func TestFastForwardOnlyMerge(t *testing.T) { gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) require.NoError(t, err) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false) + err = pull.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false) require.NoError(t, err) @@ -484,7 +483,7 @@ func TestCantFastForwardOnlyMergeDiverging(t *testing.T) { gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) require.NoError(t, err) - err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false) + err = pull.Merge(t.Context(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false) require.Error(t, err, "Merge should return an error due to being for a diverging branch") assert.True(t, models.IsErrMergeDivergingFastForwardOnly(err), "Merge error is not a diverging fast-forward-only error") @@ -633,7 +632,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) { testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") createPullResp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "Indexer notifier test pull") - require.NoError(t, queue.GetManager().FlushAll(context.Background(), 0)) + require.NoError(t, queue.GetManager().FlushAll(t.Context(), 0)) time.Sleep(time.Second) repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ @@ -672,7 +671,7 @@ func TestPullMergeIndexerNotifier(t *testing.T) { }) assert.True(t, issue.IsClosed) - require.NoError(t, queue.GetManager().FlushAll(context.Background(), 0)) + require.NoError(t, queue.GetManager().FlushAll(t.Context(), 0)) time.Sleep(time.Second) // search issues again @@ -692,7 +691,7 @@ func testResetRepo(t *testing.T, repoPath, branch, commitID string) { require.NoError(t, err) f.Close() - repo, err := git.OpenRepository(context.Background(), repoPath) + repo, err := git.OpenRepository(t.Context(), repoPath) require.NoError(t, err) defer repo.Close() id, err := repo.GetBranchCommitID(branch) diff --git a/tests/integration/pull_request_task_test.go b/tests/integration/pull_request_task_test.go index 4366d97c39..e268cb8cc5 100644 --- a/tests/integration/pull_request_task_test.go +++ b/tests/integration/pull_request_task_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "testing" "time" @@ -100,7 +99,7 @@ func TestPullRequestSynchronized(t *testing.T) { logChecker.Filter("Updating PR").StopMark("TestPullRequest ") defer cleanup() - pull_service.TestPullRequest(context.Background(), owner, repo.ID, testCase.olderThan, "branch2", true, pull.HeadCommitID, pull.HeadCommitID) + pull_service.TestPullRequest(t.Context(), owner, repo.ID, testCase.olderThan, "branch2", true, pull.HeadCommitID, pull.HeadCommitID) logFiltered, logStopped := logChecker.Check(5 * time.Second) assert.True(t, logStopped) assert.Equal(t, testCase.expected, logFiltered[0]) diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 6dd58102eb..63564b5d29 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -5,7 +5,6 @@ package integration import ( - "context" "fmt" "net/http" "net/http/httptest" @@ -179,7 +178,7 @@ func TestPullView_ResolveInvalidatedReviewComment(t *testing.T) { // (to invalidate it properly, one should push a commit which should trigger this logic, // in the meantime, use this quick-and-dirty trick) comment := loadComment(t, commentID) - require.NoError(t, issues_model.UpdateCommentInvalidate(context.Background(), &issues_model.Comment{ + require.NoError(t, issues_model.UpdateCommentInvalidate(t.Context(), &issues_model.Comment{ ID: comment.ID, Invalidated: true, })) @@ -241,7 +240,7 @@ func TestPullView_ResolveInvalidatedReviewComment(t *testing.T) { // (to invalidate it properly, one should push a commit which should trigger this logic, // in the meantime, use this quick-and-dirty trick) comment := loadComment(t, commentID) - require.NoError(t, issues_model.UpdateCommentInvalidate(context.Background(), &issues_model.Comment{ + require.NoError(t, issues_model.UpdateCommentInvalidate(t.Context(), &issues_model.Comment{ ID: comment.ID, Invalidated: true, })) diff --git a/tests/integration/remote_test.go b/tests/integration/remote_test.go index c59b4c7d32..3c322deea1 100644 --- a/tests/integration/remote_test.go +++ b/tests/integration/remote_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "fmt" "net/http" "testing" @@ -48,7 +47,7 @@ func TestRemote_MaybePromoteUserSuccess(t *testing.T) { LoginSource: remote.ID, LoginName: gitlabUserID, } - defer createUser(context.Background(), t, userBeforeSignIn)() + defer createUser(t.Context(), t, userBeforeSignIn)() // // A request for user information sent to Goth will return a @@ -81,7 +80,7 @@ func TestRemote_MaybePromoteUserSuccess(t *testing.T) { func TestRemote_MaybePromoteUserFail(t *testing.T) { defer tests.PrepareTestEnv(t)() - ctx := context.Background() + ctx := t.Context() // // OAuth2 authentication source GitLab // @@ -126,7 +125,7 @@ func TestRemote_MaybePromoteUserFail(t *testing.T) { LoginName: remoteUserID, Email: "some@example.com", } - defer createUser(context.Background(), t, remoteUser)() + defer createUser(t.Context(), t, remoteUser)() promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") require.NoError(t, err) assert.False(t, promoted) @@ -143,7 +142,7 @@ func TestRemote_MaybePromoteUserFail(t *testing.T) { LoginSource: nonexistentloginsource, LoginName: remoteUserID, } - defer createUser(context.Background(), t, remoteUser)() + defer createUser(t.Context(), t, remoteUser)() promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") require.NoError(t, err) assert.False(t, promoted) @@ -159,7 +158,7 @@ func TestRemote_MaybePromoteUserFail(t *testing.T) { LoginSource: gitlabSource.ID, LoginName: remoteUserID, } - defer createUser(context.Background(), t, remoteUser)() + defer createUser(t.Context(), t, remoteUser)() promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") require.NoError(t, err) assert.False(t, promoted) @@ -180,7 +179,7 @@ func TestRemote_MaybePromoteUserFail(t *testing.T) { LoginSource: remoteSource.ID, LoginName: remoteUserID, } - defer createUser(context.Background(), t, remoteUser)() + defer createUser(t.Context(), t, remoteUser)() promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, unrelatedSource, remoteUserID, remoteEmail) require.NoError(t, err) assert.False(t, promoted) @@ -197,7 +196,7 @@ func TestRemote_MaybePromoteUserFail(t *testing.T) { LoginSource: remoteSource.ID, LoginName: remoteUserID, } - defer createUser(context.Background(), t, remoteUser)() + defer createUser(t.Context(), t, remoteUser)() promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, remoteEmail) require.NoError(t, err) assert.True(t, promoted) diff --git a/tests/integration/wiki_test.go b/tests/integration/wiki_test.go index 30170086d2..587b138175 100644 --- a/tests/integration/wiki_test.go +++ b/tests/integration/wiki_test.go @@ -4,7 +4,6 @@ package integration import ( - "context" "fmt" "net/http" "net/url" @@ -42,7 +41,7 @@ func TestRepoCloneWiki(t *testing.T) { u, _ = url.Parse(r) u.User = url.UserPassword("user2", userPassword) t.Run("Clone", func(t *testing.T) { - require.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{})) + require.NoError(t, git.CloneWithArgs(t.Context(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{})) assertFileEqual(t, filepath.Join(dstPath, "Home.md"), []byte("# Home page\n\nThis is the home page!\n")) assertFileExist(t, filepath.Join(dstPath, "Page-With-Image.md")) assertFileExist(t, filepath.Join(dstPath, "Page-With-Spaced-Name.md")) diff --git a/tests/test_utils.go b/tests/test_utils.go index b3c03a30a1..106e74dc89 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -267,9 +267,7 @@ func cancelProcesses(t testing.TB, delay time.Duration) { } func PrepareGitRepoDirectory(t testing.TB) { - var err error - setting.RepoRootPath, err = os.MkdirTemp(t.TempDir(), "forgejo-repo-rooth") - require.NoError(t, err) + setting.RepoRootPath = t.TempDir() require.NoError(t, unittest.CopyDir(preparedDir, setting.RepoRootPath)) } From a4d5e67efed5c0655671bdd668e0dddf2f5abf7b Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 5 Mar 2025 12:03:18 +0000 Subject: [PATCH 229/669] Update module github.com/prometheus/client_golang to v1.21.1 (forgejo) (#7122) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7122 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f9c6a21408..89e5844801 100644 --- a/go.mod +++ b/go.mod @@ -84,7 +84,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 github.com/pquerna/otp v1.4.0 - github.com/prometheus/client_golang v1.21.0 + github.com/prometheus/client_golang v1.21.1 github.com/redis/go-redis/v9 v9.7.0 github.com/robfig/cron/v3 v3.0.1 github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 diff --git a/go.sum b/go.sum index c8a3577a8a..1b459b19d7 100644 --- a/go.sum +++ b/go.sum @@ -1331,8 +1331,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= -github.com/prometheus/client_golang v1.21.0 h1:DIsaGmiaBkSangBgMtWdNfxbMNdku5IK6iNhrEqWvdA= -github.com/prometheus/client_golang v1.21.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= +github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= From 6abe3f7bb84f48ac84d4b4e28d85ddd5ccff6547 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 5 Mar 2025 12:17:48 +0000 Subject: [PATCH 230/669] Update module golang.org/x/net to v0.36.0 (forgejo) (#7126) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7126 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 89e5844801..c6060e0008 100644 --- a/go.mod +++ b/go.mod @@ -103,7 +103,7 @@ require ( go.uber.org/mock v0.4.0 golang.org/x/crypto v0.35.0 golang.org/x/image v0.23.0 - golang.org/x/net v0.35.0 + golang.org/x/net v0.36.0 golang.org/x/oauth2 v0.27.0 golang.org/x/sync v0.11.0 golang.org/x/sys v0.30.0 diff --git a/go.sum b/go.sum index 1b459b19d7..8b9c0d88d5 100644 --- a/go.sum +++ b/go.sum @@ -1640,8 +1640,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= +golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From 01dd431d3290d792145dd5f0e8e164814cf7c37e Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Wed, 5 Mar 2025 14:57:07 +0000 Subject: [PATCH 231/669] chore: Add distant federation server mock (#7115) Encapsulates the federation server counterpart & makes the test more configurable. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7115 Reviewed-by: Gusted Co-authored-by: Michael Jerger Co-committed-by: Michael Jerger --- .../test/distant_federation_server_mock.go | 117 ++++++++++++++++++ .../api_activitypub_repository_test.go | 55 +------- tests/integration/repo_settings_test.go | 78 ++++-------- 3 files changed, 141 insertions(+), 109 deletions(-) create mode 100644 modules/test/distant_federation_server_mock.go diff --git a/modules/test/distant_federation_server_mock.go b/modules/test/distant_federation_server_mock.go new file mode 100644 index 0000000000..fd68c88a40 --- /dev/null +++ b/modules/test/distant_federation_server_mock.go @@ -0,0 +1,117 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package test + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +type FederationServerMockPerson struct { + ID int64 + Name string + PubKey string +} +type FederationServerMockRepository struct { + ID int64 +} +type FederationServerMock struct { + Persons []FederationServerMockPerson + Repositories []FederationServerMockRepository + LastPost string +} + +func NewFederationServerMockPerson(id int64, name string) FederationServerMockPerson { + return FederationServerMockPerson{ + ID: id, + Name: name, + PubKey: `"-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA18H5s7N6ItZUAh9tneII\nIuZdTTa3cZlLa/9ejWAHTkcp3WLW+/zbsumlMrWYfBy2/yTm56qasWt38iY4D6ul\n` + + `CPiwhAqX3REvVq8tM79a2CEqZn9ka6vuXoDgBg/sBf/BUWqf7orkjUXwk/U0Egjf\nk5jcurF4vqf1u+rlAHH37dvSBaDjNj6Qnj4OP12bjfaY/yvs7+jue/eNXFHjzN4E\n` + + `T2H4B/yeKTJ4UuAwTlLaNbZJul2baLlHelJPAsxiYaziVuV5P+IGWckY6RSerRaZ\nAkc4mmGGtjAyfN9aewe+lNVfwS7ElFx546PlLgdQgjmeSwLX8FWxbPE5A/PmaXCs\n` + + `nx+nou+3dD7NluULLtdd7K+2x02trObKXCAzmi5/Dc+yKTzpFqEz+hLNCz7TImP/\ncK//NV9Q+X67J9O27baH9R9ZF4zMw8rv2Pg0WLSw1z7lLXwlgIsDapeMCsrxkVO4\n` + + `LXX5AQ1xQNtlssnVoUBqBrvZsX2jUUKUocvZqMGuE4hfAgMBAAE=\n-----END PUBLIC KEY-----\n"`, + } +} + +func NewFederationServerMockRepository(id int64) FederationServerMockRepository { + return FederationServerMockRepository{ + ID: id, + } +} + +func (p FederationServerMockPerson) marshal(host string) string { + return fmt.Sprintf(`{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],`+ + `"id":"http://%[1]v/api/activitypub/user-id/%[2]v",`+ + `"type":"Person",`+ + `"icon":{"type":"Image","mediaType":"image/png","url":"http://%[1]v/avatars/1bb05d9a5f6675ed0272af9ea193063c"},`+ + `"url":"http://%[1]v/%[2]v",`+ + `"inbox":"http://%[1]v/api/activitypub/user-id/%[2]v/inbox",`+ + `"outbox":"http://%[1]v/api/activitypub/user-id/%[2]v/outbox",`+ + `"preferredUsername":"%[3]v",`+ + `"publicKey":{"id":"http://%[1]v/api/activitypub/user-id/%[2]v#main-key",`+ + `"owner":"http://%[1]v/api/activitypub/user-id/%[2]v",`+ + `"publicKeyPem":%[4]v}}`, host, p.ID, p.Name, p.PubKey) +} + +func NewFederationServerMock() *FederationServerMock { + return &FederationServerMock{ + Persons: []FederationServerMockPerson{ + NewFederationServerMockPerson(15, "stargoose1"), + NewFederationServerMockPerson(30, "stargoose2"), + }, + Repositories: []FederationServerMockRepository{ + NewFederationServerMockRepository(1), + }, + LastPost: "", + } +} + +func (mock *FederationServerMock) DistantServer(t *testing.T) *httptest.Server { + federatedRoutes := http.NewServeMux() + federatedRoutes.HandleFunc("/.well-known/nodeinfo", + func(res http.ResponseWriter, req *http.Request) { + // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo + // TODO: as soon as content-type will become important: content-type: application/json;charset=utf-8 + fmt.Fprintf(res, `{"links":[{"href":"http://%s/api/v1/nodeinfo","rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"}]}`, req.Host) + }) + federatedRoutes.HandleFunc("/api/v1/nodeinfo", + func(res http.ResponseWriter, req *http.Request) { + // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/nodeinfo + fmt.Fprint(res, `{"version":"2.1","software":{"name":"forgejo","version":"1.20.0+dev-3183-g976d79044",`+ + `"repository":"https://codeberg.org/forgejo/forgejo.git","homepage":"https://forgejo.org/"},`+ + `"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},`+ + `"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`) + }) + for _, person := range mock.Persons { + federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/user-id/%v", person.ID), + func(res http.ResponseWriter, req *http.Request) { + // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/2 + fmt.Fprint(res, person.marshal(req.Host)) + }) + } + for _, repository := range mock.Repositories { + federatedRoutes.HandleFunc(fmt.Sprintf("/api/v1/activitypub/repository-id/%v/inbox/", repository.ID), + func(res http.ResponseWriter, req *http.Request) { + if req.Method != "POST" { + t.Errorf("POST expected at: %q", req.URL.EscapedPath()) + } + buf := new(strings.Builder) + _, err := io.Copy(buf, req.Body) + if err != nil { + t.Errorf("Error reading body: %q", err) + } + mock.LastPost = buf.String() + }) + } + federatedRoutes.HandleFunc("/", + func(res http.ResponseWriter, req *http.Request) { + t.Errorf("Unhandled request: %q", req.URL.EscapedPath()) + }) + federatedSrv := httptest.NewServer(federatedRoutes) + return federatedSrv +} diff --git a/tests/integration/api_activitypub_repository_test.go b/tests/integration/api_activitypub_repository_test.go index 2ca7d42e2a..a86e32b278 100644 --- a/tests/integration/api_activitypub_repository_test.go +++ b/tests/integration/api_activitypub_repository_test.go @@ -1,4 +1,4 @@ -// Copyright 2024 The Forgejo Authors. All rights reserved. +// Copyright 2024, 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration @@ -6,7 +6,6 @@ package integration import ( "fmt" "net/http" - "net/http/httptest" "net/url" "testing" "time" @@ -58,56 +57,8 @@ func TestActivityPubRepositoryInboxValid(t *testing.T) { defer test.MockVariableValue(&setting.Federation.Enabled, true)() defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() - federatedRoutes := http.NewServeMux() - federatedRoutes.HandleFunc("/.well-known/nodeinfo", - func(res http.ResponseWriter, req *http.Request) { - // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo - // TODO: as soon as content-type will become important: content-type: application/json;charset=utf-8 - fmt.Fprintf(res, `{"links":[{"href":"http://%s/api/v1/nodeinfo","rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"}]}`, req.Host) - }) - federatedRoutes.HandleFunc("/api/v1/nodeinfo", - func(res http.ResponseWriter, req *http.Request) { - // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/nodeinfo - fmt.Fprint(res, `{"version":"2.1","software":{"name":"forgejo","version":"1.20.0+dev-3183-g976d79044",`+ - `"repository":"https://codeberg.org/forgejo/forgejo.git","homepage":"https://forgejo.org/"},`+ - `"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},`+ - `"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`) - }) - federatedRoutes.HandleFunc("/api/v1/activitypub/user-id/15", - func(res http.ResponseWriter, req *http.Request) { - // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/2 - fmt.Fprint(res, `{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],`+ - `"id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/15","type":"Person",`+ - `"icon":{"type":"Image","mediaType":"image/png","url":"https://federated-repo.prod.meissa.de/avatars/1bb05d9a5f6675ed0272af9ea193063c"},`+ - `"url":"https://federated-repo.prod.meissa.de/stargoose1","inbox":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/15/inbox",`+ - `"outbox":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/15/outbox","preferredUsername":"stargoose1",`+ - `"publicKey":{"id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/15#main-key","owner":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/15",`+ - `"publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA18H5s7N6ItZUAh9tneII\nIuZdTTa3cZlLa/9ejWAHTkcp3WLW+/zbsumlMrWYfBy2/yTm56qasWt38iY4D6ul\n`+ - `CPiwhAqX3REvVq8tM79a2CEqZn9ka6vuXoDgBg/sBf/BUWqf7orkjUXwk/U0Egjf\nk5jcurF4vqf1u+rlAHH37dvSBaDjNj6Qnj4OP12bjfaY/yvs7+jue/eNXFHjzN4E\n`+ - `T2H4B/yeKTJ4UuAwTlLaNbZJul2baLlHelJPAsxiYaziVuV5P+IGWckY6RSerRaZ\nAkc4mmGGtjAyfN9aewe+lNVfwS7ElFx546PlLgdQgjmeSwLX8FWxbPE5A/PmaXCs\n`+ - `nx+nou+3dD7NluULLtdd7K+2x02trObKXCAzmi5/Dc+yKTzpFqEz+hLNCz7TImP/\ncK//NV9Q+X67J9O27baH9R9ZF4zMw8rv2Pg0WLSw1z7lLXwlgIsDapeMCsrxkVO4\n`+ - `LXX5AQ1xQNtlssnVoUBqBrvZsX2jUUKUocvZqMGuE4hfAgMBAAE=\n-----END PUBLIC KEY-----\n"}}`) - }) - federatedRoutes.HandleFunc("/api/v1/activitypub/user-id/30", - func(res http.ResponseWriter, req *http.Request) { - // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/3 - fmt.Fprint(res, `{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],`+ - `"id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/30","type":"Person",`+ - `"icon":{"type":"Image","mediaType":"image/png","url":"https://federated-repo.prod.meissa.de/avatars/9c03f03d1c1f13f21976a22489326fe1"},`+ - `"url":"https://federated-repo.prod.meissa.de/stargoose2","inbox":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/30/inbox",`+ - `"outbox":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/30/outbox","preferredUsername":"stargoose2",`+ - `"publicKey":{"id":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/30#main-key","owner":"https://federated-repo.prod.meissa.de/api/v1/activitypub/user-id/30",`+ - `"publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAyv5NytsfqpWXSrwuk8a3\n0W1zE13QJioXb/e3opgN2CfKZkdm3hb+4+mGKoU/rCqegnL9/AO0Aw+R8fCHXx44\n`+ - `iNkdVpdY8Dzq+tQ9IetPWbyVIBvSzGgvpqfS05JuVPsy8cBX9wByODjr5kq7k1/v\nY1G7E3uh0a/XJc+mZutwGC3gPgR93NSrqsvTPN4wdhCCu9uj02S8OBoKuSYaPkU+\n`+ - `tZ4CEDpnclAOw/eNiH4x2irMvVtruEgtlTA5K2I4YJrmtGLidus47FCyc8/zEKUh\nAeiD8KWDvqsQgOhUwcQgRxAnYVCoMD9cnE+WFFRHTuQecNlmdNFs3Cr0yKcWjDde\n`+ - `trvnehW7LfPveGb0tHRHPuVAJpncTOidUR5h/7pqMyvKHzuAHWomm9rEaGUxd/7a\nL1CFjAf39+QIEgu0Anj8mIc7CTiz+DQhDz+0jBOsQ0iDXc5GeBz7X9Xv4Jp966nq\n`+ - `MUR0GQGXvfZQN9IqMO+WoUVy10Ddhns1EWGlA0x4fecnAgMBAAE=\n-----END PUBLIC KEY-----\n"}}`) - }) - federatedRoutes.HandleFunc("/", - func(res http.ResponseWriter, req *http.Request) { - t.Errorf("Unhandled request: %q", req.URL.EscapedPath()) - }) - federatedSrv := httptest.NewServer(federatedRoutes) + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) defer federatedSrv.Close() onGiteaRun(t, func(t *testing.T, u *url.URL) { diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go index ff138532f9..2c4ffba24d 100644 --- a/tests/integration/repo_settings_test.go +++ b/tests/integration/repo_settings_test.go @@ -5,9 +5,7 @@ package integration import ( "fmt" - "io" "net/http" - "net/http/httptest" "strings" "testing" @@ -21,6 +19,7 @@ import ( fm "code.gitea.io/gitea/modules/forgefed" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/validation" gitea_context "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" @@ -278,59 +277,8 @@ func TestRepoFollowing(t *testing.T) { setting.Federation.Enabled = false }() - federatedRoutes := http.NewServeMux() - federatedRoutes.HandleFunc("/.well-known/nodeinfo", - func(res http.ResponseWriter, req *http.Request) { - // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/.well-known/nodeinfo - responseBody := fmt.Sprintf(`{"links":[{"href":"http://%s/api/v1/nodeinfo","rel":"http://nodeinfo.diaspora.software/ns/schema/2.1"}]}`, req.Host) - t.Logf("response: %s", responseBody) - // TODO: as soon as content-type will become important: content-type: application/json;charset=utf-8 - fmt.Fprint(res, responseBody) - }) - federatedRoutes.HandleFunc("/api/v1/nodeinfo", - func(res http.ResponseWriter, req *http.Request) { - // curl -H "Accept: application/json" https://federated-repo.prod.meissa.de/api/v1/nodeinfo - responseBody := fmt.Sprintf(`{"version":"2.1","software":{"name":"forgejo","version":"1.20.0+dev-3183-g976d79044",` + - `"repository":"https://codeberg.org/forgejo/forgejo.git","homepage":"https://forgejo.org/"},` + - `"protocols":["activitypub"],"services":{"inbound":[],"outbound":["rss2.0"]},` + - `"openRegistrations":true,"usage":{"users":{"total":14,"activeHalfyear":2}},"metadata":{}}`) - fmt.Fprint(res, responseBody) - }) - repo1InboxReceivedLike := false - federatedRoutes.HandleFunc("/api/v1/activitypub/repository-id/1/inbox/", - func(res http.ResponseWriter, req *http.Request) { - if req.Method != "POST" { - t.Errorf("Unhandled request: %q", req.URL.EscapedPath()) - } - buf := new(strings.Builder) - _, err := io.Copy(buf, req.Body) - if err != nil { - t.Errorf("Error reading body: %q", err) - } - like := fm.ForgeLike{} - err = like.UnmarshalJSON([]byte(buf.String())) - if err != nil { - t.Errorf("Error unmarshalling ForgeLike: %q", err) - } - if isValid, err := validation.IsValid(like); !isValid { - t.Errorf("ForgeLike is not valid: %q", err) - } - - activityType := like.Type - object := like.Object.GetLink().String() - isLikeType := activityType == "Like" - isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1") - if !isLikeType || !isCorrectObject { - t.Errorf("Activity is not a like for this repo") - } - - repo1InboxReceivedLike = true - }) - federatedRoutes.HandleFunc("/", - func(res http.ResponseWriter, req *http.Request) { - t.Errorf("Unhandled request: %q", req.URL.EscapedPath()) - }) - federatedSrv := httptest.NewServer(federatedRoutes) + mock := test.NewFederationServerMock() + federatedSrv := mock.DistantServer(t) defer federatedSrv.Close() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) @@ -363,8 +311,24 @@ func TestRepoFollowing(t *testing.T) { req := NewRequestWithValues(t, "POST", link, map[string]string{ "_csrf": GetCSRF(t, session, repoLink), }) - assert.False(t, repo1InboxReceivedLike) + session.MakeRequest(t, req, http.StatusOK) - assert.True(t, repo1InboxReceivedLike) + + // Verify distant server received a like activity + like := fm.ForgeLike{} + err := like.UnmarshalJSON([]byte(mock.LastPost)) + if err != nil { + t.Errorf("Error unmarshalling ForgeLike: %q", err) + } + if isValid, err := validation.IsValid(like); !isValid { + t.Errorf("ForgeLike is not valid: %q", err) + } + activityType := like.Type + object := like.Object.GetLink().String() + isLikeType := activityType == "Like" + isCorrectObject := strings.HasSuffix(object, "/api/v1/activitypub/repository-id/1") + if !isLikeType || !isCorrectObject { + t.Errorf("Activity is not a like for this repo") + } }) } From ab18e391ee2b92a6cd9d5ded59bf008cad5df90a Mon Sep 17 00:00:00 2001 From: gondolyr Date: Mon, 3 Mar 2025 15:09:37 +0000 Subject: [PATCH 232/669] fix(ui): force all repo tab buttons to be the same height This fixes an issue with Simplified Chinese (and likely other languages) where the "Actions" button would be shown in English and have a different height compared to the other buttons in Chinese. This solution was proposed by Codeberg user "aimuz". Co-authored-by: aimuz --- web_src/css/base.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web_src/css/base.css b/web_src/css/base.css index dd761a1b75..5b0085ee0d 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1356,6 +1356,10 @@ table th[data-sortt-desc] .svg { border-color: var(--color-secondary); } +.ui.tabular.menu .item { + height: 100%; +} + .ui.tabular.menu .item, .ui.secondary.pointing.menu .item { padding: 11px 12px !important; From 24d77dc5ae0235c29f631a21564d0a7ef2c983d9 Mon Sep 17 00:00:00 2001 From: Litchi Pi Date: Wed, 5 Mar 2025 17:24:51 +0000 Subject: [PATCH 233/669] feat(ui): create a comment aggregator to reduce noise in issues (#6523) Closes: https://codeberg.org/forgejo/forgejo/issues/6042 Continuation of: https://codeberg.org/forgejo/forgejo/pulls/6284 Replaces: https://codeberg.org/forgejo/forgejo/pulls/6285 Context: https://codeberg.org/forgejo/forgejo/pulls/6284#issuecomment-2518599 Create a new type of comment: `CommentTypeAggregator` Replaces the grouping of labels and review request in a single place: the comment aggregator The whole list of comments is "scanned", if they can get aggregated (diff of time < 60secs, same poster, open / close issue, add / del labels, add /del review req), they are added to the aggregator. Once needed, the list of all the aggregated comments are replaced with a single aggregated comment containing all the data required. In templates, have a specific HTML rendering part for the comment aggregator, reuse the same rendering as with the other types of comments. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6523 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Otto Co-authored-by: Litchi Pi Co-committed-by: Litchi Pi --- models/issues/action_aggregator.go | 375 ++++++++ models/issues/comment.go | 10 +- release-notes/6523.md | 1 + routers/web/repo/action_aggregator_test.go | 800 +++++++++++++++++ routers/web/repo/issue.go | 191 +---- routers/web/repo/issue_test.go | 806 ------------------ .../repo/issue/view_content/comments.tmpl | 73 +- web_src/css/repo.css | 14 +- 8 files changed, 1264 insertions(+), 1006 deletions(-) create mode 100644 models/issues/action_aggregator.go create mode 100644 release-notes/6523.md create mode 100644 routers/web/repo/action_aggregator_test.go delete mode 100644 routers/web/repo/issue_test.go diff --git a/models/issues/action_aggregator.go b/models/issues/action_aggregator.go new file mode 100644 index 0000000000..722696fbb2 --- /dev/null +++ b/models/issues/action_aggregator.go @@ -0,0 +1,375 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package issues + +import ( + "slices" + + "code.gitea.io/gitea/models/organization" + user_model "code.gitea.io/gitea/models/user" +) + +type ActionAggregator struct { + StartUnix int64 + AggAge int64 + PosterID int64 + StartInd int + EndInd int + + PrevClosed bool + IsClosed bool + + AddedLabels []*Label + RemovedLabels []*Label + + AddedRequestReview []RequestReviewTarget + RemovedRequestReview []RequestReviewTarget +} + +// Get the time threshold for aggregation of multiple actions together +func (agg *ActionAggregator) timeThreshold() int64 { + if agg.AggAge > (60 * 60 * 24 * 30) { // Age > 1 month, aggregate by day + return 60 * 60 * 24 + } else if agg.AggAge > (60 * 60 * 24) { // Age > 1 day, aggregate by hour + return 60 * 60 + } else if agg.AggAge > (60 * 60) { // Age > 1 hour, aggregate by 10 mins + return 60 * 10 + } + // Else, aggregate by minute + return 60 +} + +// TODO Aggregate also +// - Dependency added / removed +// - Added / Removed due date +// - Milestone Added / Removed +func (agg *ActionAggregator) aggregateAction(c *Comment, index int) { + if agg.StartInd == -1 { + agg.StartInd = index + } + agg.EndInd = index + + if c.Type == CommentTypeClose { + agg.IsClosed = true + } else if c.Type == CommentTypeReopen { + agg.IsClosed = false + } else if c.Type == CommentTypeReviewRequest { + if c.AssigneeID > 0 { + req := RequestReviewTarget{User: c.Assignee} + if c.RemovedAssignee { + agg.delReviewRequest(req) + } else { + agg.addReviewRequest(req) + } + } else if c.AssigneeTeamID > 0 { + req := RequestReviewTarget{Team: c.AssigneeTeam} + if c.RemovedAssignee { + agg.delReviewRequest(req) + } else { + agg.addReviewRequest(req) + } + } + + for _, r := range c.RemovedRequestReview { + agg.delReviewRequest(r) + } + + for _, r := range c.AddedRequestReview { + agg.addReviewRequest(r) + } + } else if c.Type == CommentTypeLabel { + if c.Content == "1" { + agg.addLabel(c.Label) + } else { + agg.delLabel(c.Label) + } + } else if c.Type == CommentTypeAggregator { + agg.Merge(c.Aggregator) + } +} + +// Merge a past CommentAggregator with the next one in the issue comments list +func (agg *ActionAggregator) Merge(next *ActionAggregator) { + agg.IsClosed = next.IsClosed + + for _, l := range next.AddedLabels { + agg.addLabel(l) + } + + for _, l := range next.RemovedLabels { + agg.delLabel(l) + } + + for _, r := range next.AddedRequestReview { + agg.addReviewRequest(r) + } + + for _, r := range next.RemovedRequestReview { + agg.delReviewRequest(r) + } +} + +// Check if a comment can be aggregated or not depending on its type +func (agg *ActionAggregator) IsAggregated(t *CommentType) bool { + switch *t { + case CommentTypeAggregator, CommentTypeClose, CommentTypeReopen, CommentTypeLabel, CommentTypeReviewRequest: + { + return true + } + default: + { + return false + } + } +} + +// Add a label to the aggregated list +func (agg *ActionAggregator) addLabel(lbl *Label) { + for l, agglbl := range agg.RemovedLabels { + if agglbl.ID == lbl.ID { + agg.RemovedLabels = slices.Delete(agg.RemovedLabels, l, l+1) + return + } + } + + if !slices.ContainsFunc(agg.AddedLabels, func(l *Label) bool { return l.ID == lbl.ID }) { + agg.AddedLabels = append(agg.AddedLabels, lbl) + } +} + +// Remove a label from the aggregated list +func (agg *ActionAggregator) delLabel(lbl *Label) { + for l, agglbl := range agg.AddedLabels { + if agglbl.ID == lbl.ID { + agg.AddedLabels = slices.Delete(agg.AddedLabels, l, l+1) + return + } + } + + if !slices.ContainsFunc(agg.RemovedLabels, func(l *Label) bool { return l.ID == lbl.ID }) { + agg.RemovedLabels = append(agg.RemovedLabels, lbl) + } +} + +// Add a review request to the aggregated list +func (agg *ActionAggregator) addReviewRequest(req RequestReviewTarget) { + reqid := req.ID() + reqty := req.Type() + for r, aggreq := range agg.RemovedRequestReview { + if (aggreq.ID() == reqid) && (aggreq.Type() == reqty) { + agg.RemovedRequestReview = slices.Delete(agg.RemovedRequestReview, r, r+1) + return + } + } + + if !slices.ContainsFunc(agg.AddedRequestReview, func(r RequestReviewTarget) bool { return (r.ID() == reqid) && (r.Type() == reqty) }) { + agg.AddedRequestReview = append(agg.AddedRequestReview, req) + } +} + +// Delete a review request from the aggregated list +func (agg *ActionAggregator) delReviewRequest(req RequestReviewTarget) { + reqid := req.ID() + reqty := req.Type() + for r, aggreq := range agg.AddedRequestReview { + if (aggreq.ID() == reqid) && (aggreq.Type() == reqty) { + agg.AddedRequestReview = slices.Delete(agg.AddedRequestReview, r, r+1) + return + } + } + + if !slices.ContainsFunc(agg.RemovedRequestReview, func(r RequestReviewTarget) bool { return (r.ID() == reqid) && (r.Type() == reqty) }) { + agg.RemovedRequestReview = append(agg.RemovedRequestReview, req) + } +} + +// Check if anything has changed with this aggregated list of comments +func (agg *ActionAggregator) Changed() bool { + return (agg.IsClosed != agg.PrevClosed) || + (len(agg.AddedLabels) > 0) || + (len(agg.RemovedLabels) > 0) || + (len(agg.AddedRequestReview) > 0) || + (len(agg.RemovedRequestReview) > 0) +} + +func (agg *ActionAggregator) OnlyLabelsChanged() bool { + return ((len(agg.AddedLabels) > 0) || (len(agg.RemovedLabels) > 0)) && + (len(agg.AddedRequestReview) == 0) && (len(agg.RemovedRequestReview) == 0) && + (agg.PrevClosed == agg.IsClosed) +} + +func (agg *ActionAggregator) OnlyRequestReview() bool { + return ((len(agg.AddedRequestReview) > 0) || (len(agg.RemovedRequestReview) > 0)) && + (len(agg.AddedLabels) == 0) && (len(agg.RemovedLabels) == 0) && + (agg.PrevClosed == agg.IsClosed) +} + +func (agg *ActionAggregator) OnlyClosedReopened() bool { + return (agg.IsClosed != agg.PrevClosed) && + (len(agg.AddedLabels) == 0) && (len(agg.RemovedLabels) == 0) && + (len(agg.AddedRequestReview) == 0) && (len(agg.RemovedRequestReview) == 0) +} + +// Reset the aggregator to start a new aggregating context +func (agg *ActionAggregator) Reset(cur *Comment, now int64) { + agg.StartUnix = int64(cur.CreatedUnix) + agg.AggAge = now - agg.StartUnix + agg.PosterID = cur.PosterID + + agg.PrevClosed = agg.IsClosed + + agg.StartInd = -1 + agg.EndInd = -1 + agg.AddedLabels = []*Label{} + agg.RemovedLabels = []*Label{} + agg.AddedRequestReview = []RequestReviewTarget{} + agg.RemovedRequestReview = []RequestReviewTarget{} +} + +// Function that replaces all the comments aggregated with a single one +// Its CommentType depend on whether multiple type of comments are been aggregated or not +// If nothing has changed, we remove all the comments that get nullified +// +// The function returns how many comments has been removed, in order for the "for" loop +// of the main algorithm to change its index +func (agg *ActionAggregator) createAggregatedComment(issue *Issue, final bool) int { + // If the aggregation of comments make the whole thing null, erase all the comments + if !agg.Changed() { + if final { + issue.Comments = issue.Comments[:agg.StartInd] + } else { + issue.Comments = slices.Replace(issue.Comments, agg.StartInd, agg.EndInd+1) + } + return (agg.EndInd - agg.StartInd) + 1 + } + + newAgg := *agg // Trigger a memory allocation, get a COPY of the aggregator + + // Keep the same author, time, etc... But reset the parts we may want to use + comment := issue.Comments[agg.StartInd] + comment.Content = "" + comment.Label = nil + comment.Aggregator = nil + comment.Assignee = nil + comment.AssigneeID = 0 + comment.AssigneeTeam = nil + comment.AssigneeTeamID = 0 + comment.RemovedAssignee = false + comment.AddedLabels = nil + comment.RemovedLabels = nil + + // In case there's only a single change, create a comment of this type + // instead of an aggregator + if agg.OnlyLabelsChanged() { + comment.Type = CommentTypeLabel + } else if agg.OnlyClosedReopened() { + if agg.IsClosed { + comment.Type = CommentTypeClose + } else { + comment.Type = CommentTypeReopen + } + } else if agg.OnlyRequestReview() { + comment.Type = CommentTypeReviewRequest + } else { + comment.Type = CommentTypeAggregator + comment.Aggregator = &newAgg + } + + if len(newAgg.AddedLabels) > 0 { + comment.AddedLabels = newAgg.AddedLabels + } + + if len(newAgg.RemovedLabels) > 0 { + comment.RemovedLabels = newAgg.RemovedLabels + } + + if len(newAgg.AddedRequestReview) > 0 { + comment.AddedRequestReview = newAgg.AddedRequestReview + } + + if len(newAgg.RemovedRequestReview) > 0 { + comment.RemovedRequestReview = newAgg.RemovedRequestReview + } + + if final { + issue.Comments = append(issue.Comments[:agg.StartInd], comment) + } else { + issue.Comments = slices.Replace(issue.Comments, agg.StartInd, agg.EndInd+1, comment) + } + return agg.EndInd - agg.StartInd +} + +// combineCommentsHistory combines nearby elements in the history as one +func CombineCommentsHistory(issue *Issue, now int64) { + if len(issue.Comments) < 1 { + return + } + + // Initialise a new empty aggregator, ready to combine comments + var agg ActionAggregator + agg.Reset(issue.Comments[0], now) + + for i := 0; i < len(issue.Comments); i++ { + cur := issue.Comments[i] + // If the comment we encounter is not accepted inside an aggregator + if !agg.IsAggregated(&cur.Type) { + // If we aggregated some data, create the resulting comment for it + if agg.StartInd != -1 { + i -= agg.createAggregatedComment(issue, false) + } + + agg.StartInd = -1 + if i+1 < len(issue.Comments) { + agg.Reset(issue.Comments[i+1], now) + } + + // Do not need to continue the aggregation loop, skip to next comment + continue + } + + // If the comment we encounter cannot be aggregated with the current aggregator, + // we create a new empty aggregator + threshold := agg.timeThreshold() + if ((int64(cur.CreatedUnix) - agg.StartUnix) > threshold) || (cur.PosterID != agg.PosterID) { + // First, create the aggregated comment if there's data in it + if agg.StartInd != -1 { + i -= agg.createAggregatedComment(issue, false) + } + agg.Reset(cur, now) + } + + agg.aggregateAction(cur, i) + } + + // Create the aggregated comment if there's data in it + if agg.StartInd != -1 { + agg.createAggregatedComment(issue, true) + } +} + +type RequestReviewTarget struct { + User *user_model.User + Team *organization.Team +} + +func (t *RequestReviewTarget) ID() int64 { + if t.User != nil { + return t.User.ID + } + return t.Team.ID +} + +func (t *RequestReviewTarget) Name() string { + if t.User != nil { + return t.User.GetDisplayName() + } + return t.Team.Name +} + +func (t *RequestReviewTarget) Type() string { + if t.User != nil { + return "user" + } + return "team" +} diff --git a/models/issues/comment.go b/models/issues/comment.go index 0bf53bb4dd..f77b2bcc8d 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -114,6 +114,8 @@ const ( CommentTypePin // 36 pin Issue CommentTypeUnpin // 37 unpin Issue + + CommentTypeAggregator // 38 Aggregator of comments ) var commentStrings = []string{ @@ -155,6 +157,7 @@ var commentStrings = []string{ "pull_cancel_scheduled_merge", "pin", "unpin", + "action_aggregator", } func (t CommentType) String() string { @@ -236,12 +239,6 @@ func (r RoleInRepo) LocaleHelper(lang translation.Locale) string { return lang.TrString("repo.issues.role." + string(r) + "_helper") } -type RequestReviewTarget interface { - ID() int64 - Name() string - Type() string -} - // Comment represents a comment in commit and issue page. type Comment struct { ID int64 `xorm:"pk autoincr"` @@ -254,6 +251,7 @@ type Comment struct { Issue *Issue `xorm:"-"` LabelID int64 Label *Label `xorm:"-"` + Aggregator *ActionAggregator `xorm:"-"` AddedLabels []*Label `xorm:"-"` RemovedLabels []*Label `xorm:"-"` AddedRequestReview []RequestReviewTarget `xorm:"-"` diff --git a/release-notes/6523.md b/release-notes/6523.md new file mode 100644 index 0000000000..96a1fe0cf3 --- /dev/null +++ b/release-notes/6523.md @@ -0,0 +1 @@ +Reduce noise in the timeline of issues and pull requests. If certain timeline events are performed within a certain timeframe of each other with no other events in between, they will be combined into a single timeline event, and any contradictory actions will be canceled and not displayed. The older the events, the wider the timeframe will become. diff --git a/routers/web/repo/action_aggregator_test.go b/routers/web/repo/action_aggregator_test.go new file mode 100644 index 0000000000..181c1120db --- /dev/null +++ b/routers/web/repo/action_aggregator_test.go @@ -0,0 +1,800 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package repo + +import ( + "strings" + "testing" + + issue_model "code.gitea.io/gitea/models/issues" + "code.gitea.io/gitea/models/organization" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/timeutil" + + "github.com/stretchr/testify/assert" +) + +// *************** Helper functions for the tests *************** + +func testComment(t int64) *issue_model.Comment { + return &issue_model.Comment{PosterID: 1, CreatedUnix: timeutil.TimeStamp(t)} +} + +func nameToID(name string) int64 { + var id int64 + for c, letter := range name { + id += int64((c+1)*1000) * int64(letter) + } + return id +} + +func createReqReviewTarget(name string) issue_model.RequestReviewTarget { + if strings.HasSuffix(name, "-team") { + team := createTeam(name) + return issue_model.RequestReviewTarget{Team: &team} + } + user := createUser(name) + return issue_model.RequestReviewTarget{User: &user} +} + +func createUser(name string) user_model.User { + return user_model.User{Name: name, ID: nameToID(name)} +} + +func createTeam(name string) organization.Team { + return organization.Team{Name: name, ID: nameToID(name)} +} + +func createLabel(name string) issue_model.Label { + return issue_model.Label{Name: name, ID: nameToID(name)} +} + +func addLabel(t int64, name string) *issue_model.Comment { + c := testComment(t) + c.Type = issue_model.CommentTypeLabel + c.Content = "1" + lbl := createLabel(name) + c.Label = &lbl + c.AddedLabels = []*issue_model.Label{&lbl} + return c +} + +func delLabel(t int64, name string) *issue_model.Comment { + c := addLabel(t, name) + c.Content = "" + c.RemovedLabels = c.AddedLabels + c.AddedLabels = nil + return c +} + +func openOrClose(t int64, close bool) *issue_model.Comment { + c := testComment(t) + if close { + c.Type = issue_model.CommentTypeClose + } else { + c.Type = issue_model.CommentTypeReopen + } + return c +} + +func reqReview(t int64, name string, delReq bool) *issue_model.Comment { + c := testComment(t) + c.Type = issue_model.CommentTypeReviewRequest + if strings.HasSuffix(name, "-team") { + team := createTeam(name) + c.AssigneeTeam = &team + c.AssigneeTeamID = team.ID + } else { + user := createUser(name) + c.Assignee = &user + c.AssigneeID = user.ID + } + c.RemovedAssignee = delReq + return c +} + +func reqReviewList(t int64, del bool, names ...string) *issue_model.Comment { + req := []issue_model.RequestReviewTarget{} + for _, name := range names { + req = append(req, createReqReviewTarget(name)) + } + cmnt := testComment(t) + cmnt.Type = issue_model.CommentTypeReviewRequest + if del { + cmnt.RemovedRequestReview = req + } else { + cmnt.AddedRequestReview = req + } + return cmnt +} + +func aggregatedComment(t int64, + closed bool, + addLabels []*issue_model.Label, + delLabels []*issue_model.Label, + addReqReview []issue_model.RequestReviewTarget, + delReqReview []issue_model.RequestReviewTarget, +) *issue_model.Comment { + cmnt := testComment(t) + cmnt.Type = issue_model.CommentTypeAggregator + cmnt.Aggregator = &issue_model.ActionAggregator{ + IsClosed: closed, + AddedLabels: addLabels, + RemovedLabels: delLabels, + AddedRequestReview: addReqReview, + RemovedRequestReview: delReqReview, + } + if len(addLabels) > 0 { + cmnt.AddedLabels = addLabels + } + if len(delLabels) > 0 { + cmnt.RemovedLabels = delLabels + } + if len(addReqReview) > 0 { + cmnt.AddedRequestReview = addReqReview + } + if len(delReqReview) > 0 { + cmnt.RemovedRequestReview = delReqReview + } + return cmnt +} + +func commentText(t int64, text string) *issue_model.Comment { + c := testComment(t) + c.Type = issue_model.CommentTypeComment + c.Content = text + return c +} + +// **************************************************************** + +type testCase struct { + name string + beforeCombined []*issue_model.Comment + afterCombined []*issue_model.Comment + sameAfter bool + timestampCombination int64 +} + +func (kase *testCase) doTest(t *testing.T) { + issue := issue_model.Issue{Comments: kase.beforeCombined} + + var now int64 = -9223372036854775808 + for c := 0; c < len(kase.beforeCombined); c++ { + assert.Greater(t, int64(kase.beforeCombined[c].CreatedUnix), now) + now = int64(kase.beforeCombined[c].CreatedUnix) + } + + if kase.timestampCombination != 0 { + now = kase.timestampCombination + } + + issue_model.CombineCommentsHistory(&issue, now) + + after := kase.afterCombined + if kase.sameAfter { + after = kase.beforeCombined + } + + if len(after) != len(issue.Comments) { + t.Logf("Expected %v comments, got %v", len(after), len(issue.Comments)) + t.Logf("Comments got after combination:") + for c := 0; c < len(issue.Comments); c++ { + cmt := issue.Comments[c] + t.Logf("%v %v %v\n", cmt.Type, cmt.CreatedUnix, cmt.Content) + } + assert.EqualValues(t, len(after), len(issue.Comments)) + t.Fail() + return + } + + for c := 0; c < len(after); c++ { + l := (after)[c] + r := issue.Comments[c] + + // Ignore some inner data of the aggregator to facilitate testing + if l.Type == issue_model.CommentTypeAggregator { + r.Aggregator.StartUnix = 0 + r.Aggregator.PrevClosed = false + r.Aggregator.PosterID = 0 + r.Aggregator.StartInd = 0 + r.Aggregator.EndInd = 0 + r.Aggregator.AggAge = 0 + } + + // We can safely ignore this if the rest matches + if l.Type == issue_model.CommentTypeLabel { + l.Label = nil + l.Content = "" + } else if l.Type == issue_model.CommentTypeReviewRequest { + l.Assignee = nil + l.AssigneeID = 0 + l.AssigneeTeam = nil + l.AssigneeTeamID = 0 + } + + assert.EqualValues(t, (after)[c], issue.Comments[c], + "Comment %v is not equal", c, + ) + } +} + +// **************** Start of the tests ****************** + +func TestCombineLabelComments(t *testing.T) { + var tmon int64 = 60 * 60 * 24 * 30 + var tday int64 = 60 * 60 * 24 + var thour int64 = 60 * 60 + kases := []testCase{ + // ADD single = normal label comment + { + name: "add_single_label", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + commentText(10, "I'm a salmon"), + }, + sameAfter: true, + }, + + // ADD then REMOVE = Nothing + { + name: "add_label_then_remove", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + delLabel(1, "a"), + commentText(65, "I'm a salmon"), + }, + afterCombined: []*issue_model.Comment{ + commentText(65, "I'm a salmon"), + }, + }, + + // ADD 1 then comment then REMOVE = separate comments + { + name: "add_label_then_comment_then_remove", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + commentText(10, "I'm a salmon"), + delLabel(20, "a"), + }, + sameAfter: true, + }, + + // ADD 2 = Combined labels + { + name: "combine_labels", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + addLabel(10, "b"), + commentText(20, "I'm a salmon"), + addLabel(30, "c"), + addLabel(80, "d"), + addLabel(85, "e"), + delLabel(90, "c"), + }, + afterCombined: []*issue_model.Comment{ + { + PosterID: 1, + Type: issue_model.CommentTypeLabel, + CreatedUnix: timeutil.TimeStamp(0), + AddedLabels: []*issue_model.Label{ + {Name: "a", ID: nameToID("a")}, + {Name: "b", ID: nameToID("b")}, + }, + }, + commentText(20, "I'm a salmon"), + { + PosterID: 1, + Type: issue_model.CommentTypeLabel, + CreatedUnix: timeutil.TimeStamp(30), + AddedLabels: []*issue_model.Label{ + {Name: "d", ID: nameToID("d")}, + {Name: "e", ID: nameToID("e")}, + }, + }, + }, + }, + + // ADD 1, then 1 later = 2 separate comments + { + name: "add_then_later_label", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + addLabel(60, "b"), + addLabel(121, "c"), + }, + afterCombined: []*issue_model.Comment{ + { + PosterID: 1, + Type: issue_model.CommentTypeLabel, + CreatedUnix: timeutil.TimeStamp(0), + AddedLabels: []*issue_model.Label{ + {Name: "a", ID: nameToID("a")}, + {Name: "b", ID: nameToID("b")}, + }, + }, + addLabel(121, "c"), + }, + }, + + // ADD 2 then REMOVE 1 = label + { + name: "add_2_remove_1", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + addLabel(10, "b"), + delLabel(20, "a"), + }, + afterCombined: []*issue_model.Comment{ + // The timestamp will be the one of the first aggregated comment + addLabel(0, "b"), + }, + }, + + // ADD then REMOVE multiple = nothing + { + name: "add_multiple_remove_all", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + addLabel(1, "b"), + addLabel(2, "c"), + addLabel(3, "d"), + addLabel(4, "e"), + delLabel(5, "d"), + delLabel(6, "a"), + delLabel(7, "e"), + delLabel(8, "c"), + delLabel(9, "b"), + }, + afterCombined: nil, + }, + + // ADD 2, wait, REMOVE 2 = +2 then -2 comments + { + name: "add2_wait_rm2_labels", + beforeCombined: []*issue_model.Comment{ + addLabel(0, "a"), + addLabel(1, "b"), + delLabel(120, "a"), + delLabel(121, "b"), + }, + afterCombined: []*issue_model.Comment{ + { + PosterID: 1, + Type: issue_model.CommentTypeLabel, + CreatedUnix: timeutil.TimeStamp(0), + AddedLabels: []*issue_model.Label{ + {Name: "a", ID: nameToID("a")}, + {Name: "b", ID: nameToID("b")}, + }, + }, + { + PosterID: 1, + Type: issue_model.CommentTypeLabel, + CreatedUnix: timeutil.TimeStamp(120), + RemovedLabels: []*issue_model.Label{ + {Name: "a", ID: nameToID("a")}, + {Name: "b", ID: nameToID("b")}, + }, + }, + }, + }, + + // Regression check on edge case + { + name: "regression_edgecase_finalagg", + beforeCombined: []*issue_model.Comment{ + commentText(0, "hey"), + commentText(1, "ho"), + addLabel(2, "a"), + addLabel(3, "b"), + delLabel(4, "a"), + delLabel(5, "b"), + + addLabel(120, "a"), + + addLabel(220, "c"), + addLabel(221, "d"), + addLabel(222, "e"), + delLabel(223, "d"), + + delLabel(400, "a"), + }, + afterCombined: []*issue_model.Comment{ + commentText(0, "hey"), + commentText(1, "ho"), + addLabel(120, "a"), + { + PosterID: 1, + Type: issue_model.CommentTypeLabel, + CreatedUnix: timeutil.TimeStamp(220), + AddedLabels: []*issue_model.Label{ + {Name: "c", ID: nameToID("c")}, + {Name: "e", ID: nameToID("e")}, + }, + }, + delLabel(400, "a"), + }, + }, + + { + name: "combine_label_high_timestamp_separated", + timestampCombination: tmon + 1, + beforeCombined: []*issue_model.Comment{ + // 1 month old, comments separated by 1 Day + 1 sec (not agg) + addLabel(0, "d"), + delLabel(tday+1, "d"), + + // 1 day old, comments separated by 1 hour + 1 sec (not agg) + addLabel((tmon-tday)-thour, "c"), + delLabel((tmon-tday)+1, "c"), + + // 1 hour old, comments separated by 10 mins + 1 sec (not agg) + addLabel(tmon-thour, "b"), + delLabel((tmon-(50*60))+1, "b"), + + // Else, aggregate by minute + addLabel(tmon-61, "a"), + delLabel(tmon, "a"), + }, + sameAfter: true, + }, + + // Test higher timestamp diff + { + name: "combine_label_high_timestamp_merged", + timestampCombination: tmon + 1, + beforeCombined: []*issue_model.Comment{ + // 1 month old, comments separated by 1 Day (aggregated) + addLabel(0, "d"), + delLabel(tday, "d"), + + // 1 day old, comments separated by 1 hour (aggregated) + addLabel((tmon-tday)-thour, "c"), + delLabel(tmon-tday, "c"), + + // 1 hour old, comments separated by 10 mins (aggregated) + addLabel(tmon-thour, "b"), + delLabel(tmon-(50*60), "b"), + + addLabel(tmon-60, "a"), + delLabel(tmon, "a"), + }, + }, + } + + for _, kase := range kases { + t.Run(kase.name, kase.doTest) + } +} + +func TestCombineReviewRequests(t *testing.T) { + kases := []testCase{ + // ADD single = normal request review comment + { + name: "add_single_review", + beforeCombined: []*issue_model.Comment{ + reqReview(0, "toto", false), + commentText(10, "I'm a salmon"), + reqReview(20, "toto-team", false), + }, + sameAfter: true, + }, + + // ADD then REMOVE = Nothing + { + name: "add_then_remove_review", + beforeCombined: []*issue_model.Comment{ + reqReview(0, "toto", false), + reqReview(5, "toto", true), + commentText(10, "I'm a salmon"), + }, + afterCombined: []*issue_model.Comment{ + commentText(10, "I'm a salmon"), + }, + }, + + // ADD 1 then comment then REMOVE = separate comments + { + name: "add_comment_del_review", + beforeCombined: []*issue_model.Comment{ + reqReview(0, "toto", false), + commentText(5, "I'm a salmon"), + reqReview(10, "toto", true), + }, + sameAfter: true, + }, + + // ADD 2 = Combined request reviews + { + name: "combine_reviews", + beforeCombined: []*issue_model.Comment{ + reqReview(0, "toto", false), + reqReview(10, "tutu-team", false), + commentText(20, "I'm a salmon"), + reqReview(30, "titi", false), + reqReview(80, "tata", false), + reqReview(85, "tyty-team", false), + reqReview(90, "titi", true), + }, + afterCombined: []*issue_model.Comment{ + reqReviewList(0, false, "toto", "tutu-team"), + commentText(20, "I'm a salmon"), + reqReviewList(30, false, "tata", "tyty-team"), + }, + }, + + // ADD 1, then 1 later = 2 separate comments + { + name: "add_then_later_review", + beforeCombined: []*issue_model.Comment{ + reqReview(0, "titi", false), + reqReview(60, "toto-team", false), + reqReview(121, "tutu", false), + }, + afterCombined: []*issue_model.Comment{ + reqReviewList(0, false, "titi", "toto-team"), + reqReviewList(121, false, "tutu"), + }, + }, + + // ADD 2 then REMOVE 1 = single request review + { + name: "add_2_then_remove_review", + beforeCombined: []*issue_model.Comment{ + reqReview(0, "titi-team", false), + reqReview(59, "toto", false), + reqReview(60, "titi-team", true), + }, + afterCombined: []*issue_model.Comment{ + reqReviewList(0, false, "toto"), + }, + }, + + // ADD then REMOVE multiple = nothing + { + name: "add_multiple_then_remove_all_review", + beforeCombined: []*issue_model.Comment{ + reqReview(0, "titi0-team", false), + reqReview(1, "toto1", false), + reqReview(2, "titi2", false), + reqReview(3, "titi3-team", false), + reqReview(4, "titi4", false), + reqReview(5, "titi5", false), + reqReview(6, "titi6-team", false), + reqReview(10, "titi0-team", true), + reqReview(11, "toto1", true), + reqReview(12, "titi2", true), + reqReview(13, "titi3-team", true), + reqReview(14, "titi4", true), + reqReview(15, "titi5", true), + reqReview(16, "titi6-team", true), + }, + afterCombined: nil, + }, + + // ADD 2, wait, REMOVE 2 = +2 then -2 comments + { + name: "add2_wait_rm2_requests", + beforeCombined: []*issue_model.Comment{ + reqReview(1, "titi", false), + reqReview(2, "toto-team", false), + reqReview(121, "titi", true), + reqReview(122, "toto-team", true), + }, + afterCombined: []*issue_model.Comment{ + reqReviewList(1, false, "titi", "toto-team"), + reqReviewList(121, true, "titi", "toto-team"), + }, + }, + } + + for _, kase := range kases { + t.Run(kase.name, kase.doTest) + } +} + +func TestCombineOpenClose(t *testing.T) { + kases := []testCase{ + // Close then open = nullified + { + name: "close_open_nullified", + beforeCombined: []*issue_model.Comment{ + openOrClose(0, true), + openOrClose(10, false), + }, + afterCombined: nil, + }, + + // Close then open later = separate comments + { + name: "close_open_later", + beforeCombined: []*issue_model.Comment{ + openOrClose(0, true), + openOrClose(61, false), + }, + sameAfter: true, + }, + + // Close then comment then open = separate comments + { + name: "close_comment_open", + beforeCombined: []*issue_model.Comment{ + openOrClose(0, true), + commentText(1, "I'm a salmon"), + openOrClose(2, false), + }, + sameAfter: true, + }, + } + + for _, kase := range kases { + t.Run(kase.name, kase.doTest) + } +} + +func TestCombineMultipleDifferentComments(t *testing.T) { + lblA := createLabel("a") + kases := []testCase{ + // Add Label + Close + ReqReview = Combined + { + name: "label_close_reqreview_combined", + beforeCombined: []*issue_model.Comment{ + reqReview(1, "toto", false), + addLabel(2, "a"), + openOrClose(3, true), + + reqReview(101, "toto", true), + openOrClose(102, false), + delLabel(103, "a"), + }, + afterCombined: []*issue_model.Comment{ + aggregatedComment(1, + true, + []*issue_model.Label{&lblA}, + []*issue_model.Label{}, + []issue_model.RequestReviewTarget{createReqReviewTarget("toto")}, + []issue_model.RequestReviewTarget{}, + ), + aggregatedComment(101, + false, + []*issue_model.Label{}, + []*issue_model.Label{&lblA}, + []issue_model.RequestReviewTarget{}, + []issue_model.RequestReviewTarget{createReqReviewTarget("toto")}, + ), + }, + }, + + // Add Req + Add Label + Close + Del Req + Del Label = Close only + { + name: "req_label_close_dellabel_delreq", + beforeCombined: []*issue_model.Comment{ + addLabel(2, "a"), + reqReview(3, "titi", false), + openOrClose(4, true), + delLabel(5, "a"), + reqReview(6, "titi", true), + }, + afterCombined: []*issue_model.Comment{ + openOrClose(2, true), + }, + }, + + // Close + Add Req + Add Label + Del Req + Open = Label only + { + name: "close_req_label_open_delreq", + beforeCombined: []*issue_model.Comment{ + openOrClose(2, true), + reqReview(4, "titi", false), + addLabel(5, "a"), + reqReview(6, "titi", true), + openOrClose(8, false), + }, + afterCombined: []*issue_model.Comment{ + addLabel(2, "a"), + }, + }, + + // Add Label + Close + Add ReqReview + Del Label + Open = ReqReview only + { + name: "label_close_req_dellabel_open", + beforeCombined: []*issue_model.Comment{ + addLabel(1, "a"), + openOrClose(2, true), + reqReview(4, "titi", false), + openOrClose(7, false), + delLabel(8, "a"), + }, + afterCombined: []*issue_model.Comment{ + reqReviewList(1, false, "titi"), + }, + }, + + // Add Label + Close + ReqReview, then delete everything = nothing + { + name: "add_multiple_delete_everything", + beforeCombined: []*issue_model.Comment{ + addLabel(1, "a"), + openOrClose(2, true), + reqReview(4, "titi", false), + openOrClose(7, false), + delLabel(8, "a"), + reqReview(10, "titi", true), + }, + afterCombined: nil, + }, + + // Add multiple, then comment, then delete everything = separate aggregation + { + name: "add_multiple_comment_delete_everything", + beforeCombined: []*issue_model.Comment{ + addLabel(1, "a"), + openOrClose(2, true), + reqReview(4, "titi", false), + + commentText(6, "I'm a salmon"), + + openOrClose(7, false), + delLabel(8, "a"), + reqReview(10, "titi", true), + }, + afterCombined: []*issue_model.Comment{ + aggregatedComment(1, + true, + []*issue_model.Label{&lblA}, + []*issue_model.Label{}, + []issue_model.RequestReviewTarget{createReqReviewTarget("titi")}, + []issue_model.RequestReviewTarget{}, + ), + commentText(6, "I'm a salmon"), + aggregatedComment(7, + false, + []*issue_model.Label{}, + []*issue_model.Label{&lblA}, + []issue_model.RequestReviewTarget{}, + []issue_model.RequestReviewTarget{createReqReviewTarget("titi")}, + ), + }, + }, + + { + name: "regression_edgecase_finalagg", + beforeCombined: []*issue_model.Comment{ + commentText(0, "hey"), + commentText(1, "ho"), + addLabel(2, "a"), + reqReview(3, "titi", false), + delLabel(4, "a"), + reqReview(5, "titi", true), + + addLabel(120, "a"), + + openOrClose(220, true), + addLabel(221, "d"), + reqReview(222, "toto-team", false), + delLabel(223, "d"), + + delLabel(400, "a"), + }, + afterCombined: []*issue_model.Comment{ + commentText(0, "hey"), + commentText(1, "ho"), + addLabel(120, "a"), + aggregatedComment(220, + true, + []*issue_model.Label{}, + []*issue_model.Label{}, + []issue_model.RequestReviewTarget{createReqReviewTarget("toto-team")}, + []issue_model.RequestReviewTarget{}, + ), + delLabel(400, "a"), + }, + }, + } + + for _, kase := range kases { + t.Run(kase.name, kase.doTest) + } +} diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index a86d043497..e45abd3952 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1834,8 +1834,7 @@ func ViewIssue(ctx *context.Context) { ctx.Data["LatestCloseCommentID"] = latestCloseCommentID // Combine multiple label assignments into a single comment - combineLabelComments(issue) - combineRequestReviewComments(issue) + issues_model.CombineCommentsHistory(issue, time.Now().Unix()) getBranchData(ctx, issue) if issue.IsPull { @@ -3710,194 +3709,6 @@ func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, return attachHTML } -type RequestReviewTarget struct { - user *user_model.User - team *organization.Team -} - -func (t *RequestReviewTarget) ID() int64 { - if t.user != nil { - return t.user.ID - } - return t.team.ID -} - -func (t *RequestReviewTarget) Name() string { - if t.user != nil { - return t.user.GetDisplayName() - } - return t.team.Name -} - -func (t *RequestReviewTarget) Type() string { - if t.user != nil { - return "user" - } - return "team" -} - -// combineRequestReviewComments combine the nearby request review comments as one. -func combineRequestReviewComments(issue *issues_model.Issue) { - var prev, cur *issues_model.Comment - for i := 0; i < len(issue.Comments); i++ { - cur = issue.Comments[i] - if i > 0 { - prev = issue.Comments[i-1] - } - if i == 0 || cur.Type != issues_model.CommentTypeReviewRequest || - (prev != nil && prev.PosterID != cur.PosterID) || - (prev != nil && cur.CreatedUnix-prev.CreatedUnix >= 60) { - if cur.Type == issues_model.CommentTypeReviewRequest && (cur.Assignee != nil || cur.AssigneeTeam != nil) { - if cur.RemovedAssignee { - if cur.AssigneeTeam != nil { - cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam}) - } else { - cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{user: cur.Assignee}) - } - } else { - if cur.AssigneeTeam != nil { - cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam}) - } else { - cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{user: cur.Assignee}) - } - } - } - continue - } - - // Previous comment is not a review request, so cannot group. Start a new group. - if prev.Type != issues_model.CommentTypeReviewRequest { - if cur.RemovedAssignee { - if cur.AssigneeTeam != nil { - cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam}) - } else { - cur.RemovedRequestReview = append(cur.RemovedRequestReview, &RequestReviewTarget{user: cur.Assignee}) - } - } else { - if cur.AssigneeTeam != nil { - cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam}) - } else { - cur.AddedRequestReview = append(cur.AddedRequestReview, &RequestReviewTarget{user: cur.Assignee}) - } - } - continue - } - - // Start grouping. - if cur.RemovedAssignee { - addedIndex := slices.IndexFunc(prev.AddedRequestReview, func(t issues_model.RequestReviewTarget) bool { - if cur.AssigneeTeam != nil { - return cur.AssigneeTeam.ID == t.ID() && t.Type() == "team" - } - return cur.Assignee.ID == t.ID() && t.Type() == "user" - }) - - // If for this target a AddedRequestReview, then we remove that entry. If it's not found, then add it to the RemovedRequestReview. - if addedIndex == -1 { - if cur.AssigneeTeam != nil { - prev.RemovedRequestReview = append(prev.RemovedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam}) - } else { - prev.RemovedRequestReview = append(prev.RemovedRequestReview, &RequestReviewTarget{user: cur.Assignee}) - } - } else { - prev.AddedRequestReview = slices.Delete(prev.AddedRequestReview, addedIndex, addedIndex+1) - } - } else { - removedIndex := slices.IndexFunc(prev.RemovedRequestReview, func(t issues_model.RequestReviewTarget) bool { - if cur.AssigneeTeam != nil { - return cur.AssigneeTeam.ID == t.ID() && t.Type() == "team" - } - return cur.Assignee.ID == t.ID() && t.Type() == "user" - }) - - // If for this target a RemovedRequestReview, then we remove that entry. If it's not found, then add it to the AddedRequestReview. - if removedIndex == -1 { - if cur.AssigneeTeam != nil { - prev.AddedRequestReview = append(prev.AddedRequestReview, &RequestReviewTarget{team: cur.AssigneeTeam}) - } else { - prev.AddedRequestReview = append(prev.AddedRequestReview, &RequestReviewTarget{user: cur.Assignee}) - } - } else { - prev.RemovedRequestReview = slices.Delete(prev.RemovedRequestReview, removedIndex, removedIndex+1) - } - } - - // Propagate creation time. - prev.CreatedUnix = cur.CreatedUnix - - // Remove the current comment since it has been combined to prev comment - issue.Comments = append(issue.Comments[:i], issue.Comments[i+1:]...) - i-- - } -} - -// combineLabelComments combine the nearby label comments as one. -func combineLabelComments(issue *issues_model.Issue) { - var prev, cur *issues_model.Comment - for i := 0; i < len(issue.Comments); i++ { - cur = issue.Comments[i] - if i > 0 { - prev = issue.Comments[i-1] - } - if i == 0 || cur.Type != issues_model.CommentTypeLabel || - (prev != nil && prev.PosterID != cur.PosterID) || - (prev != nil && cur.CreatedUnix-prev.CreatedUnix >= 60) { - if cur.Type == issues_model.CommentTypeLabel && cur.Label != nil { - if cur.Content != "1" { - cur.RemovedLabels = append(cur.RemovedLabels, cur.Label) - } else { - cur.AddedLabels = append(cur.AddedLabels, cur.Label) - } - } - continue - } - - if cur.Label != nil { // now cur MUST be label comment - if prev.Type == issues_model.CommentTypeLabel { // we can combine them only prev is a label comment - if cur.Content != "1" { - // remove labels from the AddedLabels list if the label that was removed is already - // in this list, and if it's not in this list, add the label to RemovedLabels - addedAndRemoved := false - for i, label := range prev.AddedLabels { - if cur.Label.ID == label.ID { - prev.AddedLabels = append(prev.AddedLabels[:i], prev.AddedLabels[i+1:]...) - addedAndRemoved = true - break - } - } - if !addedAndRemoved { - prev.RemovedLabels = append(prev.RemovedLabels, cur.Label) - } - } else { - // remove labels from the RemovedLabels list if the label that was added is already - // in this list, and if it's not in this list, add the label to AddedLabels - removedAndAdded := false - for i, label := range prev.RemovedLabels { - if cur.Label.ID == label.ID { - prev.RemovedLabels = append(prev.RemovedLabels[:i], prev.RemovedLabels[i+1:]...) - removedAndAdded = true - break - } - } - if !removedAndAdded { - prev.AddedLabels = append(prev.AddedLabels, cur.Label) - } - } - prev.CreatedUnix = cur.CreatedUnix - // remove the current comment since it has been combined to prev comment - issue.Comments = append(issue.Comments[:i], issue.Comments[i+1:]...) - i-- - } else { // if prev is not a label comment, start a new group - if cur.Content != "1" { - cur.RemovedLabels = append(cur.RemovedLabels, cur.Label) - } else { - cur.AddedLabels = append(cur.AddedLabels, cur.Label) - } - } - } - } -} - // get all teams that current user can mention func handleTeamMentions(ctx *context.Context) { if ctx.Doer == nil || !ctx.Repo.Owner.IsOrganization() { diff --git a/routers/web/repo/issue_test.go b/routers/web/repo/issue_test.go deleted file mode 100644 index d642c14b5f..0000000000 --- a/routers/web/repo/issue_test.go +++ /dev/null @@ -1,806 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package repo - -import ( - "testing" - - issues_model "code.gitea.io/gitea/models/issues" - org_model "code.gitea.io/gitea/models/organization" - user_model "code.gitea.io/gitea/models/user" - - "github.com/stretchr/testify/assert" -) - -func TestCombineLabelComments(t *testing.T) { - kases := []struct { - name string - beforeCombined []*issues_model.Comment - afterCombined []*issues_model.Comment - }{ - { - name: "kase 1", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - CreatedUnix: 0, - AddedLabels: []*issues_model.Label{}, - Label: &issues_model.Label{ - Name: "kind/bug", - }, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - }, - { - name: "kase 2", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 70, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - CreatedUnix: 0, - AddedLabels: []*issues_model.Label{ - { - Name: "kind/bug", - }, - }, - Label: &issues_model.Label{ - Name: "kind/bug", - }, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "", - CreatedUnix: 70, - RemovedLabels: []*issues_model.Label{ - { - Name: "kind/bug", - }, - }, - Label: &issues_model.Label{ - Name: "kind/bug", - }, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - }, - { - name: "kase 3", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 2, - Content: "", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - CreatedUnix: 0, - AddedLabels: []*issues_model.Label{ - { - Name: "kind/bug", - }, - }, - Label: &issues_model.Label{ - Name: "kind/bug", - }, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 2, - Content: "", - CreatedUnix: 0, - RemovedLabels: []*issues_model.Label{ - { - Name: "kind/bug", - }, - }, - Label: &issues_model.Label{ - Name: "kind/bug", - }, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - }, - { - name: "kase 4", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/backport", - }, - CreatedUnix: 10, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - CreatedUnix: 10, - AddedLabels: []*issues_model.Label{ - { - Name: "kind/bug", - }, - { - Name: "kind/backport", - }, - }, - Label: &issues_model.Label{ - Name: "kind/bug", - }, - }, - }, - }, - { - name: "kase 5", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 2, - Content: "testtest", - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - AddedLabels: []*issues_model.Label{ - { - Name: "kind/bug", - }, - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 2, - Content: "testtest", - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "", - RemovedLabels: []*issues_model.Label{ - { - Name: "kind/bug", - }, - }, - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - }, - }, - { - name: "kase 6", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "reviewed/confirmed", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/feature", - }, - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeLabel, - PosterID: 1, - Content: "1", - Label: &issues_model.Label{ - Name: "kind/bug", - }, - AddedLabels: []*issues_model.Label{ - { - Name: "reviewed/confirmed", - }, - { - Name: "kind/feature", - }, - }, - CreatedUnix: 0, - }, - }, - }, - } - - for _, kase := range kases { - t.Run(kase.name, func(t *testing.T) { - issue := issues_model.Issue{ - Comments: kase.beforeCombined, - } - combineLabelComments(&issue) - assert.EqualValues(t, kase.afterCombined, issue.Comments) - }) - } -} - -func TestCombineReviewRequests(t *testing.T) { - testCases := []struct { - name string - beforeCombined []*issues_model.Comment - afterCombined []*issues_model.Comment - }{ - { - name: "case 1", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - RemovedAssignee: true, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - AddedRequestReview: []issues_model.RequestReviewTarget{}, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - }, - }, - { - name: "case 2", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 2, - Name: "Ghost 2", - }, - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - AddedRequestReview: []issues_model.RequestReviewTarget{ - &RequestReviewTarget{ - user: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - &RequestReviewTarget{ - user: &user_model.User{ - ID: 2, - Name: "Ghost 2", - }, - }, - }, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - }, - }, - { - name: "case 3", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - RemovedAssignee: true, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - AddedRequestReview: []issues_model.RequestReviewTarget{ - &RequestReviewTarget{ - user: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - }, - RemovedRequestReview: []issues_model.RequestReviewTarget{ - &RequestReviewTarget{ - team: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - }, - }, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - }, - }, - { - name: "case 4", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - RemovedAssignee: true, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - AddedRequestReview: []issues_model.RequestReviewTarget{ - &RequestReviewTarget{ - user: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - }, - RemovedRequestReview: []issues_model.RequestReviewTarget{}, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - }, - }, - { - name: "case 5", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - RemovedAssignee: true, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - RemovedAssignee: true, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - AddedRequestReview: []issues_model.RequestReviewTarget{}, - RemovedRequestReview: []issues_model.RequestReviewTarget{}, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - }, - }, - { - name: "case 6", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - RemovedAssignee: true, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - RemovedAssignee: true, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - RemovedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{ - team: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - }}, - AddedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{ - user: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }}, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - { - Type: issues_model.CommentTypeComment, - PosterID: 1, - Content: "test", - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - AddedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{ - team: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - }}, - RemovedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{ - user: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }}, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - }, - }, - }, - { - name: "case 7", - beforeCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - CreatedUnix: 0, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - CreatedUnix: 61, - }, - }, - afterCombined: []*issues_model.Comment{ - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - AddedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{ - user: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }}, - Assignee: &user_model.User{ - ID: 1, - Name: "Ghost", - }, - }, - { - Type: issues_model.CommentTypeReviewRequest, - PosterID: 1, - CreatedUnix: 0, - RemovedRequestReview: []issues_model.RequestReviewTarget{&RequestReviewTarget{ - team: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - }}, - AssigneeTeam: &org_model.Team{ - ID: 1, - Name: "Team 1", - }, - }, - }, - }, - } - - for _, testCase := range testCases { - t.Run(testCase.name, func(t *testing.T) { - issue := issues_model.Issue{ - Comments: testCase.beforeCombined, - } - combineRequestReviewComments(&issue) - assert.EqualValues(t, testCase.afterCombined[0], issue.Comments[0]) - }) - } -} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 604efed737..bea517dfa2 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -12,7 +12,7 @@ 26 = DELETE_TIME_MANUAL, 27 = REVIEW_REQUEST, 28 = MERGE_PULL_REQUEST, 29 = PULL_PUSH_EVENT, 30 = PROJECT_CHANGED, 31 = PROJECT_BOARD_CHANGED 32 = DISMISSED_REVIEW, 33 = COMMENT_TYPE_CHANGE_ISSUE_REF, 34 = PR_SCHEDULE_TO_AUTO_MERGE, - 35 = CANCEL_SCHEDULED_AUTO_MERGE_PR, 36 = PIN_ISSUE, 37 = UNPIN_ISSUE --> + 35 = CANCEL_SCHEDULED_AUTO_MERGE_PR, 36 = PIN_ISSUE, 37 = UNPIN_ISSUE, 38 = ACTION_AGGREGATOR --> {{if eq .Type 0}}
{{if .OriginalAuthor}} @@ -524,7 +524,7 @@
{{else if eq .Type 27}} - {{if or .AddedRequestReview .RemovedRequestReview}} + {{if or .AddedRequestReview .RemovedRequestReview}}
{{svg "octicon-tag"}} {{template "shared/user/avatarlink" dict "user" .Poster}} @@ -540,7 +540,7 @@ {{end}}
- {{end}} + {{end}} {{else if and (eq .Type 29) (or (gt .CommitsNum 0) .IsForcePush)}} {{if and .Issue.IsClosed (gt .ID $.LatestCloseCommentID)}} @@ -676,6 +676,73 @@ {{else}}{{ctx.Locale.Tr "repo.issues.unpin_comment" $createdStr}}{{end}}
+ {{else if eq .Type 38}} +
+ {{svg "octicon-list-unordered" 16}} + {{template "shared/user/avatarlink" dict "user" .Poster}} + + + {{template "shared/user/authorlink" .Poster}} + {{$createdStr}} + +
    + + + {{if and .Aggregator.PrevClosed (not .Aggregator.IsClosed)}} +
  • + {{svg "octicon-dot-fill"}} + {{if .Issue.IsPull}} + {{ctx.Locale.Tr "repo.pulls.reopened_at" "" ""}} + {{else}} + {{ctx.Locale.Tr "repo.issues.reopened_at" "" ""}} + {{end}} +
  • + {{else if and (not .Aggregator.PrevClosed) .Aggregator.IsClosed}} + {{svg "octicon-circle-slash"}} +
  • + {{if .Issue.IsPull}} + {{ctx.Locale.Tr "repo.pulls.closed_at" "" ""}} + {{else}} + {{ctx.Locale.Tr "repo.issues.closed_at" "" ""}} + {{end}} +
  • + {{end}} + + + {{if or .AddedLabels .RemovedLabels}} +
  • + {{svg "octicon-tag" 20}} + {{if and .AddedLabels (not .RemovedLabels)}} + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) ""}} + {{else if and (not .AddedLabels) .RemovedLabels}} + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) ""}} + {{else}} + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) ""}} + {{end}} +
  • + {{end}} + + {{if or .AddedRequestReview .RemovedRequestReview}} +
  • + {{svg "octicon-tag" 20}} + + + {{if and (eq (len .RemovedRequestReview) 1) (eq (len .AddedRequestReview) 0) (eq ((index .RemovedRequestReview 0).ID) .PosterID) (eq ((index .RemovedRequestReview 0).Type) "user")}} + {{ctx.Locale.Tr "repo.issues.review.remove_review_request_self" ""}} + {{else if and .AddedRequestReview (not .RemovedRequestReview)}} + {{ctx.Locale.TrN (len .AddedRequestReview) "repo.issues.review.add_review_request" "repo.issues.review.add_review_requests" (RenderReviewRequest .AddedRequestReview) ""}} + {{else if and (not .AddedRequestReview) .RemovedRequestReview}} + {{ctx.Locale.TrN (len .RemovedRequestReview) "repo.issues.review.remove_review_request" "repo.issues.review.remove_review_requests" (RenderReviewRequest .RemovedRequestReview) ""}} + {{else}} + {{ctx.Locale.Tr "repo.issues.review.add_remove_review_requests" (RenderReviewRequest .AddedRequestReview) (RenderReviewRequest .RemovedRequestReview) ""}} + {{end}} + + +
  • + {{end}} +
+
+
{{end}} {{end}} {{end}} diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 46c6b70c29..1b962aacb6 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -759,7 +759,7 @@ td .commit-summary { .repository.view.issue .comment-list .timeline-item, .repository.view.issue .comment-list .timeline-item-group { - padding: 16px 0; + padding: 0.65rem 0; } .repository.view.issue .comment-list .timeline-item-group .timeline-item { @@ -842,6 +842,18 @@ td .commit-summary { margin-right: 0.25em; } +.repository.view.issue .comment-list .timeline-item .aggregated-actions .badge { + width: 20px; + height: 20px; + margin-top: 5px; + padding: 12px; +} + +.repository.view.issue .comment-list .timeline-item .aggregated-actions .badge .svg { + width: 20px; + height: 20px; +} + .singular-commit { display: flex; align-items: center; From a71f7fba39ead1ed4af43da0fa6ab4d2538bc3a6 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 6 Mar 2025 00:42:02 +0000 Subject: [PATCH 234/669] Update module github.com/urfave/cli/v2 to v2.27.6 (forgejo) (#7132) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c6060e0008..26be560f28 100644 --- a/go.mod +++ b/go.mod @@ -94,7 +94,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 github.com/ulikunitz/xz v0.5.12 - github.com/urfave/cli/v2 v2.27.5 + github.com/urfave/cli/v2 v2.27.6 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 github.com/yuin/goldmark v1.7.8 diff --git a/go.sum b/go.sum index 8b9c0d88d5..b8be9d1054 100644 --- a/go.sum +++ b/go.sum @@ -1412,8 +1412,8 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= -github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= +github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g= +github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= From 2029741b602067031ddca6b7b960ef8aee3465ef Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 6 Mar 2025 08:23:55 +0000 Subject: [PATCH 235/669] Update module golang.org/x/crypto to v0.36.0 (forgejo) (#7134) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7134 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 8 ++++---- go.sum | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 26be560f28..23282f5d9f 100644 --- a/go.mod +++ b/go.mod @@ -101,13 +101,13 @@ require ( github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc gitlab.com/gitlab-org/api/client-go v0.123.0 go.uber.org/mock v0.4.0 - golang.org/x/crypto v0.35.0 + golang.org/x/crypto v0.36.0 golang.org/x/image v0.23.0 golang.org/x/net v0.36.0 golang.org/x/oauth2 v0.27.0 - golang.org/x/sync v0.11.0 - golang.org/x/sys v0.30.0 - golang.org/x/text v0.22.0 + golang.org/x/sync v0.12.0 + golang.org/x/sys v0.31.0 + golang.org/x/text v0.23.0 google.golang.org/grpc v1.70.0 google.golang.org/protobuf v1.36.3 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df diff --git a/go.sum b/go.sum index b8be9d1054..70c0161c07 100644 --- a/go.sum +++ b/go.sum @@ -1507,8 +1507,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1693,8 +1693,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1786,8 +1786,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1802,8 +1802,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1824,8 +1824,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From e138458de83e1e941820f46d563a2a79fed905a2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 5 Mar 2025 18:40:13 +0100 Subject: [PATCH 236/669] feat: improve error handling of commit rendering - Simplify if-else expression to `NotFoundOrServerError`. - I cannot find an existing scenario where `Getdiff` returns an error and where it therefore should show a 404 error in the context of rendering a diff of a commit. So simply return it as an Internal Server Error, this also helps with debugging if an actual error occurs here (404 errors are only logged at the DEBUG level). - The first change is already covered under existing testing, the second change is not trivial to test. --- routers/web/repo/commit.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index c2436a1c59..857e34381e 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -315,11 +315,7 @@ func Diff(ctx *context.Context) { commit, err := gitRepo.GetCommit(commitID) if err != nil { - if git.IsErrNotExist(err) { - ctx.NotFound("Repo.GitRepo.GetCommit", err) - } else { - ctx.ServerError("Repo.GitRepo.GetCommit", err) - } + ctx.NotFoundOrServerError("gitRepo.GetCommit", git.IsErrNotExist, err) return } if len(commitID) != commit.ID.Type().FullLength() { @@ -343,7 +339,7 @@ func Diff(ctx *context.Context) { FileOnly: fileOnly, }, files...) if err != nil { - ctx.NotFound("GetDiff", err) + ctx.ServerError("GetDiff", err) return } From a03902959bc6d509bfa410ad86bd4a792fb6e8eb Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 5 Mar 2025 00:10:46 +0100 Subject: [PATCH 237/669] fix: correct logging if caller has generics - If the caller function has generics then `runtime.FuncForPC(...).Name()` will not show the generic types and instead collapse it to `[...]`. Remove this suffix from the function name. - This fixes an issue where the logging of functions that use generics such as `db.Find` to be logged as `]()` instead of `Find()`, as the last dot in `[...]` was being used as a cutoff point. - Unit test added. --- modules/log/logger_impl.go | 2 +- modules/log/logger_impl_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 modules/log/logger_impl_test.go diff --git a/modules/log/logger_impl.go b/modules/log/logger_impl.go index d38c6516ed..77e7edf6aa 100644 --- a/modules/log/logger_impl.go +++ b/modules/log/logger_impl.go @@ -191,7 +191,7 @@ func (l *LoggerImpl) Log(skip int, level Level, format string, logArgs ...any) { if ok { fn := runtime.FuncForPC(pc) if fn != nil { - event.Caller = fn.Name() + "()" + event.Caller = strings.TrimSuffix(fn.Name(), "[...]") + "()" } } event.Filename, event.Line = strings.TrimPrefix(filename, projectPackagePrefix), line diff --git a/modules/log/logger_impl_test.go b/modules/log/logger_impl_test.go new file mode 100644 index 0000000000..59276a83f4 --- /dev/null +++ b/modules/log/logger_impl_test.go @@ -0,0 +1,27 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package log + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func testGeneric[T any](log *LoggerImpl, t T) { + log.Log(0, INFO, "Just testing the logging of a generic function %v", t) +} + +func TestLog(t *testing.T) { + bufferWriter := NewEventWriterBuffer("test-buffer", WriterMode{ + Level: INFO, + }) + + logger := NewLoggerWithWriters(t.Context(), "test", bufferWriter) + + testGeneric(logger, "I'm the generic value!") + logger.Close() + + assert.Contains(t, bufferWriter.Buffer.String(), ".../logger_impl_test.go:13:testGeneric() [I] Just testing the logging of a generic function I'm the generic value!") +} From 4053b2e75b2e8ea7625d2bc3a445ad09a400dd8a Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 6 Mar 2025 11:43:36 +0000 Subject: [PATCH 238/669] Update module golang.org/x/image to v0.25.0 (forgejo) (#7135) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7135 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 23282f5d9f..0616b43465 100644 --- a/go.mod +++ b/go.mod @@ -102,7 +102,7 @@ require ( gitlab.com/gitlab-org/api/client-go v0.123.0 go.uber.org/mock v0.4.0 golang.org/x/crypto v0.36.0 - golang.org/x/image v0.23.0 + golang.org/x/image v0.25.0 golang.org/x/net v0.36.0 golang.org/x/oauth2 v0.27.0 golang.org/x/sync v0.12.0 diff --git a/go.sum b/go.sum index 70c0161c07..9b80887a74 100644 --- a/go.sum +++ b/go.sum @@ -1539,8 +1539,8 @@ golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeap golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= -golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= From c04249cf9e87c013f2c10b35fb8d1ccf259099ff Mon Sep 17 00:00:00 2001 From: christopher-besch Date: Thu, 6 Mar 2025 12:49:24 +0000 Subject: [PATCH 239/669] interpret Precedence: auto_reply as an auto reply (#7137) Some email clients like to be special and only set the "Precedence" header to "auto_reply" when sending automatic replies. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7137 Reviewed-by: Gusted Reviewed-by: Otto Co-authored-by: christopher-besch Co-committed-by: christopher-besch --- services/mailer/incoming/incoming.go | 4 ++++ services/mailer/incoming/incoming_test.go | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/services/mailer/incoming/incoming.go b/services/mailer/incoming/incoming.go index 249dac66cd..092f738cd9 100644 --- a/services/mailer/incoming/incoming.go +++ b/services/mailer/incoming/incoming.go @@ -297,6 +297,10 @@ func isAutomaticReply(env *enmime.Envelope) bool { if autoReply == "yes" { return true } + precedence := env.GetHeader("Precedence") + if precedence == "auto_reply" { + return true + } autoRespond := env.GetHeader("X-Autorespond") return autoRespond != "" } diff --git a/services/mailer/incoming/incoming_test.go b/services/mailer/incoming/incoming_test.go index 6101bc7e32..fe11c9e5c6 100644 --- a/services/mailer/incoming/incoming_test.go +++ b/services/mailer/incoming/incoming_test.go @@ -65,6 +65,12 @@ func TestIsAutomaticReply(t *testing.T) { }, Expected: true, }, + { + Headers: map[string]string{ + "Precedence": "auto_reply", + }, + Expected: true, + }, } for _, c := range cases { From be2839f6d09732ab309412cb07d9681649e9b5d6 Mon Sep 17 00:00:00 2001 From: Kerwin Bryant Date: Tue, 25 Feb 2025 02:24:56 +0800 Subject: [PATCH 240/669] Add No Results Prompt Message on Issue List Page (#33699) (cherry picked from commit 2cd2ae07a7ff267eaa9d2b984d015c58edcb0647) --- options/locale/locale_en-US.ini | 4 +++- templates/shared/issuelist.tmpl | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5b1e04566c..af7142e7e5 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1540,6 +1540,8 @@ issues.filter_milestones = Filter Milestone issues.filter_projects = Filter Project issues.filter_labels = Filter Label issues.filter_reviewers = Filter Reviewer +issues.filter_no_results = No results +issues.filter_no_results_placeholder = Try adjusting your search filters. issues.new = New issue issues.new.title_empty = Title cannot be empty issues.new.labels = Labels @@ -3963,4 +3965,4 @@ filepreview.lines = Lines %[1]d to %[2]d in %[3]s filepreview.truncated = Preview has been truncated [translation_meta] -test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :) +test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :) \ No newline at end of file diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 5a45ba5a71..53575d82b2 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -145,6 +145,11 @@
+ {{else}} +
+

{{ctx.Locale.Tr "repo.issues.filter_no_results"}}

+

{{ctx.Locale.Tr "repo.issues.filter_no_results_placeholder"}}

+
{{end}} {{if .IssueIndexerUnavailable}}
From 8f729647b6c35773765a6ff22e45152496545f49 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 25 Feb 2025 15:27:23 -0800 Subject: [PATCH 241/669] Fix inconsistent closed issue list icon (#33722) Fixe #33718 Before ![image](https://github.com/user-attachments/assets/2c77e249-a118-4471-8c63-ead4fe0f6336) After ![image](https://github.com/user-attachments/assets/c082eba8-5b21-4814-b091-c725ca46ccf3) (cherry picked from commit 74c8e95e87300c74453e6aedd8a41352dbac02f9) --- templates/repo/issue/openclose.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/issue/openclose.tmpl b/templates/repo/issue/openclose.tmpl index efe4fa6756..25f73a857d 100644 --- a/templates/repo/issue/openclose.tmpl +++ b/templates/repo/issue/openclose.tmpl @@ -10,7 +10,7 @@ {{ctx.Locale.PrettyNumber .OpenCount}} {{ctx.Locale.Tr "repo.issues.open_title"}} - {{svg "octicon-check" 16 "tw-mr-2"}} + {{svg "octicon-issue-closed" 16 "tw-mr-2"}} {{ctx.Locale.PrettyNumber .ClosedCount}} {{ctx.Locale.Tr "repo.issues.closed_title"}} {{if not .PageIsMilestones}} From 8929357d639492b11b593ae51af4616926f7563c Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 27 Feb 2025 20:05:28 +0100 Subject: [PATCH 242/669] Remove superflous tw-content-center (#33741) (cherry picked from commit 8362a4155929007b8d02b63a2e657557edb08fb9) --- templates/base/paginate.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/base/paginate.tmpl b/templates/base/paginate.tmpl index 2ca72f6a34..253892c009 100644 --- a/templates/base/paginate.tmpl +++ b/templates/base/paginate.tmpl @@ -17,7 +17,7 @@ {{if eq .Num -1}} ... {{else}} - {{.Num}} + {{.Num}} {{end}} {{end}} From ca38d9463fdb95a6fd71f7db1cf8976bfac185c7 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 28 Feb 2025 08:29:21 -0800 Subject: [PATCH 243/669] Add composor source field (#33502) Fix #33066 (cherry picked from commit aca21549f285255f0d78f90f1f15f0c6f9396761) --- models/packages/descriptor.go | 9 +++-- routers/api/packages/composer/api.go | 22 ++++++++++-- services/packages/package_update.go | 17 ++++----- .../integration/api_packages_composer_test.go | 35 +++++++++++++++++++ 4 files changed, 70 insertions(+), 13 deletions(-) diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index 32c871558a..31ab84fd68 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -110,9 +110,12 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc if err != nil { return nil, err } - repository, err := repo_model.GetRepositoryByID(ctx, p.RepoID) - if err != nil && !repo_model.IsErrRepoNotExist(err) { - return nil, err + var repository *repo_model.Repository + if p.RepoID > 0 { + repository, err = repo_model.GetRepositoryByID(ctx, p.RepoID) + if err != nil && !repo_model.IsErrRepoNotExist(err) { + return nil, err + } } creator, err := user_model.GetUserByID(ctx, pv.CreatorID) if err != nil { diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go index a3bcf80417..a3ea2c2f9a 100644 --- a/routers/api/packages/composer/api.go +++ b/routers/api/packages/composer/api.go @@ -66,6 +66,7 @@ type PackageMetadataResponse struct { } // PackageVersionMetadata contains package metadata +// https://getcomposer.org/doc/05-repositories.md#package type PackageVersionMetadata struct { *composer_module.Metadata Name string `json:"name"` @@ -73,6 +74,7 @@ type PackageVersionMetadata struct { Type string `json:"type"` Created time.Time `json:"time"` Dist Dist `json:"dist"` + Source Source `json:"source"` } // Dist contains package download information @@ -82,6 +84,13 @@ type Dist struct { Checksum string `json:"shasum"` } +// Source contains package source information +type Source struct { + URL string `json:"url"` + Type string `json:"type"` + Reference string `json:"reference"` +} + func createPackageMetadataResponse(registryURL string, pds []*packages_model.PackageDescriptor) *PackageMetadataResponse { versions := make([]*PackageVersionMetadata, 0, len(pds)) @@ -94,7 +103,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac } } - versions = append(versions, &PackageVersionMetadata{ + pkg := PackageVersionMetadata{ Name: pd.Package.Name, Version: pd.Version.Version, Type: packageType, @@ -105,7 +114,16 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac URL: fmt.Sprintf("%s/files/%s/%s/%s", registryURL, url.PathEscape(pd.Package.LowerName), url.PathEscape(pd.Version.LowerVersion), url.PathEscape(pd.Files[0].File.LowerName)), Checksum: pd.Files[0].Blob.HashSHA1, }, - }) + } + if pd.Repository != nil { + pkg.Source = Source{ + URL: pd.Repository.HTMLURL(), + Type: "git", + Reference: pd.Version.Version, + } + } + + versions = append(versions, &pkg) } return &PackageMetadataResponse{ diff --git a/services/packages/package_update.go b/services/packages/package_update.go index 8d851fac53..4a22ee7a62 100644 --- a/services/packages/package_update.go +++ b/services/packages/package_update.go @@ -44,16 +44,17 @@ func UnlinkFromRepository(ctx context.Context, pkg *packages_model.Package, doer } repo, err := repo_model.GetRepositoryByID(ctx, pkg.RepoID) - if err != nil { + if err != nil && !repo_model.IsErrRepoNotExist(err) { return fmt.Errorf("error getting repository %d: %w", pkg.RepoID, err) } - - perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) - if err != nil { - return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) - } - if !perms.CanWrite(unit.TypePackages) { - return util.ErrPermissionDenied + if err == nil { + perms, err := access_model.GetUserRepoPermission(ctx, repo, doer) + if err != nil { + return fmt.Errorf("error getting permissions for user %d on repository %d: %w", doer.ID, repo.ID, err) + } + if !perms.CanWrite(unit.TypePackages) { + return util.ErrPermissionDenied + } } user, err := user_model.GetUserByID(ctx, pkg.OwnerID) diff --git a/tests/integration/api_packages_composer_test.go b/tests/integration/api_packages_composer_test.go index 9d25cc4d64..774676f8e2 100644 --- a/tests/integration/api_packages_composer_test.go +++ b/tests/integration/api_packages_composer_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/packages" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" composer_module "code.gitea.io/gitea/modules/packages/composer" @@ -218,5 +219,39 @@ func TestPackageComposer(t *testing.T) { assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum) assert.Len(t, pkgs[0].Bin, 1) assert.Equal(t, packageBin, pkgs[0].Bin[0]) + + // Test package linked to repository + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + userPkgs, err := packages.GetPackagesByType(db.DefaultContext, user.ID, packages.TypeComposer) + require.NoError(t, err) + assert.Len(t, userPkgs, 1) + assert.EqualValues(t, 0, userPkgs[0].RepoID) + + err = packages.SetRepositoryLink(db.DefaultContext, userPkgs[0].ID, repo1.ID) + require.NoError(t, err) + + req = NewRequest(t, "GET", fmt.Sprintf("%s/p2/%s/%s.json", url, vendorName, projectName)). + AddBasicAuth(user.Name) + resp = MakeRequest(t, req, http.StatusOK) + + result = composer.PackageMetadataResponse{} + DecodeJSON(t, resp, &result) + + assert.Contains(t, result.Packages, packageName) + pkgs = result.Packages[packageName] + assert.Len(t, pkgs, 1) + assert.Equal(t, packageName, pkgs[0].Name) + assert.Equal(t, packageVersion, pkgs[0].Version) + assert.Equal(t, packageType, pkgs[0].Type) + assert.Equal(t, packageDescription, pkgs[0].Description) + assert.Len(t, pkgs[0].Authors, 1) + assert.Equal(t, packageAuthor, pkgs[0].Authors[0].Name) + assert.Equal(t, "zip", pkgs[0].Dist.Type) + assert.Equal(t, "4f5fa464c3cb808a1df191dbf6cb75363f8b7072", pkgs[0].Dist.Checksum) + assert.Len(t, pkgs[0].Bin, 1) + assert.Equal(t, packageBin, pkgs[0].Bin[0]) + assert.Equal(t, repo1.HTMLURL(), pkgs[0].Source.URL) + assert.Equal(t, "git", pkgs[0].Source.Type) + assert.Equal(t, packageVersion, pkgs[0].Source.Reference) }) } From d3f62c9e8dafa5de744ba2339424c7f2bf046b59 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 6 Mar 2025 23:26:08 +0000 Subject: [PATCH 244/669] fix: consider public issues for project boards (#7143) - The security patch of forgejo/forgejo#6843 fixed the issue where project boards loaded all issues without considering if the doer actually had permission to view that issue. Within that patch the call to `Issues` was modified to include this permission checking. - The query being generated was not entirely correct. Issues in public repositories weren't considered correctly (partly the fault of not setting `AllPublic` unconditionally) in the cause an authenticated user loaded the project. - This is now fixed by setting `AllPublic` unconditionally and subsequently fixing the `Issue` function to ensure that the combination of setting `AllPublic` and `User` generates the correct query, by combining the permission check and issues in public repositories as one `AND` query. - Added unit testing. - Added integration testing. - Resolves Codeberg/Community#1809 - Regression of https://codeberg.org/forgejo/forgejo/pulls/6843 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7143 Reviewed-by: Otto Co-authored-by: Gusted Co-committed-by: Gusted --- .../fixtures/PrivateIssueProjects/project.yml | 4 +- .../PrivateIssueProjects/project_issue.yml | 12 ++++++ models/issues/issue_project.go | 3 +- models/issues/issue_project_test.go | 39 ++++++++++++++----- models/issues/issue_search.go | 22 ++++++++--- tests/integration/private_project_test.go | 23 +++++++---- 6 files changed, 76 insertions(+), 27 deletions(-) diff --git a/models/fixtures/PrivateIssueProjects/project.yml b/models/fixtures/PrivateIssueProjects/project.yml index cf63e4e413..8950b33606 100644 --- a/models/fixtures/PrivateIssueProjects/project.yml +++ b/models/fixtures/PrivateIssueProjects/project.yml @@ -1,6 +1,6 @@ - id: 1001 - title: Org project that contains private issues + title: Org project that contains private and public issues owner_id: 3 repo_id: 0 is_closed: false @@ -12,7 +12,7 @@ - id: 1002 - title: User project that contains private issues + title: User project that contains private and public issues owner_id: 2 repo_id: 0 is_closed: false diff --git a/models/fixtures/PrivateIssueProjects/project_issue.yml b/models/fixtures/PrivateIssueProjects/project_issue.yml index 222b2e5f71..0245fb47f3 100644 --- a/models/fixtures/PrivateIssueProjects/project_issue.yml +++ b/models/fixtures/PrivateIssueProjects/project_issue.yml @@ -9,3 +9,15 @@ issue_id: 7 project_id: 1002 project_board_id: 1002 + +- + id: 1003 + issue_id: 16 + project_id: 1001 + project_board_id: 1001 + +- + id: 1004 + issue_id: 1 + project_id: 1002 + project_board_id: 1002 diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index f606b713cf..697ef7fad6 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -56,12 +56,11 @@ func LoadIssuesFromColumn(ctx context.Context, b *project_model.Column, doer *us ProjectID: b.ProjectID, SortType: "project-column-sorting", IsClosed: isClosed, + AllPublic: true, } if doer != nil { issueOpts.User = doer issueOpts.Org = org - } else { - issueOpts.AllPublic = true } issueList, err := Issues(ctx, issueOpts) diff --git a/models/issues/issue_project_test.go b/models/issues/issue_project_test.go index 6ebc803722..e28b52128e 100644 --- a/models/issues/issue_project_test.go +++ b/models/issues/issue_project_test.go @@ -33,12 +33,13 @@ func TestPrivateIssueProjects(t *testing.T) { defer tests.PrintCurrentTest(t)() issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user2, org, optional.None[bool]()) require.NoError(t, err) - assert.Len(t, issueList, 1) - assert.EqualValues(t, 6, issueList[0].ID) + assert.Len(t, issueList, 2) + assert.EqualValues(t, 16, issueList[0].ID) + assert.EqualValues(t, 6, issueList[1].ID) issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.EqualValues(t, 2, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(true)) require.NoError(t, err) @@ -46,18 +47,27 @@ func TestPrivateIssueProjects(t *testing.T) { issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, user2, org, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.EqualValues(t, 2, issuesNum) }) t.Run("Anonymous user", func(t *testing.T) { defer tests.PrintCurrentTest(t)() issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, nil, org, optional.None[bool]()) require.NoError(t, err) - assert.Empty(t, issueList) + assert.Len(t, issueList, 1) + assert.EqualValues(t, 16, issueList[0].ID) issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.None[bool]()) require.NoError(t, err) + assert.EqualValues(t, 1, issuesNum) + + issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.Some(true)) + require.NoError(t, err) assert.EqualValues(t, 0, issuesNum) + + issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, orgProject, nil, org, optional.Some(false)) + require.NoError(t, err) + assert.EqualValues(t, 1, issuesNum) }) }) @@ -69,12 +79,13 @@ func TestPrivateIssueProjects(t *testing.T) { defer tests.PrintCurrentTest(t)() issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, user2, nil, optional.None[bool]()) require.NoError(t, err) - assert.Len(t, issueList, 1) + assert.Len(t, issueList, 2) assert.EqualValues(t, 7, issueList[0].ID) + assert.EqualValues(t, 1, issueList[1].ID) issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.None[bool]()) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.EqualValues(t, 2, issuesNum) issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(true)) require.NoError(t, err) @@ -82,19 +93,27 @@ func TestPrivateIssueProjects(t *testing.T) { issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, user2, nil, optional.Some(false)) require.NoError(t, err) - assert.EqualValues(t, 1, issuesNum) + assert.EqualValues(t, 2, issuesNum) }) t.Run("Anonymous user", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - issueList, err := issues.LoadIssuesFromColumn(db.DefaultContext, column, nil, nil, optional.None[bool]()) require.NoError(t, err) - assert.Empty(t, issueList) + assert.Len(t, issueList, 1) + assert.EqualValues(t, 1, issueList[0].ID) issuesNum, err := issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.None[bool]()) require.NoError(t, err) + assert.EqualValues(t, 1, issuesNum) + + issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.Some(true)) + require.NoError(t, err) assert.EqualValues(t, 0, issuesNum) + + issuesNum, err = issues.NumIssuesInProject(db.DefaultContext, userProject, nil, nil, optional.Some(false)) + require.NoError(t, err) + assert.EqualValues(t, 1, issuesNum) }) }) } diff --git a/models/issues/issue_search.go b/models/issues/issue_search.go index e9f116bfc6..56c219af39 100644 --- a/models/issues/issue_search.go +++ b/models/issues/issue_search.go @@ -49,9 +49,13 @@ type IssuesOptions struct { //nolint // prioritize issues from this repo PriorityRepoID int64 IsArchived optional.Option[bool] - Org *organization.Organization // issues permission scope - Team *organization.Team // issues permission scope - User *user_model.User // issues permission scope + + // If combined with AllPublic, then private as well as public issues + // that matches the criteria will be returned, if AllPublic is false + // only the private issues will be returned. + Org *organization.Organization // issues permission scope + Team *organization.Team // issues permission scope + User *user_model.User // issues permission scope } // applySorts sort an issues-related session based on the provided @@ -196,7 +200,8 @@ func applyRepoConditions(sess *xorm.Session, opts *IssuesOptions) { } else if len(opts.RepoIDs) > 1 { opts.RepoCond = builder.In("issue.repo_id", opts.RepoIDs) } - if opts.AllPublic { + // If permission scoping is set, then we set this condition at a later stage. + if opts.AllPublic && opts.User == nil { if opts.RepoCond == nil { opts.RepoCond = builder.NewCond() } @@ -268,7 +273,14 @@ func applyConditions(sess *xorm.Session, opts *IssuesOptions) { applyLabelsCondition(sess, opts) if opts.User != nil { - sess.And(issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value())) + cond := issuePullAccessibleRepoCond("issue.repo_id", opts.User.ID, opts.Org, opts.Team, opts.IsPull.Value()) + // If AllPublic was set, then also consider all issues in public + // repositories in addition to the private repositories the user has access + // to. + if opts.AllPublic { + cond = cond.Or(builder.In("issue.repo_id", builder.Select("id").From("repository").Where(builder.Eq{"is_private": false}))) + } + sess.And(cond) } } diff --git a/tests/integration/private_project_test.go b/tests/integration/private_project_test.go index 1a4adb4366..663c1c1e75 100644 --- a/tests/integration/private_project_test.go +++ b/tests/integration/private_project_test.go @@ -24,7 +24,7 @@ func TestPrivateIssueProject(t *testing.T) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) sess := loginUser(t, user2.Name) - test := func(t *testing.T, sess *TestSession, username string, projectID int64, hasAccess bool) { + test := func(t *testing.T, sess *TestSession, username string, projectID int64, hasAccess bool, publicIssueHref ...string) { t.Helper() defer tests.PrintCurrentTest(t, 1)() @@ -35,9 +35,9 @@ func TestPrivateIssueProject(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) openCloseStats := htmlDoc.Find(".milestone-toolbar .group").First().Text() if hasAccess { - assert.Contains(t, openCloseStats, "1\u00a0Open") + assert.Contains(t, openCloseStats, "2\u00a0Open") } else { - assert.Contains(t, openCloseStats, "0\u00a0Open") + assert.Contains(t, openCloseStats, "1\u00a0Open") } assert.Contains(t, openCloseStats, "0\u00a0Closed") @@ -46,14 +46,21 @@ func TestPrivateIssueProject(t *testing.T) { resp = sess.MakeRequest(t, req, http.StatusOK) htmlDoc = NewHTMLParser(t, resp.Body) - htmlDoc.AssertElement(t, ".project-column .issue-card", hasAccess) + issueCardsLen := htmlDoc.Find(".project-column .issue-card").Length() + if hasAccess { + assert.EqualValues(t, 2, issueCardsLen) + } else { + assert.EqualValues(t, 1, issueCardsLen) + // Ensure that the public issue is shown. + assert.EqualValues(t, publicIssueHref[0], htmlDoc.Find(".project-column .issue-card .issue-card-title").AttrOr("href", "")) + } // And that the issue count is correct. issueCount := strings.TrimSpace(htmlDoc.Find(".project-column-issue-count").Text()) if hasAccess { - assert.EqualValues(t, "1", issueCount) + assert.EqualValues(t, "2", issueCount) } else { - assert.EqualValues(t, "0", issueCount) + assert.EqualValues(t, "1", issueCount) } } @@ -66,7 +73,7 @@ func TestPrivateIssueProject(t *testing.T) { }) t.Run("Anonymous user", func(t *testing.T) { - test(t, emptyTestSession(t), org.Name, orgProject.ID, false) + test(t, emptyTestSession(t), org.Name, orgProject.ID, false, "/org3/repo21/issues/1") }) }) @@ -78,7 +85,7 @@ func TestPrivateIssueProject(t *testing.T) { }) t.Run("Anonymous user", func(t *testing.T) { - test(t, emptyTestSession(t), user2.Name, userProject.ID, false) + test(t, emptyTestSession(t), user2.Name, userProject.ID, false, "/user2/repo1/issues/1") }) }) } From f5a4fb8ecf921635b3bf8108c5a3cdc2ea9226e4 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 7 Mar 2025 00:45:40 +0000 Subject: [PATCH 245/669] Update module github.com/caddyserver/certmagic to v0.22.0 (forgejo) (#7146) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7146 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 0616b43465..54d2136b90 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.4.4 github.com/buildkite/terminal-to-html/v3 v3.16.6 - github.com/caddyserver/certmagic v0.21.7 + github.com/caddyserver/certmagic v0.22.0 github.com/chi-middleware/proxy v1.1.1 github.com/djherbis/buffer v1.2.0 github.com/djherbis/nio/v3 v3.0.1 @@ -68,7 +68,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/klauspost/compress v1.17.11 - github.com/klauspost/cpuid/v2 v2.2.9 + github.com/klauspost/cpuid/v2 v2.2.10 github.com/lib/pq v1.10.9 github.com/markbates/goth v1.80.0 github.com/mattn/go-isatty v0.0.20 @@ -103,7 +103,7 @@ require ( go.uber.org/mock v0.4.0 golang.org/x/crypto v0.36.0 golang.org/x/image v0.25.0 - golang.org/x/net v0.36.0 + golang.org/x/net v0.37.0 golang.org/x/oauth2 v0.27.0 golang.org/x/sync v0.12.0 golang.org/x/sys v0.31.0 @@ -216,13 +216,13 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/libdns/libdns v0.2.2 // indirect + github.com/libdns/libdns v0.2.3 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/markbates/going v1.0.3 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/mholt/acmez/v3 v3.0.1 // indirect - github.com/miekg/dns v1.1.62 // indirect + github.com/mholt/acmez/v3 v3.1.0 // indirect + github.com/miekg/dns v1.1.63 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -268,9 +268,9 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect - golang.org/x/mod v0.22.0 // indirect + golang.org/x/mod v0.24.0 // indirect golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.28.0 // indirect + golang.org/x/tools v0.31.0 // indirect google.golang.org/api v0.203.0 // indirect google.golang.org/genproto v0.0.0-20241015192408-796eee8c2d53 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect diff --git a/go.sum b/go.sum index 9b80887a74..642469762f 100644 --- a/go.sum +++ b/go.sum @@ -769,8 +769,8 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/buildkite/terminal-to-html/v3 v3.16.6 h1:QKHWPjAnKQnV1hVG/Nb2TwYDr4pliSbvCQDENk8EaJo= github.com/buildkite/terminal-to-html/v3 v3.16.6/go.mod h1:PgzeBymbRFC8I2m46Sci3S18AbwonEgpaz3TGhD7EPs= -github.com/caddyserver/certmagic v0.21.7 h1:66KJioPFJwttL43KYSWk7ErSmE6LfaJgCQuhm8Sg6fg= -github.com/caddyserver/certmagic v0.21.7/go.mod h1:LCPG3WLxcnjVKl/xpjzM0gqh0knrKKKiO5WVttX2eEI= +github.com/caddyserver/certmagic v0.22.0 h1:hi2skv2jouUw9uQUEyYSTTmqPZPHgf61dOANSIVCLOw= +github.com/caddyserver/certmagic v0.22.0/go.mod h1:Vc0msarAPhOagbDc/SU6M2zbzdwVuZ0lkTh2EqtH4vs= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -1205,8 +1205,8 @@ github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= +github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -1225,8 +1225,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s= -github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= +github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8= +github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI= github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0/go.mod h1:JEfTc3+2DF9Z4PXhLLvXL42zexJyh8rIq3OzUj/0rAk= github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= @@ -1253,12 +1253,12 @@ github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBW github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/meilisearch/meilisearch-go v0.31.0 h1:yZRhY1qJqdH8h6GFZALGtkDLyj8f9v5aJpsNMyrUmnY= github.com/meilisearch/meilisearch-go v0.31.0/go.mod h1:aNtyuwurDg/ggxQIcKqWH6G9g2ptc8GyY7PLY4zMn/g= -github.com/mholt/acmez/v3 v3.0.1 h1:4PcjKjaySlgXK857aTfDuRbmnM5gb3Ruz3tvoSJAUp8= -github.com/mholt/acmez/v3 v3.0.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/mholt/acmez/v3 v3.1.0 h1:RlOx2SSZ8dIAM5GfkMe8TdaxjjkiHTGorlMUt8GeMzg= +github.com/mholt/acmez/v3 v3.1.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= -github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= -github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= @@ -1573,8 +1573,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1640,8 +1640,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1898,8 +1898,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From c5691db8e7b5f102719bf772583126f58f843806 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 7 Mar 2025 10:27:10 +0000 Subject: [PATCH 246/669] Update module golang.org/x/tools/cmd/deadcode to v0.31.0 (forgejo) (#7150) Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 86b1771d01..4f88b2b0a5 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renova XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go -DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.30.0 # renovate: datasource=go +DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.31.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.18.1 # renovate: datasource=go RENOVATE_NPM_PACKAGE ?= renovate@39.185.0 # renovate: datasource=docker packageName=data.forgejo.org/renovate/renovate From 8ab32172c80eaafa31f7e065e376d07027b0b01c Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 7 Mar 2025 10:27:51 +0000 Subject: [PATCH 247/669] Update module golang.org/x/oauth2 to v0.28.0 (forgejo) (#7149) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7149 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 54d2136b90..5a97bfbce2 100644 --- a/go.mod +++ b/go.mod @@ -104,7 +104,7 @@ require ( golang.org/x/crypto v0.36.0 golang.org/x/image v0.25.0 golang.org/x/net v0.37.0 - golang.org/x/oauth2 v0.27.0 + golang.org/x/oauth2 v0.28.0 golang.org/x/sync v0.12.0 golang.org/x/sys v0.31.0 golang.org/x/text v0.23.0 diff --git a/go.sum b/go.sum index 642469762f..6405ccd3e8 100644 --- a/go.sum +++ b/go.sum @@ -1671,8 +1671,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From e5f9557d37902586d85e0bd077d484eb8845e0c6 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Fri, 7 Mar 2025 14:26:37 +0000 Subject: [PATCH 248/669] i18n: update of translations from Codeberg Translate (#7066) Co-authored-by: lordwektabyte Co-authored-by: Marti Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: alanmena Co-authored-by: Juno Takano Co-authored-by: Benedikt Straub Co-authored-by: Edgarsons Co-authored-by: Wuzzy Co-authored-by: justbispo Co-authored-by: Kita Ikuyo Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: kwoot Co-authored-by: SomeTr Co-authored-by: monty24 Co-authored-by: Sampo Harjula Co-authored-by: tacaly Co-authored-by: artnay Co-authored-by: Outbreak2096 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7066 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_ca.ini | 19 ++- options/locale/locale_cs-CZ.ini | 8 +- options/locale/locale_da.ini | 81 ++++++++++- options/locale/locale_de-DE.ini | 34 ++++- options/locale/locale_es-ES.ini | 8 +- options/locale/locale_fa-IR.ini | 27 ++-- options/locale/locale_fi-FI.ini | 30 ++++- options/locale/locale_fil.ini | 138 +++++++++++-------- options/locale/locale_ga-IE.ini | 4 +- options/locale/locale_gl.ini | 28 +++- options/locale/locale_lv-LV.ini | 186 +++++++++++++++----------- options/locale/locale_nds.ini | 12 +- options/locale/locale_nl-NL.ini | 76 +++++++---- options/locale/locale_pt-BR.ini | 56 ++++++-- options/locale/locale_pt-PT.ini | 48 +++++-- options/locale/locale_ru-RU.ini | 34 ++++- options/locale/locale_sv-SE.ini | 2 +- options/locale/locale_uk-UA.ini | 64 ++++++--- options/locale/locale_zh-CN.ini | 10 +- options/locale_next/locale_cs-CZ.json | 26 ++-- options/locale_next/locale_da.json | 16 ++- options/locale_next/locale_de-DE.json | 23 ++-- options/locale_next/locale_fa-IR.json | 20 ++- options/locale_next/locale_lv-LV.json | 26 ++-- options/locale_next/locale_nds.json | 24 ++-- options/locale_next/locale_nl-NL.json | 24 ++-- options/locale_next/locale_pt-BR.json | 26 ++-- options/locale_next/locale_pt-PT.json | 25 ++-- options/locale_next/locale_ru-RU.json | 26 ++-- options/locale_next/locale_uk-UA.json | 26 ++-- options/locale_next/locale_zh-CN.json | 16 ++- 31 files changed, 809 insertions(+), 334 deletions(-) diff --git a/options/locale/locale_ca.ini b/options/locale/locale_ca.ini index 4231a68e8a..9cb7d5e50c 100644 --- a/options/locale/locale_ca.ini +++ b/options/locale/locale_ca.ini @@ -115,7 +115,7 @@ write = Escriure preview = Previsualitzar loading = Carregant… error = Error -error404 = La pàgina a la que estàs intentant arribar no existeix o no estàs autoritzat a veure-la. +error404 = La pàgina a la qual estàs intentant arribar no existeix, ha sigut eliminada o no estàs autoritzat a veure-la. go_back = Tornar Enrere invalid_data = Dades invalides: %v unknown = Desconegut @@ -173,7 +173,7 @@ pull_kind = Cerca "pulls"... exact = Exacte exact_tooltip = Inclou només resultats que són exactament el terme de cerca issue_kind = Cerca problemes... -regexp = Expressió regular +regexp = RegExp regexp_tooltip = Interpreta el terme de cerca com una expressió regular [heatmap] @@ -213,7 +213,7 @@ reinstall_error = Estas intentant instaŀlar sobre una base de dades existent de reinstall_confirm_message = Reinstaŀlar amb una base de dades existent de Forgejo pot causar diferents problemes. En la majoria de casos, s'hauria d'utilitzar l'"app.ini" existent per executar Forgejo. Si saps el que estàs fent, confirma el seguent: no_admin_and_disable_registration = No pot deshabilitar l'autoregistre d'usuaris sense crear un compte d'administrador. err_admin_name_is_reserved = El nom d'usuari "Administrador" no es vàlid: està reservat -smtp_addr = Hoste SMPT +smtp_addr = Hoste SMTP smtp_port = Port SMPT smtp_from = Enviar correu com a mailer_user = Nom d'usuari SMTP @@ -410,6 +410,9 @@ oauth_signin_tab = Vincular a un compte existent oauth.signin.error = Hi ha hagut un error processant la sol·licitud d'autorització. Si persisteix, poseu-vos en contacte amb l'administrador del lloc. disable_forgot_password_mail_admin = La recuperació de comptes només està disponible quan s'ha configurat el correu electrònic. Si us plau, configureu el correu electrònic per a habilitar la recuperació de comptes. non_local_account = Els usuaris no locals no poden actualitzar la seva contrasenya mitjançant l'interfície web de Forgejo +openid_register_desc = No s'ha reconegut la URI OpenID. Vinculeu-la amb un compte nou aquí. +openid_connect_desc = No s'ha reconegut la URI OpenID. Vinculeu-la amb un compte nou aquí. +sign_in_openid = Accediu amb OpenID [editor] buttons.indent.tooltip = Aniua els elements un nivell @@ -434,6 +437,10 @@ table_modal.placeholder.header = Capçalera table_modal.placeholder.content = Contingut table_modal.label.rows = Files table_modal.label.columns = Columnes +link_modal.header = Afegeix un enllaç +link_modal.url = URL +link_modal.description = Descripció +link_modal.paste_reminder = Pista: Amb un enllaç en el teu porta-retalls, pots enganxar-la directament a l'editor per a crear un enllaç. [home] my_orgs = Organitzacions @@ -472,4 +479,8 @@ reply = o responeu directament a aquest correu activate_account.text_1 = Hola %[1]s, gràcies per registrar-te a %[2]s! register_notify = Benvinguts a %s admin.new_user.text = Si us plau, cliqueu aui per administrar aquest usuari des del panell d'administració. -admin.new_user.user_info = Informació d'usuari \ No newline at end of file +admin.new_user.user_info = Informació d'usuari +admin.new_user.subject = Nou usuari %s s'acaba d'enregistrar +activate_email.text = Si us plau, cliqueu el següent enllaç per verificar la vostra adreça de correu electrònic en %s +activate_email = Verifica la teva adreça de correu electrònic +activate_account.text_2 = Si us plau, cliqueu l'enllaç següent per activar el vostre compte en %s: \ No newline at end of file diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index fbe5ef49a2..7fc4d1c7aa 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -2721,7 +2721,7 @@ error.csv.unexpected=Tento soubor nelze vykreslit, protože obsahuje neočekáva error.csv.invalid_field_count=Soubor nelze vykreslit, protože má nesprávný počet polí na řádku %d. pulls.made_using_agit = AGit settings.confirm_wiki_branch_rename = Přejmenovat větev wiki -issues.comment.blocked_by_user = U tohoto problému nemůžete vytvořit komentář, protože jste byl/a zablokován/a majitelem repozitáře nebo autorem problému. +issues.comment.blocked_by_user = Tento problém nemůžete okomentovat, protože jste byli zablokováni majitelem repozitáře nebo autorem problému. contributors.contribution_type.additions = Přidání admin.manage_flags = Spravovat vlajky admin.enabled_flags = Vlajky povolené v repozitáři: @@ -2734,7 +2734,7 @@ clone_in_vscodium = Klonovat do VSCodium settings.wiki_rename_branch_main_notices_1 = Tato operace je NEVRATNÁ. settings.wiki_branch_rename_success = Název větve wiki repozitáře byl úspěšně normalizován. rss.must_be_on_branch = Abyste mohli mít zdroj RSS, musíte se nacházet ve větvi. -issues.blocked_by_user = V tomto repozitáři nemůžete vytvořit problém, protože jste byl/a jeho majitelem zablokován/a. +issues.blocked_by_user = V tomto repozitáři nemůžete vytvářet problémy, protože jste byl/a jeho majitelem zablokován/a. migrate.forgejo.description = Migrovat data z codeberg.org nebo jiných instancí Forgejo. mirror_sync = synchronizováno blame.ignore_revs = Ignorování revizí v souboru .git-blame-ignore-revs. Klikněte sem pro udělení výjimky a zobrazení normálního přehledu blame. @@ -2901,6 +2901,10 @@ editor.commit_email = E-mail revize commits.view_single_diff = Zobrazit změny tohoto souboru provedené v této revizi pulls.editable = Upravitelné pulls.editable_explanation = Tato žádost o sloučení umožňuje úpravy správci. Můžete přispět přímo do ní. +issues.reopen.blocked_by_user = Tento problém nemůžete znovu otevřít, protože jste byli zablokováni majitelem repozitáře nebo autorem tohoto problému. +pulls.comment.blocked_by_user = Tuto žádost o sloučení nemůžete okomentovat, protože jste byli zablokováni majitelem repozitáře nebo autorem žádosti. +issues.filter_no_results_placeholder = Zkuste upravit filtry vyhledávání. +issues.filter_no_results = Žádné výsledky [graphs] component_loading_info = Tohle může chvíli trvat… diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index c22c5fffaf..5a79004acb 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -213,6 +213,10 @@ table_modal.label.columns = Kolonner buttons.unindent.tooltip = Udsortere genstande med ét niveau buttons.indent.tooltip = Indlejring af genstande med ét niveau buttons.switch_to_legacy.tooltip = Brug den gamle editor i stedet +link_modal.header = Tilføj et link +link_modal.url = Url +link_modal.description = Beskrivelse +link_modal.paste_reminder = Tip: Med en URL i dit udklipsholder kan du indsætte direkte i editoren for at oprette et link. [filter] string.asc = A - Z @@ -978,6 +982,26 @@ change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil væ change_username_redirect_prompt.with_cooldown.few = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. keep_pronouns_private = Vis kun stedord til godkendte brugere keep_pronouns_private.description = Dette vil skjule dine stedord for besøgende, der ikke er logget ind. +quota.applies_to_user = Følgende kvoteregler gælder for din konto +quota.rule.exceeded.helper = Den samlede størrelse af objekter for denne regel har overskredet kvoten. +storage_overview = Opbevaringsoversigt +quota = Kvote +quota.applies_to_org = Følgende kontingentregler gælder for denne organisation +quota.rule.exceeded = Oversteget +quota.rule.no_limit = Ubegrænset +quota.sizes.all = Alle +quota.sizes.repos.all = Depoter +quota.sizes.repos.public = Offentlige depoter +quota.sizes.repos.private = Private depoter +quota.sizes.git.all = Git indhold +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = Aktiver +quota.sizes.assets.attachments.all = Vedhæftede filer +quota.sizes.assets.attachments.issues = Problemets vedhæftede filer +quota.sizes.assets.attachments.releases = Udgivelsens vedhæftede filer +quota.sizes.assets.artifacts = Artefakter +quota.sizes.assets.packages.all = Pakker +quota.sizes.wiki = Wiki [repo] rss.must_be_on_branch = Du skal være på en gren for at have et RSS-feed. @@ -1755,8 +1779,8 @@ issues.review.un_resolve_conversation = Uafklaret samtale issues.content_history.delete_from_history = Slet fra historikken issues.content_history.delete_from_history_confirm = Slet fra historikken? issues.content_history.options = Valgmuligheder -issues.blocked_by_user = Du kan ikke oprette et problem på dette depot, fordi du er blokeret af depotes ejer. -issues.comment.blocked_by_user = Du kan ikke oprette en kommentar til dette problem, fordi du er blokeret af depotes ejer eller anmelder af problemet. +issues.blocked_by_user = Du kan ikke oprette problemer på dette depot, fordi du er blokeret af depotes ejer. +issues.comment.blocked_by_user = Du kan ikke kommentere til dette problem, fordi du er blokeret af depotes ejer eller anmelder af problemet. issues.reference_link = Reference: %s compare.compare_base = base compare.compare_head = sammenlign @@ -1856,10 +1880,10 @@ pulls.rebase_merge_pull_request = Rebaser og spole derefter frem pulls.rebase_merge_commit_pull_request = Rebase og opret derefter flet commit pulls.waiting_count_1 = %d venter på gennemgang pulls.invalid_merge_option = Du kan ikke bruge denne fletteindstilling til denne pull-anmodning. -pulls.merge_conflict = Sammenfletning mislykkedes: Der var en konflikt under sammenlægningen. Tip: Prøv en anden strategi +pulls.merge_conflict = Fletning mislykkedes: Der var en konflikt under sammenlægningen. Tip: Prøv en anden strategi pulls.merge_conflict_summary = Fejl besked pulls.rebase_conflict_summary = Fejl besked -pulls.unrelated_histories = Sammenfletning mislykkedes: Sammenfletnings-hovedet og -basen deler ikke en fælles historie. Tip: Prøv en anden strategi +pulls.unrelated_histories = Fletning mislykkedes: Sammenfletnings-hovedet og -basen deler ikke en fælles historie. Tip: Prøv en anden strategi pulls.head_out_of_date = Fletning mislykkedes: Under genereringen af fletningen blev hovedet opdateret. Tip: Prøv igen. pulls.has_merged = Mislykkedes: Pull-anmodningen er blevet flettet, du kan ikke flette igen eller ændre målgrenen. pulls.rebase_conflict = Fletning mislykkedes: Der var en konflikt under re-basering af commit: %[1]s. Tip: Prøv en anden strategi @@ -1867,6 +1891,55 @@ pulls.merge_out_of_date = Fletning mislykkedes: Under genereringen af fletningen pulls.push_rejected_summary = Fuld afvisningsmeddelelse pulls.push_rejected = Push mislykkedes: Pushet blev afvist. Gennemgå Git-hooks for dette depot. pulls.push_rejected_no_message = Push mislykkedes: Pushet blev afvist, men der var ingen fjernmeddelelse. Gennemgå Git-hooks for dette depot +pulls.status_checks_failure = Nogle kontroller mislykkedes +issues.reopen.blocked_by_user = Du kan ikke genåbne dette problem, fordi du er blokeret af depotes ejer eller aflæggeren af dette problem. +pulls.comment.blocked_by_user = Du kan ikke kommentere denne pull-anmodning, fordi du er blokeret af depotes ejer eller plakaten for pull-anmodningen. +pulls.open_unmerged_pull_exists = `Du kan ikke udføre en genåbnings handling, fordi der er en afventende pull-anmodning (#%d) med identiske egenskaber.` +pulls.status_checking = Nogle kontroller afventer +pulls.status_checks_warning = Nogle kontroller rapporterede advarsler +pulls.status_checks_error = Nogle kontroller rapporterede fejl +pulls.cmd_instruction_merge_desc = Flet ændringerne og opdater på Forgejo. +pulls.clear_merge_message = Ryd flettemeddelelse +pulls.cmd_instruction_merge_warning = Advarsel: Indstillingen "Autofind manuel fletning" er ikke aktiveret for dette depot. Du skal efterfølgende markere denne pull-anmodning som manuelt flettet. +pulls.clear_merge_message_hint = Rydning af flettemeddelelsen vil kun fjerne commit-meddelelsens indhold og beholde genererede git-trailere såsom "Co-Authored-By ...". +pulls.reopen_failed.head_branch = Pull-anmodningen kan ikke genåbnes, fordi hovedgrenen ikke eksisterer længere. +pulls.editable_explanation = Denne pull-anmodning tillader redigeringer fra vedligeholdere. Du kan bidrage direkte til det. +pulls.auto_merge_button_when_succeed = (Når kontroller lykkes) +pulls.status_checks_requested = Påkrævet +pulls.close = Luk pull anmodning +pulls.commit_ref_at = `henviste til denne pull-anmodning fra en commit %[2]s` +pulls.cmd_instruction_hint = Se instruktionerne på kommandolinjen +pulls.reopened_at = `genåbnede denne pull-anmodning %[2]s` +pulls.closed_at = `lukkede denne pull-anmodning %[2]s` +pulls.cmd_instruction_checkout_desc = Fra dit projektdepot, tjek en ny gren og test ændringerne. +pulls.editable = Redigerbar +pulls.made_using_agit = AGit +pulls.agit_explanation = Oprettet ved hjælp af AGit-arbejdsgangen. AGit lader bidragydere foreslå ændringer ved hjælp af "git push" uden at oprette en fork eller en ny gren. +pulls.status_checks_success = Alle kontroller lykkedes +pulls.cmd_instruction_merge_title = Flet +pulls.reopen_failed.base_branch = Pull-anmodningen kan ikke genåbnes, fordi basisgrenen ikke eksisterer længere. +pulls.cmd_instruction_checkout_title = Checkout +pulls.outdated_with_base_branch = Denne gren er forældet med basisgrenen +pulls.status_checks_details = Detaljer +pulls.status_checks_hide_all = Skjul alle kontroller +pulls.status_checks_show_all = Vis alle kontroller +pulls.update_branch = Opdater gren ved fletning +pulls.update_branch_rebase = Opdel gren ved genbase +pulls.update_branch_success = Grenopdatering var vellykket +pulls.update_not_allowed = Du har ikke tilladelse til at opdatere grenen +pulls.auto_merge_has_pending_schedule = %[1]s planlagde denne pull-anmodning til automatisk at flette, når alle kontroller lykkes %[2]s. +pulls.auto_merge_cancel_schedule = Annuller automatisk fletning +pulls.auto_merge_not_scheduled = Denne pull-anmodning er ikke planlagt til automatisk fletning. +pulls.auto_merge_canceled_schedule = Den automatiske fletning blev annulleret for denne pull-anmodning. +pulls.auto_merge_newly_scheduled_comment = `planlagde denne pull-anmodning til automatisk at flette, når alle kontroller lykkes %[1]s.` +pulls.auto_merge_canceled_schedule_comment = `annullerede automatisk fletning af denne pull-anmodning, når alle kontroller lykkes %[1]s` +pulls.delete_after_merge.head_branch.is_default = Den hovedgren, du vil slette, er standardgrenen og kan ikke slettes. +pulls.delete_after_merge.head_branch.is_protected = Den hovedgren, du vil slette, er en beskyttet gren og kan ikke slettes. +pulls.delete_after_merge.head_branch.insufficient_branch = Du har ikke tilladelse til at slette hovedgrenen. +pulls.delete.title = Vil du slette denne pull-anmodning? +pulls.delete.text = Vil du virkelig slette denne pull-anmodning? (Dette fjerner alt indhold permanent. Overvej at lukke det i stedet, hvis du har til hensigt at beholde det arkiveret) +pulls.auto_merge_newly_scheduled = Pull-anmodningen var planlagt til at flette, når alle kontroller lykkes. +pulls.auto_merge_when_succeed = Automatisk fletning, når alle kontroller lykkes [notification] watching = Overvåger diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 34a69202b0..ccd50d0718 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -206,6 +206,10 @@ table_modal.placeholder.content = Inhalt table_modal.placeholder.header = Kopfzeile table_modal.label.rows = Zeilen table_modal.label.columns = Spalten +link_modal.header = Einen Link hinzufügen +link_modal.url = URL +link_modal.description = Beschreibung +link_modal.paste_reminder = Hinweis: Wenn du einen URL in der Zwischenablage hast, kannst du durch einfügen im Editor direkt einen Link erstellen. [filter] string.asc=A–Z @@ -1064,6 +1068,26 @@ change_username_redirect_prompt.with_cooldown.one = Der alte Benutzername ist na change_username_redirect_prompt.with_cooldown.few = Der alte Benutzername ist nach einer Schutzzeit von %[1]d Tagen wieder für alle Verfügbar. Du kannst den alten Benutzername während dieser Schutzzeit erneut beanspruchen. keep_pronouns_private = Pronomen nur angemeldeten Nutzern anzeigen keep_pronouns_private.description = Dies verbirgt deine Pronomen von Besuchern die nicht angemeldet sind. +quota.sizes.assets.artifacts = Artefakte +quota.applies_to_user = Die folgenden Quota-Regeln greifen für deinen Account +quota.sizes.assets.attachments.issues = Issue-Anhänge +quota.rule.exceeded.helper = Die Gesamtgröße der Objekte für diese Regel hat die Quota überschritten. +storage_overview = Speicherübersicht +quota = Quota +quota.sizes.assets.attachments.releases = Release-Anhänge +quota.applies_to_org = Die folgenden Quota-Regeln greifen für diese Organisation +quota.rule.exceeded = Überschritten +quota.rule.no_limit = Unbegrenzt +quota.sizes.all = Alle +quota.sizes.repos.all = Repositorys +quota.sizes.repos.public = Öffentliche Repositorys +quota.sizes.repos.private = Private Repositorys +quota.sizes.git.all = Git-Inhalte +quota.sizes.git.lfs = Git-LFS +quota.sizes.assets.all = Assets +quota.sizes.assets.attachments.all = Anhänge +quota.sizes.assets.packages.all = Pakete +quota.sizes.wiki = Wiki [repo] owner=Besitzer @@ -1185,8 +1209,8 @@ template.issue_labels=Issue-Labels template.one_item=Es muss mindestens ein Vorlagenelement ausgewählt werden template.invalid=Es muss ein Vorlagen-Repository ausgewählt werden -archive.title=Dieses Repository ist archiviert. Du kannst Dateien ansehen und es klonen, kannst aber du kannst den Status des Repositorys nicht verändern, zum Beispiel nichts pushen, und keine Issues eröffnen, oder Pull-Requests oder Kommentare erstellen. -archive.title_date=Dieses Repository wurde am %s archiviert. Du kannst Dateien ansehen und es klonen, kannst aber den Status des Repositorys nicht verändern, zum Beispiel nichts pushen, und keine Issues eröffnen, oder Pull-Requests oder Kommentare erstellen. +archive.title=Dieses Repository ist archiviert. Du kannst Dateien ansehen und es klonen, kannst aber du kannst seinen Status nicht verändern, zum Beispiel nichts pushen, und keine Issues eröffnen, oder Pull-Requests oder Kommentare erstellen. +archive.title_date=Dieses Repository wurde am %s archiviert. Du kannst Dateien ansehen und es klonen, kannst aber seinen Status nicht verändern, zum Beispiel nichts pushen, und keine Issues eröffnen, oder Pull-Requests oder Kommentare erstellen. archive.issue.nocomment=Dieses Repository ist archiviert. Du kannst Issues nicht kommentieren. archive.pull.nocomment=Dieses Repository ist archiviert. Du kannst Pull-Requests nicht kommentieren. @@ -2699,7 +2723,7 @@ error.csv.unexpected=Diese Datei kann nicht gerendert werden, da sie ein unerwar error.csv.invalid_field_count=Diese Datei kann nicht gerendert werden, da sie eine falsche Anzahl an Feldern in Zeile %d hat. rss.must_be_on_branch = Du musst auf einem Branch sein, um einen RSS-Feed zu haben. new_repo_helper = Ein Repository enthält alle Projektdateien inklusive der Revisionshistorie. Bereits woanders gehostet? Repository migrieren. -issues.comment.blocked_by_user = Du kannst kein Kommentar für dieses Issue erstellen, weil du vom Repository-Besitzer oder dem Autoren des Issues blockiert wurdest. +issues.comment.blocked_by_user = Du kannst dieses Issue nicht kommentieren, weil du vom Repository-Besitzer oder dem Autoren des Issues blockiert wurdest. clone_in_vscodium = In VSCodium klonen settings.units.add_more = Mehr aktivieren settings.wiki_rename_branch_main_desc = Den Branch, der intern vom Wiki benutzt wird, zu „%s“ umbenennen. Dies ist permanent und kann nicht rückgängig gemacht werden. @@ -2715,7 +2739,7 @@ settings.wiki_branch_rename_success = Der Branch-Name des Repository-Wikis wurde settings.archive.mirrors_unavailable = Spiegel sind nicht verfügbar in archivierten Repos. pulls.blocked_by_user = Du kannst keinen Pull-Request in diesem Repository erstellen, weil du vom Repository-Besitzer blockiert wurdest. settings.add_collaborator_blocked_our = Der Mitarbeiter konnte nicht hinzugefügt werden, weil der Repository-Besitzer ihn blockiert hat. -issues.blocked_by_user = Du kannst kein Issue in diesem Repository erstellen, weil du vom Repository-Besitzer blockiert wurdest. +issues.blocked_by_user = Du kannst keine Issues in diesem Repository erstellen, weil du vom Repository-Besitzer blockiert wurdest. admin.manage_flags = Flags verwalten admin.enabled_flags = Vom Repository aktivierte Flags: admin.update_flags = Flags aktualisieren @@ -2879,6 +2903,8 @@ editor.commit_email = Commit-E-Mail commits.view_single_diff = Änderungen an dieser Datei, die in diesem Commit eingeführt wurden, betrachten pulls.editable = Bearbeitbar pulls.editable_explanation = Dieser Pull-Request erlaubt Bearbeitungen durch Maintainer. Du kannst direkt dazu beitragen. +issues.reopen.blocked_by_user = Du kannst dieses Issue nicht wieder eröffnen, weil du vom Repository-Besitzer oder Ersteller des Issues blockiert wurdest. +pulls.comment.blocked_by_user = Du kannst diesen Pull-Request nicht kommentieren, da du vom Repository-Besitzer oder Ersteller des Pull-Requests blockiert wurdest. [graphs] component_loading_failed = Konnte %s nicht laden diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 4ade0821a1..8b29ef0f4a 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -110,7 +110,7 @@ preview=Vista previa loading=Cargando… error=Error -error404=La página a la que está intentando acceder o no existe o no está autorizado para verla. +error404=La página a la que está intentando acceder no existe,ha sido eliminada o no está autorizado a verla. go_back=Volver never=Nunca @@ -206,6 +206,9 @@ table_modal.placeholder.header = Cabecera table_modal.label.rows = Filas table_modal.label.columns = Columnas table_modal.placeholder.content = Contenido +link_modal.header = Añadir enlace +link_modal.description = Descripción +link_modal.paste_reminder = Pista: Con una URL en tu portapapeles, puedes pegar directamente en el editor para crear un enlace. [filter] string.asc=A - Z @@ -678,6 +681,8 @@ Biography = Biografía Location = Ubicación To = Nombre de rama Website = Sitio web +username_claiming_cooldown = El nombre de usuario no se puede reclamar, debido a que su periodo de protección no ha terminado aún. Puede ser reclamado el %[1]s. +email_domain_is_not_allowed = El dominio de la dirección de correo electrónico del usuario %s entra en conflicto con EMAL_DOMAIN_ALLOWLIST o EMAIL_DOMAIN_BLOCKLIST. Asegúrese de que ha establecido la dirección de correo electrónico correctamente. [user] @@ -1055,6 +1060,7 @@ language.description = Este idioma se guardará en tu cuenta y se utilizará com language.localization_project = ¡Ayúdanos a traducir Forgejo a tu idioma! Más información. pronouns_custom_label = Pronombres personalizados user_block_yourself = No puedes bloquearte a tí mismo. +quota = Cuota [repo] owner=Propietario diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index b6c065f7ee..804b48b2b2 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -209,6 +209,8 @@ buttons.switch_to_legacy.tooltip = به جای آن از ویرایش‌گر ک buttons.code.tooltip = افزودن کد link_modal.header = افزودن پیوند link_modal.url = یوآرال +link_modal.paste_reminder = راهنمایی: با داشتن یک یوآرال در بریده‌‌دان، می‌توانید با رونوشت مستقیم آن در ویرایش‌گرتان یک پیوند بسازید. +link_modal.description = دیباچه [filter] string.asc = آ - ی @@ -235,7 +237,7 @@ install_desc = به آسانی مستندات را مطالعه نمایید. db_title=تنظیمات پایگاه‌داده db_type=گونه پایگاه‌داده @@ -257,24 +259,24 @@ err_empty_db_path=مسیر دیتابیس SQLite3 نمیتواند خالی با no_admin_and_disable_registration=شما بدون ایجاد حساب‌ کاربری مدیر نمی‌توانید عضویت را غیر فعال کنید. err_empty_admin_password=کلمه عبور حساب مدیر نمی تواند خالی باشد. err_empty_admin_email=رایانامه (ایمیل) مدیر نمی تواند خالی باشد. -err_admin_name_is_reserved=نام کاربری مدیر اشتباه است. نام کاربری قبلا استفاده شده است +err_admin_name_is_reserved=نام‌کاربری مدیر اشتباه است، نام‌کاربری قبلا استفاده شده است err_admin_name_pattern_not_allowed=نام کاربری مدیر اشتباه است. نام کاربری قبلا استفاده شده است err_admin_name_is_invalid=نام کابری مدیر اشتباه است general_title=تنظیمات عمومی -app_name=عنوان سایت -app_name_helper=شما می توانید نام شرکت خود را در اینجا وارد کنید. +app_name=تیتر نمونه +app_name_helper=شما می توانید نام نمونه خود را در اینجا وارد کنید. آن در همه برگه‌ها به نمایش در خواهد آمد. repo_path=مسیر ریشه مخزن repo_path_helper=تمام مخازن کد راه دور در این پوشه ذخیره می‌شوند. -lfs_path=مسیر Git LFS +lfs_path=مسیر ریشه ذخیره‌سازی پرونده بزرگ گیت lfs_path_helper=فایل هایی که توسط Git LFS دنبال میشوند در این پوشه ذخیره خواهند شد. درصورت خالی بودن فیلد این قابلیت غیرفعال خواهد بود. -run_user=اجرا به عنوان نام کاربری -domain=دامنه سرور +run_user=اجرای کاربر به عنوان +domain=دامنه کارساز domain_helper=آدرس میزبان یا دامنه برای سرور. -ssh_port=پورت SSH سرور -ssh_port_helper=شماره درگاهی که سرور SSH گوش می دهد. برای غیر فعال کردن خالی بگذارید. -http_port=پورت HTTP گیتی -http_port_helper=پورت سرور وب گیتی. +ssh_port=درگاه کارساز پوسته‌امن +ssh_port_helper=شماره درگاهی که سرور پوسته‌امن استفاده می‌کند. برای غیر فعال کردن خالی بگذارید. +http_port=درگاه شنونده اچ‌تی‌تی‌پی +http_port_helper=شماره درگاهی که توسط کارساز وب فرججو استفاده می‌شود. app_url=آدرس پایه گیتی app_url_helper=آدرس پایه برای URLهای اجماع HTTP(S) و هشدار های رایانامه (ایمیل). log_root_path=مسیر گزارش‌ها @@ -335,6 +337,9 @@ no_reply_address=مخفی کردن دامنه ایمیل no_reply_address_helper=نام دامنه برای کاربران دارای آدرس ایمیل پنهان است. به عنوان مثال ، اگر نام دامنه ایمیل مخفی روی "noreply.example.org" تنظیم شده باشد ، نام کاربری "joe" در Git به عنوان "joe@noreply.example.org" وارد می شود password_algorithm=الگوریتم درهم‌ساز گذرواژه require_db_desc = فروججو به مای‌اس‌کیوال، پستگری‌اس‌کیوال،اس‌کیولایت۳ یا تی‌دی‌بی نیاز دارد. +run_user_helper = نام‌کاربری سامانه‌عامل که با عنوان فرججو اجرا می‌شود. به یادداشته باشید که کاربر باید دسترسی به مسیر ریشه مخازن داشته باشد. +app_slogan_helper = شعار نمونه خود را اینجا وارد کنید. برای غیرفعال شدن خالی بگذارید. +app_slogan = شعار نمونه [home] uname_holder=نام کاربری یا نشانی ایمیل diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 9d5ac3c43e..9c011b7fd1 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -98,7 +98,7 @@ preview=Esikatselu loading=Ladataan… error=Virhe -error404=Sivu, jota yrität nähdä, joko ei löydy tai et ole oikeutettu katsomaan sitä. +error404=Sivu, jota yrität nähdä, joko ei löydy, on poistettu tai et ole oikeutettu katsomaan sitä. never=Ei koskaan @@ -206,6 +206,10 @@ table_modal.placeholder.content = Sisältö table_modal.label.rows = Rivit table_modal.label.columns = Sarakkeet buttons.unindent.tooltip = Vähennä sisennystä yhden tason verran +link_modal.header = Lisää linkki +link_modal.url = Osoite +link_modal.description = Kuvaus +link_modal.paste_reminder = Vihje: Jos leikepöydällä on URL-osoite, voit liittää suoraan editoriin luodaksesi linkin. [filter] string.asc = A - Ö @@ -620,6 +624,8 @@ unable_verify_ssh_key = SSH-avainta ei voi vahvistaa, tarkista se mahdollisten v url_error = `"%s" ei ole kelvollinen URL-osoite.` must_use_public_key = Antamasi avain on yksityinen avain. Älä lähetä yksityistä avaintasi mihinkään. Käytä sen sijaan julkista avaintasi. still_own_packages = Tilisi omistaa yhden tai useamman paketin, poista ne ensin. +AccessToken = Pääsypoletti +enterred_invalid_owner_name = Uuden omistajan nimi ei ole kelvollinen. [user] @@ -947,6 +953,21 @@ openid_deletion_desc = Tämän OpenID-osoitteen poistaminen tililtäsi estää k generate_token_name_duplicate = Nimeä %s on jo käytetty sovelluksen nimenä. Käytä eri nimeä. ssh_signonly = SSH on tällä hetkellä poistettu käytöstä, joten näitä avaimia käytetään vain kommittien allekirjoituksen vahvistamiseen. oauth2_applications_desc = OAuth2-sovellukset mahdollistavat käyttämäsi kolmannen osapuolen sovelluksen todentaa turvallisesti käyttäjiä tähän Forgejo-instanssiin. +quota.sizes.assets.attachments.all = Liitteet +quota.applies_to_user = Seuraavia kiintiösääntöjä sovelletaan tiliisi +user_block_yourself = Et voi estää itseäsi. +quota = Kiintiö +storage_overview = Tallennustilan yleisnäkymä +quota.applies_to_org = Seuraavia kiintiösääntöjä sovelletaan tähän organisaatioon +quota.rule.exceeded = Ylitetty +quota.rule.no_limit = Rajoittamaton +quota.sizes.all = Kaikki +quota.sizes.repos.all = Repot +quota.sizes.repos.public = Julkiset repot +quota.sizes.repos.private = Yksityiset repot +quota.sizes.git.all = Git-sisältö +quota.sizes.assets.packages.all = Paketit +quota.sizes.wiki = Wiki [repo] owner=Omistaja @@ -2096,6 +2117,13 @@ settings.delete_notices_2 = - Tämä toiminto poistaa pysyvästi repon % issues.filter_assginee_no_select = Kaikki käsittelijät issues.new.assign_to_me = Osoita itselle pulls.closed_at = `sulki tämän vetopyynnön %[2]s` +tree_path_not_found_branch = Polkua %[1]s ei ole olemassa haarassa %[2]s +transfer.no_permission_to_reject = Sinulla ei ole oikeutta hylätä tätä siirtoa. +generate_repo = Luo repo +tree_path_not_found_commit = Polkua %[1]s ei ole olemassa kommitissa %[2]s +archive.pull.noreview = Tämä repo on arkistoitu. Et voi katselmoida vetopyyntöjä. +tree_path_not_found_tag = Polkua %[1]s ei ole olemassa tagissa %[2]s +transfer.no_permission_to_accept = Sinulla ei ole oikeutta hyväksyä tätä siirtoa. diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 31e0e2135b..12b43f93e9 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -231,10 +231,10 @@ ssl_mode = SSL path = Daanan sqlite_helper = File path para sa SQLite3 database.
Maglagay ng absolute path kapag tinatakbo mo ang Forgejo bilang serbisyo. reinstall_confirm_check_3 = Kinukumprima mo na sigurado ka talaga na ang Forgejo na ito ay tumatakbo sa tamang app.ini na lokasyon at sigurado ka na kailangan mo mag-reinstall. Kinukumpirma mo na kilalanin ang mga panganib sa itaas. -err_empty_db_path = Hindi maaring walang laman ang path ng SQLite database. -no_admin_and_disable_registration = Hindi mo maaring i-disable ang user self-registration nang hindi gumawa ng isang tagapangasiwa na account. -err_empty_admin_password = Hindi maaring walang laman ang password ng tagapangasiwa. -err_empty_admin_email = Hindi maaring walang laman ang email ng tagapangasiwa. +err_empty_db_path = Hindi maaaring walang laman ang path ng SQLite database. +no_admin_and_disable_registration = Hindi mo maaaring i-disable ang user self-registration nang hindi gumawa ng isang tagapangasiwa na account. +err_empty_admin_password = Hindi maaaring walang laman ang password ng tagapangasiwa. +err_empty_admin_email = Hindi maaaring walang laman ang email ng tagapangasiwa. err_admin_name_is_reserved = Hindi angkop ang username ng tagapangasiwa, naka-reserba ang username err_admin_name_is_invalid = Hindi angkop ang username ng tagapangasiwa general_title = Mga General Setting @@ -250,9 +250,9 @@ domain_helper = Domain o host para sa server na ito. ssh_port = Port ng SSH Server http_port = Listen port sa HTTP lfs_path_helper = Ang mga file na naka-track sa Git LFS ay ilalagay sa directory na ito. Iwanang walang laman para i-disable. -reinstall_confirm_message = Ang pag-install muli na may umiiral na Forgejo database ay maaring magdulot ng mga problema. Sa karamihan ng mga kaso, dapat mong gamitin ang iyong umiiral na "app.ini" para patakbuhin ang Forgejo. Kung alam mo ang ginagawa mo, kumpirmahin ang mga sumusunod: -reinstall_confirm_check_1 = Ang data na naka-encrypt sa pamamagitan ng SECRET_KEY sa app.ini ay maaring mawala: baka hindi maka-log in ang mga user gamit ng 2FA/OTP at ang mga mirror ay maaring hindi gumana mg maayos. Sa pamamagitan ng pag-check ng box na ito kinukumpirma mo na ang kasalukuyang app.ini file ay naglalaman ng tamang SECRET_KEY. -reinstall_confirm_check_2 = Ang mga repositoryo at mga setting ay maaring kailangang i-resynchronize. Sa pamamagitan ng pag-check ng box na ito kinukumprima mo na ire-resynchronize mo ang mga hook para sa mga repositoryo at authorized_keys ng mano-mano. Kinukumpirma mo na sisiguraduhin mo na tama ang mga setting ng repositoryo at mirror. +reinstall_confirm_message = Ang pag-install muli na may umiiral na Forgejo database ay maaaring magdulot ng mga problema. Sa karamihan ng mga kaso, dapat mong gamitin ang iyong umiiral na "app.ini" para patakbuhin ang Forgejo. Kung alam mo ang ginagawa mo, kumpirmahin ang mga sumusunod: +reinstall_confirm_check_1 = Ang data na naka-encrypt sa pamamagitan ng SECRET_KEY sa app.ini ay maaaring mawala: baka hindi maka-log in ang mga user gamit ng 2FA/OTP at ang mga mirror ay maaaring hindi gumana mg maayos. Sa pamamagitan ng pag-check ng box na ito kinukumpirma mo na ang kasalukuyang app.ini file ay naglalaman ng tamang SECRET_KEY. +reinstall_confirm_check_2 = Ang mga repositoryo at mga setting ay maaaring kailangang i-resynchronize. Sa pamamagitan ng pag-check ng box na ito kinukumprima mo na ire-resynchronize mo ang mga hook para sa mga repositoryo at authorized_keys ng mano-mano. Kinukumpirma mo na sisiguraduhin mo na tama ang mga setting ng repositoryo at mirror. err_admin_name_pattern_not_allowed = Hindi angkop ang username ng tagapangasiwa, ang username ay tumutugma sa reserved pattern ssh_port_helper = Numero ng port na gagamitin ng SSH server. Iwanang walang laman para i-disable ang SSH server. server_service_title = Mga setting ng server at third-party na serbisyo @@ -362,6 +362,10 @@ table_modal.placeholder.content = Nilalaman table_modal.header = Magdagdag ng table table_modal.label.rows = Mga Row table_modal.label.columns = Mga Column +link_modal.header = Magdagdag ng link +link_modal.url = Url +link_modal.description = Deskripsyon +link_modal.paste_reminder = Pahiwatig: Kapag may URL sa clipboard, maari mong direktang i-paste sa editor para gumawa ng link. [filter] string.asc = A - Z @@ -565,7 +569,7 @@ SSPISeparatorReplacement = Pang-hiwalay SSPIDefaultLanguage = Default na wika CommitSummary = Pangkalahatang-ideya ng commit glob_pattern_error = ` hindi angkop ang glob pattern: %s` -require_error = ` hindi maaring walang laman.` +require_error = ` hindi maaaring walang laman.` alpha_dash_error = ` dapat maglaman lamang ng alphanumeric, dash ("-") at underscore ("_") na mga character.` alpha_dash_dot_error = ` dapat maglaman lamang ng alphanumeric, dash ("-"), underscore ("_") at tuldok (".") na mga character.` git_ref_name_error = ` dapat na mahusay na nabuong pangalan ng Git reference` @@ -613,8 +617,8 @@ unset_password = Hindi nagtakda ng password ang login user. unsupported_login_type = Hindi sinusuportahan ang uri ng pag-login para burahin ang account. user_not_exist = Hindi umiiral ang user. team_not_exist = Hindi umiiral ang koponan. -last_org_owner = Hindi mo maaring tanggalin ang pinakahuling user sa "mga may-ari" na koponan. Kailangan may kahit isang may-ari para sa organisasyon. -cannot_add_org_to_team = Hindi maaring madagdag ang isang organisasyon bilang miyembro ng koponan. +last_org_owner = Hindi mo maaaring tanggalin ang pinakahuling user sa "mga may-ari" na koponan. Kailangan may kahit isang may-ari para sa organisasyon. +cannot_add_org_to_team = Hindi maaaring madagdag ang isang organisasyon bilang miyembro ng koponan. duplicate_invite_to_team = Inimbita na ang user bilang miyembro ng koponan. organization_leave_success = Matagumpay kang umalis sa organisasyon na %s. invalid_ssh_key = Hindi ma-verify ang iyong SSH key: %s @@ -629,7 +633,7 @@ still_own_packages = Ang iyong account ay nagmamay-ari ng isa o higit pang packa org_still_own_repo = Ang organisasyon na ito ay nagmamay-ari ng isa o higit pang mga repositoryo, burahin o ilipat sila muna. org_still_own_packages = Ang organisasyon na ito ay nagmamay-ari ng isa o higit pang mga package, burahin sila muna. target_branch_not_exist = Hindi umiiral ang target branch. -admin_cannot_delete_self = Hindi mo maaring burahin ang sarili mo kapag isa kang tagapangasiwa. Paki-tanggal ang iyong pribilehiyong tagapangasiwa muna. +admin_cannot_delete_self = Hindi mo maaaring burahin ang sarili mo kapag isa kang tagapangasiwa. Paki-tanggal ang iyong pribilehiyong tagapangasiwa muna. required_prefix = Ang input ay dapat magsimula sa "%s" FullName = Buong pangalan Description = Paglalarawan @@ -762,8 +766,8 @@ gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig delete_token_success = Nabura na ang token. Ang mga application na gumagamit nito ay hindi na maa-access ang iyong account. add_email_confirmation_sent = Ang isang email na pang-kumpirma ay ipinadala sa %s. Para kumpirmahin ang iyong email address, pakisuri ang iyong inbox at sundan ang ibinigay na link sa loob ng %s. key_content_ssh_placeholder = Nagsisimula sa "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com", o "sk-ssh-ed25519@openssh.com" -gpg_key_verified_long = Na-verify ang key na ito gamit ng isang token at maaring gamitin para i-verify ang mga commit na tumutugma sa anumang mga naka-activate na email address para sa user na ito kasama ang mga tumutugmang pagkakakilanlan para sa key na ito. -ssh_key_verified_long = Ang key na ito ay na-verify gamit ng isang token at maaring gamitin para i-verify ang mga commit na tumutugma na email address para sa user na ito. +gpg_key_verified_long = Na-verify ang key na ito gamit ng isang token at maaaring gamitin para i-verify ang mga commit na tumutugma sa anumang mga naka-activate na email address para sa user na ito kasama ang mga tumutugmang pagkakakilanlan para sa key na ito. +ssh_key_verified_long = Ang key na ito ay na-verify gamit ng isang token at maaaring gamitin para i-verify ang mga commit na tumutugma na email address para sa user na ito. add_principal_success = Idinagdag na ang SSH certificate principal na "%s". ssh_key_deletion_desc = Ang pagtanggal ng SSH key ay matatanggihan ang pag-access sa iyong account. Magpatuloy? no_activity = Walang kamakilang aktibidad @@ -822,7 +826,7 @@ keep_email_private = Itago ang email address openid_desc = Hinahayaan ka ng OpenID na mag-delegate ng pagpapatunay sa isang panlabas na tagabigay ng serbisyo. ssh_desc = Ang mga pampublikong SSH key na ito ay nauugnay sa iyong account. Pinapayagan ng kaukulang pribadong key ang buong pag-access sa iyong mga repositoryo. Ang mga SSH key na na-verify ay maaaring magamit upang mapatunayan ang mga naka-sign na Git commit sa pamamagitan ng SSH. principal_desc = Ang mga SSH principal na ito ay nauugnay sa iyong account at pinapayagan ang buong pag-access sa iyong mga repositoryo. -ssh_helper = Kailangan ng tulong? Tignan ang guide sa paggawa ng sarili mong mga SSH key o ilutas ang mga karaniwang problema na maaring moong matagpo gamit ng SSH. +ssh_helper = Kailangan ng tulong? Tignan ang guide sa paggawa ng sarili mong mga SSH key o ilutas ang mga karaniwang problema na maaaring moong matagpo gamit ng SSH. gpg_helper = Kailangan ng tulong? Tignan ang guide tungkol sa GPG. add_new_key = Magdagdag ng SSH key add_new_gpg_key = Magdagdag ng GPG key @@ -832,7 +836,7 @@ ssh_key_been_used = Idinagdag na ang SSH key na ito sa server. ssh_key_name_used = Ang isang SSH key na may katulad na pangalan ay umiiral na sa iyong account. ssh_principal_been_used = Idinagdag na ang principal na ito sa server. gpg_key_matched_identities = Mga Tumutugma na Pagkakakilanlan: -gpg_key_matched_identities_long = Ang mga naka-embed na pagkakakilanlan sa key na ito ay tumutugma sa mga sumusunod na naka-activate na email address para sa user na ito. Ang mga commit na tumutugma sa mga email address na ito ay maaring i-verify gamit ng key na ito. +gpg_key_matched_identities_long = Ang mga naka-embed na pagkakakilanlan sa key na ito ay tumutugma sa mga sumusunod na naka-activate na email address para sa user na ito. Ang mga commit na tumutugma sa mga email address na ito ay maaaring i-verify gamit ng key na ito. gpg_key_verified = Naka-verify na key gpg_key_verify = I-verify gpg_invalid_token_signature = Ang ibinigay na GPG key, signature, at token ay hindi tumutugma o luma. @@ -984,7 +988,7 @@ manage_account_links_desc = Ang mga panlabas na account na ito ay naka-link sa i hooks.desc = Magdagdag ng mga webhook na mati-trigger para sa lahat ng mga repositoryo na minamay-ari mo. orgs_none = Hindi ka isang miyembro ng anumang mga organisasyon. oauth2_application_create_description = Ang mga OAuth2 application ay pinapayagan ang mga third-party na aplikasyon na i-access ang mga user account sa instansya na ito. -oauth2_application_locked = Ang Forgejo ay pini-pre register ang ibang mga OAuth2 application sa startup kapag naka-enable sa config. Para iwasan ang hindi inaasahang gawain, hindi ito maaring i-edit o tanggalin. Mangyaring sumangguni sa dokumentasyon ng OAuth2 para sa karagdagang impormasyon. +oauth2_application_locked = Ang Forgejo ay pini-pre register ang ibang mga OAuth2 application sa startup kapag naka-enable sa config. Para iwasan ang hindi inaasahang gawain, hindi ito maaaring i-edit o tanggalin. Mangyaring sumangguni sa dokumentasyon ng OAuth2 para sa karagdagang impormasyon. remove_account_link_desc = Ang pagtanggal ng naka-link na account ay babawiin ang pag-access nito sa iyong Forgejo account. Magpatuloy? visibility.public_tooltip = Makikita ng lahat hints = Mga Pahiwatig @@ -1000,11 +1004,31 @@ keep_activity_private.description = Makikita mo lang at mga tagapangasiwa ng ins language.description = Mase-save ang wika sa iyong account at gagamitin bilang default pagkatapos mong mag-log in. language.localization_project = Tulungan kaming isalin ang Forgejo sa iyong wika! Matuto pa. pronouns_custom_label = Mga pasadyang pronoun -user_block_yourself = Hindi mo maaring harangan ang sarili mo. +user_block_yourself = Hindi mo maaaring harangan ang sarili mo. change_username_redirect_prompt.with_cooldown.one = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. change_username_redirect_prompt.with_cooldown.few = Magiging available ang lumang username sa lahat pagkatapos ng panahon ng cooldown ng %[1]d araw, maari mo pa ring ma-claim muli ang lumang username sa panahon ng panahon ng cooldown. keep_pronouns_private = Ipakita lang ang mga panghalip sa mga naka-authenticate na user keep_pronouns_private.description = Itatago nito ang iyong mga panghalip mula sa mga bisita na hindi naka-log in. +quota.applies_to_user = Nag-aapply ang mga sumusunod na panuntunan ng quota sa iyong account +quota.sizes.assets.attachments.issues = Mga attachment sa isyu +quota.applies_to_org = Ang mga sumusunod na panuntunan sa quota ay nalalapat sa organisasyong ito +storage_overview = Buod ng Storage +quota = Quota +quota.rule.exceeded = Nalampasan +quota.rule.exceeded.helper = Ang kabuuang sukat ng mga bagay para sa panuntunang ito ay lumampas sa quota. +quota.rule.no_limit = Walang limitasyon +quota.sizes.all = Lahat +quota.sizes.repos.all = Mga repositoryo +quota.sizes.repos.public = Mga pampublikong repositoryo +quota.sizes.repos.private = Mga pribadong repositoryo +quota.sizes.git.all = Nilalaman ng Git +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = Mga asset +quota.sizes.assets.attachments.all = Mga attachment +quota.sizes.assets.attachments.releases = Mga attachment sa release +quota.sizes.assets.artifacts = Mga artifact +quota.sizes.assets.packages.all = Mga package +quota.sizes.wiki = Wiki [repo] template_description = Ang mga template na repositoryo ay pinapayagan ang mga gumagamit na mag-generate ng mga bagong repositoryo na may magkatulad na istraktura ng direktoryo, mga file, at opsyonal na mga setting. @@ -1017,7 +1041,7 @@ admin.enabled_flags = Mga flag na naka-enable para sa repositoryo: admin.update_flags = I-update ang mga flag admin.flags_replaced = Napalitan ang mga flag ng repositoryo owner = May-ari -owner_helper = Maaring hindi mapapakita ang ibang organisasyon sa dropdown dahil sa pinakamataas na bilang ng repositoryo na limitasyon. +owner_helper = Maaaring hindi mapapakita ang ibang organisasyon sa dropdown dahil sa pinakamataas na bilang ng repositoryo na limitasyon. repo_name = Pangalan ng repositoryo repo_name_helper = Ang mga magandang pangalan ng repositoryo ay gumagamit ng maliit, makakaalala, at unique na mga keyword. repo_size = Laki ng Repositoryo @@ -1033,7 +1057,7 @@ fork_repo = I-fork ang repositoryo fork_from = I-fork mula sa already_forked = Na-fork mo na ang %s fork_to_different_account = Mag-fork sa ibang account -fork_visibility_helper = Ang visibility ng isang naka-fork na repositoryo ay hindi maaring baguhin. +fork_visibility_helper = Ang visibility ng isang naka-fork na repositoryo ay hindi maaaring baguhin. open_with_editor = Buksan gamit ang %s download_bundle = I-download ang BUNDLE repo_gitignore_helper_desc = Piliin kung anong mga file na hindi susubaybayin sa listahan ng mga template para sa mga karaniwang wika. Ang mga tipikal na artifact na ginagawa ng mga build tool ng wika ay kasama sa .gitignore ng default. @@ -1118,7 +1142,7 @@ stars = Mga bitwin migrate_options_mirror_helper = Magiging salamin ang repositoryong ito migrate_options_lfs_endpoint.description.local = Sinusuporta rin ang lokal na server path. editor.this_file_locked = Nakakandado ang file -editor.filename_cannot_be_empty = Hindi maaring walang laman ang pangalan ng file. +editor.filename_cannot_be_empty = Hindi maaaring walang laman ang pangalan ng file. commits.message = Mensahe commits.newer = Mas bago commits.date = Petsa @@ -1175,7 +1199,7 @@ template.avatar = Avatar migrate_options = Mga opsyon sa paglipat migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na repositoryo need_auth = Awtorisasyon -migrate.github_token_desc = Maari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaring magdulot ng pag-block ng account. +migrate.github_token_desc = Maaari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaaring magdulot ng pag-block ng account. template.invalid = Kailangang pumili ng kahit isang template na repositoryo migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa .git-blame-ignore-revs. @@ -1208,7 +1232,7 @@ adopt_preexisting_success = Pinagtibay ang mga file at ginawa ang repositoryo mu delete_preexisting_success = Burahin ang mga hindi pinatibay na file sa %s blame_prior = Tignan ang blame bago ang pagbabago na ito migrate.permission_denied = Hindi ka pinapayagang mag-import ng mga lokal na repositoryo. -migrate.permission_denied_blocked = Hindi ka maaring mag-import mula sa mga hindi pinapayagang host, magyaring magtanong sa pangangasiwa na suriin ang ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS na mga setting. +migrate.permission_denied_blocked = Hindi ka maaaring mag-import mula sa mga hindi pinapayagang host, magyaring magtanong sa pangangasiwa na suriin ang ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS na mga setting. migrate.invalid_local_path = Hindi wasto ang lokal na path. Hindi ito umiiral o hindi isang direktoryo. migrate.invalid_lfs_endpoint = Hindi wasto ang LFS endpoint. migrate.migrating_failed = Nabigo ang pag-migrate mula sa %s. @@ -1257,7 +1281,7 @@ file_follow = Sundan ang symlink file_view_source = Tignan ang source file_view_rendered = Tignan ng naka-render ambiguous_runes_header = `Naglalaman ng file na ito ng mga hindi tiyak na Unicode character` -ambiguous_runes_description = `Ang file na ito ay naglalaman ng mga Unicode character na maaring malilito sa ibang mga character. Kung sa tingin mo ay sinasadya ito, maari mong ligtas na hindi pansinin ang babala ito. Gamitin ang I-escape na button para ipakita sila.` +ambiguous_runes_description = `Ang file na ito ay naglalaman ng mga Unicode character na maaaring malilito sa ibang mga character. Kung sa tingin mo ay sinasadya ito, maaari mong ligtas na hindi pansinin ang babala ito. Gamitin ang I-escape na button para ipakita sila.` file_copy_permalink = Kopyahin ang permalink view_git_blame = Tignan ang git blame video_not_supported_in_browser = Hindi sinusuportahan ng inyong browser ang HTML5 "video" tag. @@ -1287,7 +1311,7 @@ broken_message = Ang Git data na pinagbabatayan sa repositoryo na ito ay hindi m file_history = Kasaysayan invisible_runes_header = `Nalalaman ng file na ito ng mga hindi nakikitang Unicode character` file_too_large = Masyadong malaki ang file para ipakita. -invisible_runes_description = `Ang file na ito ay naglalaman ng mga hindi nakikitang Unicode character na hindi nakikilala ng mga tao ngunit maaring maproseso ng ibang paraan ng isang computer. Kung sa tingin mo ay sinasadya ito, maari mong ligtas na hindi pansinin ang babala na ito. Gamitin ang I-escape na button para ipakita sila.` +invisible_runes_description = `Ang file na ito ay naglalaman ng mga hindi nakikitang Unicode character na hindi nakikilala ng mga tao ngunit maaaring maproseso ng ibang paraan ng isang computer. Kung sa tingin mo ay sinasadya ito, maaari mong ligtas na hindi pansinin ang babala na ito. Gamitin ang I-escape na button para ipakita sila.` commit.contained_in_default_branch = Ang commit na ito ay bahagi ng default na branch migrate.migrating_labels = Nililipat ang mga label filter_branch_and_tag = I-filter ang branch o tag @@ -1321,7 +1345,7 @@ clone_this_repo = I-clone ang repositoryo na ito cite_this_repo = Banggitin ang repositoryo na ito create_new_repo_command = Paggawa ng bagong repositoryo sa command line code = Code -ambiguous_character = `Ang %[1]c [U+%04[1]X] ay maaring malito sa %[2]c [U+%04[2]X]` +ambiguous_character = `Ang %[1]c [U+%04[1]X] ay maaaring malito sa %[2]c [U+%04[2]X]` escape_control_characters = I-escape unescape_control_characters = I-unescape invisible_runes_line = `Ang linya na ito ay may mga hindi nakikitang Unicode character` @@ -1335,7 +1359,7 @@ editor.must_be_on_a_branch = Dapat nasa branch ka upang gumawa o magmunkahi ng m editor.new_branch_name_desc = Bagong pangalan ng branch… editor.cancel = Kanselahin issues.role.member = Miyembro -issues.remove_request_review_block = Hindi maaring tanggalin ang hiling sa pagsuri +issues.remove_request_review_block = Hindi maaaring tanggalin ang hiling sa pagsuri issues.edit = Baguhin issues.cancel = Kanselahin issues.save = IImbak @@ -1736,7 +1760,7 @@ issues.label_exclusive_desc = Pangalanan ang label na scope/item up issues.archived_label_description = (Naka-archive) %s issues.label.filter_sort.alphabetically = Ayon sa alpabeto issues.subscribe = Mag-subscribe -issues.max_pinned = Hindi ka maaring mag-pin ng higit pang mga isyu +issues.max_pinned = Hindi ka maaaring mag-pin ng higit pang mga isyu issues.pin_comment = na-pin ito %s issues.unpin_comment = na-unpin ito %s issues.lock = I-lock ang usapan @@ -1792,7 +1816,7 @@ wiki.last_commit_info = Binago ni %s ang pahinang ito %s issues.content_history.edited = binago issues.ref_pull_from = `isinangguni ang hiling sa paghila na ito %[4]s %[2]s` pulls.merged_title_desc_few = isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s -settings.org_not_allowed_to_be_collaborator = Hindi maaring idagdag ang mga organizasyon bilang tagatulong. +settings.org_not_allowed_to_be_collaborator = Hindi maaaring idagdag ang mga organisasyon bilang tagatulong. settings.add_collaborator_success = Naidagdag ang tagatulong. settings.federation_following_repos = Mga URL ng Mga Sinusundang Repositoryo. Hinihiwalay ng ";", walang whitespace. diff.comment.reply = Tumugon @@ -1891,8 +1915,8 @@ issues.dependency.remove_info = Tanggalin ang dependency na ito issues.dependency.added_dependency = `nagdagdag ng bagong dependency %s` issues.review.dismissed_label = Nadismiss issues.review.dismissed = nadismiss ang pagsuri ni %s %s -issues.review.self.approval = Hindi mo maaring aprubahan ang sarili mong hiling sa paghila. -issues.review.self.rejection = Hindi mo maaring humiling ng pagbabago sa sarili mong hiling sa paghila. +issues.review.self.approval = Hindi mo maaaring aprubahan ang sarili mong hiling sa paghila. +issues.review.self.rejection = Hindi mo maaaring humiling ng pagbabago sa sarili mong hiling sa paghila. pulls.nothing_to_compare_have_tag = Magkapareho ang mga piniling branch/tag. issues.dependency.no_permission_1 = Wala kang pahintulot na basahin ang dependency na %d issues.dependency.no_permission_n = Wala kang pahintulot na basahin ang mga %d dependency @@ -1926,7 +1950,7 @@ activity.active_prs_count_n = %d aktibong mga hiling sa paghila issues.author.tooltip.issue = May-akda ng iysung ito ang user. issues.author.tooltip.pr = May-akda ng hiling sa paghila na ito ang user na ito. issues.dependency.add_error_dep_exists = Umiiral na and dependency. -issues.dependency.add_error_cannot_create_circular = Hindi ka maaring gumawa ng dependency na may dalawang isyu na humaharang sa isa't isa. +issues.dependency.add_error_cannot_create_circular = Hindi ka maaaring gumawa ng dependency na may dalawang isyu na humaharang sa isa't isa. issues.dependency.add_error_same_issue = Hindi mo magagwang dumepende ang isyu sa sarili. issues.dependency.add_error_dep_not_same_repo = Dapat nasa katulad na repositoryo ang mga isyu. issues.dependency.add_error_dep_issue_not_exist = Hindi umiiral ang dumedependeng isyu. @@ -1945,7 +1969,7 @@ issues.review.show_resolved = Ipakita ang naresolba issues.review.hide_resolved = Itago ang naresolba issues.review.resolve_conversation = Iresolba ang paguusap issues.review.un_resolve_conversation = I-unresolve ang paguusap -issues.blocked_by_user = Hindi ka maaring gumawa ng isyu sa repositoryo na ito dahil hinarang ka ng may-ari ng repositoryo. +issues.blocked_by_user = Hindi ka maaaring gumawa ng mga isyu sa repositoryong ito dahil hinarang ka ng may-ari ng repositoryo. issues.review.show_outdated = Ipakita ang luma issues.review.hide_outdated = Itago ang luma issues.review.resolved_by = minarkahan ang paguusap na ito bilang naresolba @@ -1963,7 +1987,7 @@ wiki.wiki_page_revisions = Mga rebisyon ng pahina settings.federation_not_enabled = Hindi naka-enable ang federation sa instansya na ito. pulls.filter_changes_by_commit = I-filter ayon sa commit settings.githooks = Mga Git hook -issues.comment.blocked_by_user = Hindi ka makakagawa ng komento sa isyu na ito dahil hinarang ka ng may-ari ng repositoryo o ng gumawa ng isyu. +issues.comment.blocked_by_user = Hindi ka maaaring gumawa ng komento sa isyu na ito dahil hinarang ka ng may-ari ng repositoryo o ng gumawa ng isyu. pulls.view = Tignan ang hiling sa paghila activity.navbar.contributors = Mga contributor activity.navbar.recent_commits = Mga kamakailang commit @@ -2042,7 +2066,7 @@ pulls.wrong_commit_id = ang commit id ay dapat ang commit id sa patutunguhan na pulls.blocked_by_changed_protected_files_1 = Hinarangan ang hiling sa paghila na ito dahil nagbabago ito ng isang nakaprotektang file: pulls.blocked_by_changed_protected_files_n = Hinarangan ang hiling sa paghila na ito dahil nagbabago ito ng mga nakaprotektang file: pulls.blocked_by_official_review_requests = Hinarangan ang hiling sa paghila na ito dahil may nawawalang pag-apruba mula sa isa o higit pang mga opisyal na tagasuri. -pulls.can_auto_merge_desc = Maaring isama ng awtomatiko ang hiling sa paghila na ito. +pulls.can_auto_merge_desc = Maaaring isama ng awtomatiko ang hiling sa paghila na ito. pulls.num_conflicting_files_n = %d mga magkasalungat na file pulls.num_conflicting_files_1 = %d magkasalungat na file issues.review.add_review_requests = humiling ng mga pagsuri mula sa %[1]s %[2]s @@ -2322,7 +2346,7 @@ settings.convert_confirm = I-convert ang repositoryo settings.webhook.replay.description_disabled = Para i-replay ang webhook na ito, i-activate ito. settings.tracker_issue_style.regexp = Regular na Ekspresyon settings.admin_stats_indexer = Taga-index ng istatistika ng code -pulls.open_unmerged_pull_exists = `Hindi ka maaring gumawa ng pagbukas-muli na operasyon dahil may nakabinbin na hiling sa paghila (#%d) na may magkatulad na katangian.` +pulls.open_unmerged_pull_exists = `Hindi ka maaaring gumawa ng pagbukas-muli na operasyon dahil may nakabinbin na hiling sa paghila (#%d) na may magkatulad na katangian.` milestones.deletion_success = Binura na ang milestone. pulls.auto_merge_has_pending_schedule = Naiskedyul ni %[1]s na awtomatiko na isama ang hiling sa paghila na ito kapag magtagumpay ang lahat ng mga pagsusuri %[2]s. issues.summary_card_alt = Pangkalahatang-ideyang card ng isyu na tawag na "%s" sa repositoryong %s @@ -2390,9 +2414,9 @@ settings.external_wiki_url_desc = Ire-redirect ang mga bisita sa URL ng panlabas settings.use_external_issue_tracker = Gumamit ng panlabas na tagasubaybay na isyu settings.wiki_desc = I-enable ang wiki ng repositoryo settings.trust_model.default.desc = Gamitin ang default na modelo ng pagkatiwala ng repositoryo para sa installation na ito. -settings.add_webhook.invalid_path = Hindi maaring maglaman ang path ng parte na "." o ".." o walang laman na string. Hindi maaring magsimula o magtapos sa slash. +settings.add_webhook.invalid_path = Hindi maaaring maglaman ang path ng parte na "." o ".." o walang laman na string. Hindi maaaring magsimula o magtapos sa slash. settings.webhook_deletion = Tanggalin ang webhook -settings.add_webhook.invalid_channel_name = Hindi maaring walang laman ang pangalan ng channel ng webhook at hindi maaring maglaman lang ng # na character. +settings.add_webhook.invalid_channel_name = Hindi maaaring walang laman ang pangalan ng channel ng webhook at hindi maaaring maglaman lang ng # na character. pulls.update_branch = I-update ang branch sa pamamagitan ng pagsama pulls.status_checks_show_all = Ipakita ang lahat ng mga pagsusuri pulls.cmd_instruction_checkout_title = I-checkout @@ -2445,8 +2469,8 @@ settings.mirror_settings.docs.doc_link_title = Paano ako mag-mirror ng mga repos settings.pull_mirror_sync_quota_exceeded = Nalagpasan ang quota, hindi hihila ng mga pagbabago. settings.mirror_settings.push_mirror.none_ssh = Wala settings.mirror_settings.push_mirror.copy_public_key = Kopyahin ang publikong key -pulls.delete_after_merge.head_branch.is_protected = Ang head branch na gusto mong burahin ay isang pinoprotektahang branch at hindi maaring burahin. -pulls.delete_after_merge.head_branch.is_default = Ang head branch na gusto mong burahin ay ang default branch at hindi maaring burahin. +pulls.delete_after_merge.head_branch.is_protected = Ang head branch na gusto mong burahin ay isang pinoprotektahang branch at hindi maaaring burahin. +pulls.delete_after_merge.head_branch.is_default = Ang head branch na gusto mong burahin ay ang default branch at hindi maaaring burahin. issues.num_reviews_few = %d mga pagsusuri issues.num_reviews_one = %d pagsusuri diff.image.swipe = I-swipe @@ -2469,7 +2493,7 @@ diff.comment.add_single_comment = Magdagdag ng iisang komento diff.comment.placeholder = Mag-iwan ng komento release.detail = Mga detalye sa release release.tags = Mga tag -release.title_empty = Hindi maaring walang laman ang paksa. +release.title_empty = Hindi maaaring walang laman ang paksa. branch.included_desc = Ang branch na ito ay kabilang ng default branch release.source_code = Source code release.edit_subheader = Inaayos ng mga release ang mga bersyon ng proyekto. @@ -2512,8 +2536,8 @@ settings.block_outdated_branch_desc = Hindi magiging posible ang pagsasama kung settings.block_rejected_reviews_desc = Hindi magiging posible ang pagsasama kapag may mga hiniling ng pagbabago ang mga opisyal na tagasuri, kahit na may sapat na pagapruba. settings.block_on_official_review_requests = Harangan ang merge sa opisyal na hiling sa pagsuri settings.tags.protection.allowed = Pinapayagan -settings.lfs_delete_warning = Ang pagbura ng LFS file ay maaring magdulot ng mga "object does not exist" na error sa checkout. Sigurado ka ba? -settings.protected_branch_required_approvals_min = Hindi maaring negatibo ang mga kinakailangang pagapruba. +settings.lfs_delete_warning = Ang pagbura ng LFS file ay maaaring magdulot ng mga "object does not exist" na error sa checkout. Sigurado ka ba? +settings.protected_branch_required_approvals_min = Hindi maaaring negatibo ang mga kinakailangang pagapruba. settings.lfs_lock_path = File path na kakandaduhin… settings.lfs_force_unlock = Pilitin ang pag-unlock settings.lfs_pointers.accessible = Naa-access ng user @@ -2524,7 +2548,7 @@ settings.protect_merge_whitelist_users = Mga naka-whitelist na user para sa pags settings.protect_merge_whitelist_teams = Mga naka-whitelist na koponan para sa pagsasama settings.protect_check_status_contexts = I-enable ang pagsusuri ng estado settings.protect_status_check_patterns = Mga pattern sa pagsusuri ng estado -settings.protect_status_check_patterns_desc = Ilagay ang mga pattern para i-specify kung aling mga pagsusuri ng estado na kailangang magpasa bago maisama ang mga branch sa isang branch na tumutugma sa rule na ito. Nagse-specify ang bawat linya ng pattern. Hindi maaring walang laman ang mga pattern. +settings.protect_status_check_patterns_desc = Ilagay ang mga pattern para i-specify kung aling mga pagsusuri ng estado na kailangang magpasa bago maisama ang mga branch sa isang branch na tumutugma sa rule na ito. Nagse-specify ang bawat linya ng pattern. Hindi maaaring walang laman ang mga pattern. settings.protect_check_status_contexts_list = Mga pagsusuri ng estado na nahanap sa huling linggo para sa repositoryo na ito diff.generated = na-generate branch.confirm_create_branch = Gumawa ng branch @@ -2674,9 +2698,9 @@ settings.protected_branch_deletion_desc = Ang pag-disable ng branch protection a settings.rename_branch_failed_protected = Hindi mababago ang pangalan ng branch na %s dahil ito ay isang nakaprotektang branch. editor.add_tmpl.filename = Pangalan ng file settings.protect_approvals_whitelist_users = Mga naka-whitelist na tagasuri -settings.protect_protected_file_patterns_desc = Ang mga nakaprotektang file ay hindi pinapayagan na direktang mabago kahit na may karapatan ang user na magdagdag, i-edit, o burahin ang mga file sa branch na ito. Ang mga maraming pattern ay maaring mahiwalay gamit ng semicolon (";"). Tignan ang %[2]s na dokumentasyon para sa pattern syntax. Mga halimbawa: .drone.yml, /docs/**/*.txt. -branch.delete_branch_has_new_commits = Hindi maaring burahin ang branch na "%s" dahil may mga bagong commit na nadagdag matapos ang pagsasama. -settings.protect_unprotected_file_patterns_desc = Ang mga hindi nakaprotektang file ay pinapayagan na direktang mabago kung may write access ang user, bina-bypass ang restriction ng pagtulak. Ang mga maraming pattern ay maaring mahiwalay gamit ng semicolon (";"). Tignan ang %[2]s na dokumentasyon para sa pattern syntax. Mga halimbawa: .drone.yml, /docs/**/*.txt. +settings.protect_protected_file_patterns_desc = Ang mga nakaprotektang file ay hindi pinapayagan na direktang mabago kahit na may karapatan ang user na magdagdag, i-edit, o burahin ang mga file sa branch na ito. Ang mga maraming pattern ay maaaring mahiwalay gamit ng semicolon (";"). Tignan ang %[2]s na dokumentasyon para sa pattern syntax. Mga halimbawa: .drone.yml, /docs/**/*.txt. +branch.delete_branch_has_new_commits = Hindi maaaring burahin ang branch na "%s" dahil may mga bagong commit na nadagdag matapos ang pagsasama. +settings.protect_unprotected_file_patterns_desc = Ang mga hindi nakaprotektang file ay pinapayagan na direktang mabago kung may write access ang user, bina-bypass ang restriction ng pagtulak. Ang mga maraming pattern ay maaaring mahiwalay gamit ng semicolon (";"). Tignan ang %[2]s na dokumentasyon para sa pattern syntax. Mga halimbawa: .drone.yml, /docs/**/*.txt. settings.no_protected_branch = Walang mga nakaprotekta na branch. settings.protected_branch_required_rule_name = Kinakailangan na pangalan ng rule settings.protected_branch_duplicate_rule_name = Mayroon nang rule para sa set ng mga branch na ito @@ -2692,7 +2716,7 @@ branch.branch_name_conflict = Sumasalungat ang pangalan ng branch na "%s" sa umi branch.protected_deletion_failed = Nakaprotekta ang branch na "%s". Hindi ito mabubura. diff.file_after = Pagkatapos release.deletion_tag_desc = Buburahin ang tag na ito sa repositoryo. Mapapanatiling hindi nabago ang nilalaman at kasaysayan ng repositoryo. Magpatuloy? -topic.format_prompt = Dapat magsimula ang mga topic ng numero o letra, maaring magsama ng mga dash ("-") at dot ("."), maaring hanggang sa 35 na character na haba. Kailangang lowercase ang mga character. +topic.format_prompt = Dapat magsimula ang mga topic ng numero o letra, maaaring magsama ng mga dash ("-") at dot ("."), maaaring hanggang sa 35 na character na haba. Kailangang lowercase ang mga character. branch.new_branch_from = Gumawa ng bagong branch mula sa "%s" error.csv.unexpected = Hindi ma-render ang file na ito dahil naglalaman ito ng hindi inaasahang character sa linyang %d at column %d. settings.ignore_stale_approvals_desc = Huwag ibilang ang mga pagapruba na ginawa sa mga lumang commit (mga lipas na pagsusuri) sa kung gaano karaming pagapruba ang mayroon sa PR na ito. Walang kinalaman kung ang mga lipas na pagsusuri ay na-dismiss na. @@ -2735,6 +2759,10 @@ archive.pull.noreview = Naka-archive ang repositoryong ito. Hindi ka makakasuri commits.view_single_diff = Tignan ang mga pagbabago sa file na ito na ipinakilala sa commit na ito pulls.editable = Nababago pulls.editable_explanation = Pinapayagan ng hiling sa paghila na ito ang mga pagbabago mula sa mga tagapangasiwa. Maaari kang direktang mag-ambag dito. +issues.reopen.blocked_by_user = Hindi mo maaaring buksan muli ang isyung ito dahil hinarang ka ng may-ari ng repositoryo o ng may-akda ng isyung ito. +pulls.comment.blocked_by_user = Hindi ka maaaring magkomento sa hiling sa paghila na ito dahil hinarang ka ng may-ari ng repositoryo o ng may-akda ng hiling sa paghila. +issues.filter_no_results = Walang mga resulta +issues.filter_no_results_placeholder = Subukang ayusin ang iyong mga filter sa paghahanap. [search] commit_kind = Maghanap ng mga commit… @@ -2845,11 +2873,11 @@ users.repos = Mga Repo users.send_register_notify = Abisuhan tungkol sa pagrehistro sa pamamagitan ng email users.is_admin = Tagapangasiwa na account users.is_restricted = Pinaghihigpitang account -users.allow_import_local = Maaring mag-import ng mga lokal na repositoryo +users.allow_import_local = Maaaring mag-import ng mga lokal na repositoryo users.allow_create_organization = Makakagawa ng mga organisasyon users.update_profile = I-update ang user account users.delete_account = Burahin ang user account -users.cannot_delete_self = Hindi mo maaring burahin ang sarili mo +users.cannot_delete_self = Hindi mo maaaring burahin ang sarili mo users.still_own_repo = Ang user na ito ay nagmamay-ari pa ng isa o higit pang mga repositoryo. Burahin o ilipat sila muna. users.list_status_filter.is_active = Aktibo users.list_status_filter.not_active = Hindi aktibo @@ -3120,7 +3148,7 @@ config.allow_dots_in_usernames = Payagan ang mga user na gumamit ng mga dot sa k config.https_only = HTTPS lamang auths.tip.github = Magrehistro ng bagong OAuth application sa %s auths.tip.gitlab_new = Magrehistro ng bagong application sa %s -emails.delete_primary_email_error = Hindi mo maaring burahin ang pangunahing email. +emails.delete_primary_email_error = Hindi mo maaaring burahin ang pangunahing email. config.provider_config = Config ng provider config.cache_test_slow = Matagumpay ang pagsubok ng cache, ngunit mabagal ang tugon: %s. config.picture_config = Configuration ng larawan at avatar @@ -3271,7 +3299,7 @@ config.cookie_name = Pangalan ng cookie config.gc_interval_time = Oras ng pagitan ng GC config.cookie_life_time = Lifetime ng cookie config.git_clone_timeout = Timeout ng operasyon na pag-clone -monitor.process.cancel_desc = Ang pagkansela ng proseso ay maaring magdulot ng pagkawalan ng data +monitor.process.cancel_desc = Ang pagkansela ng proseso ay maaaring magdulot ng pagkawalan ng data monitor.queue.name = Pangalan auths.oauth2_required_claim_value_helper = Itakda ang value na ito upang i-restrict ang pag-login mula sa pinagmulang ito sa mga user na may claim na may ganitong pangalan at value auths.tip.bitbucket = Magrehistro ng bagong OAuth consumer sa %s at idagdag ang pahintulot na "Account" - "Read" @@ -3358,7 +3386,7 @@ members.member = Miyembro members.private_helper = Gawing visible settings.location = Lokasyon settings.update_setting_success = Nabago na ang mga setting ng organisasyon. -teams.can_create_org_repo_helper = Maaring gumawa ang mga miyembro ng mga bagong repositoryo sa organisasyon. Magkakaroon ng tagapangasiwa na access ang tagagawa sa bagong repositoryo. +teams.can_create_org_repo_helper = Maaaring gumawa ang mga miyembro ng mga bagong repositoryo sa organisasyon. Magkakaroon ng tagapangasiwa na access ang tagagawa sa bagong repositoryo. settings.change_orgname_prompt = Tandaan: Ang pagpalit ng pangalan ng organisasyon ay papalitan din ang URL ng organisasyon at mapapalaya ang lumang pangalan. settings.labels_desc = Magdagdag ng mga label na magagamit sa mga isyu para sa lahat ng mga repositoryo sa ilalim ng organisasyon. members.public_helper = Gawing nakatago @@ -3583,7 +3611,7 @@ search_in_external_registry = Maghanap sa %s alt.registry = I-setup ang registry na ito mula sa command line: alt.registry.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: alt.install = I-install ang package -alt.setup = Idagdag ang repositoryo sa listahan ng mga nakakonektang repositoryo (piliin ang kinakailangang architechture sa halip ng '_arch_'): +alt.setup = Idagdag ang repositoryo sa listahan ng mga nakakonektang repositoryo (piliin ang kinakailangang architechture sa halip ng "_arch_"): alt.repository = Info ng repositoryo alt.repository.architectures = Mga architechture alt.repository.multiple_groups = Available ang package na ito sa iba't ibang grupo. @@ -3641,7 +3669,7 @@ variables.deletion.description = Permanente ang pagtanggal ng isang variable at status.running = Tumatakbo runners.new_notice = Paano magsimula ng runner runners.update_runner_success = Matagumpay na na-update ang runner -runners.delete_runner_notice = Kapag may trabaho na tumatakbo sa runner na ito, titigilan ito at mamarkahan bilang nabigo. Maaring sirain ang building workflow. +runners.delete_runner_notice = Kapag may trabaho na tumatakbo sa runner na ito, titigilan ito at mamarkahan bilang nabigo. Maaaring sirain ang building workflow. runners.none = Walang mga available na runner runs.status_no_select = Lahat ng status runs.empty_commit_message = (walang laman na mensahe ng commit) @@ -3781,7 +3809,7 @@ error.unit_not_allowed = Hindi ka pinapayagang ma-access ang seksyon ng reposito [dropzone] default_message = I-drop ang mga file o mag-click dito para mag-upload. -invalid_input_type = Hindi ka maaring mag-upload ng mga file sa uri na ito. +invalid_input_type = Hindi ka maaaring mag-upload ng mga file sa uri na ito. file_too_big = Ang laki ng file ({{filesize}}) MB) ay lumalagpas sa pinakamataas na size na ({{maxFilesize}} MB). remove_file = Tanggalin ang file diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index b7372e12f2..d2d960b627 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -19,8 +19,8 @@ language = Teanga notifications = Fógraí active_stopwatch = Rianaitheoir Ama Gníomhach tracked_time_summary = Achoimre ar an am rianaithe bunaithe ar scagairí an liosta eisiúna -create_new = Cruthaigh... -user_profile_and_more = Próifíl agus Socruithe... +create_new = Cruthaigh… +user_profile_and_more = Próifíl agus Socruithe… signed_in_as = Sínithe isteach mar enable_javascript = Éilíonn JavaScript ar an suíomh Gréasáin seo. toc = Tábla na nÁbhar diff --git a/options/locale/locale_gl.ini b/options/locale/locale_gl.ini index 6583eca499..75763775eb 100644 --- a/options/locale/locale_gl.ini +++ b/options/locale/locale_gl.ini @@ -187,6 +187,9 @@ buttons.switch_to_legacy.tooltip = Utilizar o editor herdado buttons.new_table.tooltip = Engadir táboa table_modal.header = Engadir táboa table_modal.placeholder.header = Cabeceira +link_modal.header = Engadir ligazón +link_modal.url = Url +link_modal.description = Descrición [search] @@ -221,6 +224,7 @@ platform = Multiplataforma app_desc = Um servizo Git autoxestionado e fácil de usar install = Fácil de instalar install_desc = Simplemente executa o binario para a túa plataforma, envíao con Docker ou consígueo empaquetado. +license = Código aberto [error] occurred = Ocorreu un erro @@ -264,4 +268,26 @@ sqlite_helper = Ruta do ficheiro para a base de datos SQLite3.
Introduza unha reinstall_confirm_message = A reinstalación cunha base de datos Forgejo existente pode causar varios problemas. Na maioría dos casos, deberías usar o teu "app.ini" existente para executar Forgejo. Se sabes o que estás facendo, confirma o seguinte: reinstall_confirm_check_1 = É posible que se perdan os datos cifrados pola SECRET_KEY en app.ini: é posible que os usuarios non poidan iniciar sesión con 2FA/OTP e que os espellos non funcionen correctamente. Ao marcar esta caixa, confirmas que o ficheiro app.ini actual contén a SECRET_KEY correcta. disable_gravatar.description = Desactiva o uso de Gravatar ou outras fontes de avatares de terceiros. As imaxes predeterminadas utilizaranse para os avatares dos usuarios a menos que carguen o seu propio avatar na instancia. -federated_avatar_lookup = Activar avatares federados \ No newline at end of file +federated_avatar_lookup = Activar avatares federados +repo_path = Ruta raíz do repositorio +run_user = O usuario co que executar +password = Contrasinal +repo_path_helper = Os repositorios Git remotos gardaránse neste directorio. +lfs_path = Ruta raíz de Git LFS +install = Instalación +db_title = Configuración da base de datos +db_name = Nome da base de datos +db_schema = Esquema +db_schema_helper = Déixao baleiro para a base de datos por defecto ("public"). +app_name = Título da instancia +user = Nome de usuario +general_title = Opcións xerais +app_name_helper = Escribe o nome da túa instancia aqui. Será amosado en cada páxina. +mailer_user = Usuario SMTP +mailer_password = Contrasinal SMTP +title = Configuración inicial +db_type = Tipo de base de datos +app_slogan = Slogan da instancia +app_slogan_helper = Escribe o slogan da túa instancia aqui. Ou deixao baleiro para desabilitala. +domain = Dominio do servidor +ssh_port = Porto do servidor SSH \ No newline at end of file diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index e01064588f..dade632016 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -7,7 +7,7 @@ logo=Logo sign_in=Pieteikties sign_in_with_provider=Pieteikties ar %s sign_in_or=vai -sign_out=Attekties +sign_out=Atteikties sign_up=Reģistrēties link_account=Sasaistīt kontu register=Reģistrēties @@ -162,7 +162,7 @@ filter.not_archived = Nav arhivētas filter.is_fork = Atzarojumi filter.not_fork = Nav atzarojumi filter.is_mirror = Spoguļglabātavas -filter.public = Publiskas +filter.public = Atklātas filter.private = Privātas filter.clear = Notīrīt atlasi confirm_delete_artifact = Vai tiešām izdzēst artefaktu '%s'? @@ -206,6 +206,10 @@ buttons.indent.tooltip = Pārvietot vienumus vienu līmeni dziļāk buttons.unindent.tooltip = Pārvietot vienumus vienu līmeni augstāk buttons.new_table.tooltip = Pievienot tabulu table_modal.header = Pievienot tabulu +link_modal.header = Pievienot saiti +link_modal.url = URL +link_modal.description = Apraksts +link_modal.paste_reminder = Norāde: starpliktuvē esošu URL var ielīmēt uzreiz redaktorā, lai izveidotu saiti. [filter] string.asc=A - Z @@ -298,7 +302,7 @@ disable_gravatar.description=Atspējot Gravatar un citu trešo pušu attēlu avo federated_avatar_lookup=Iespējot apvienotos profila attēlus federated_avatar_lookup.description=Uzmeklēt profila attēlus ar Libravatar. disable_registration=Atspējot pašreģistrēšanos -disable_registration.description=Tikai servera pārvaldītāji varēs izveidot jaunus lietotāju kontus. Ir ļoti ieteicams reģistrēšanos paturēt atspējotu, ja vien nav iecerēts mitināt visiem pieejamu publisku serveri un ir gatavība tikt galā ar lielu negodprātīgu kontu skaitu. +disable_registration.description=Tikai servera pārvaldītāji varēs izveidot jaunus lietotāju kontus. Ir ļoti ieteicams reģistrēšanos paturēt atspējotu, ja vien nav iecerēts mitināt visiem pieejamu serveri un ir gatavība tikt galā ar lielu negodprātīgu kontu skaitu. allow_only_external_registration.description=Lietotāji varēs izveidot jaunos kontus tikai izmantojot konfigurētus ārējos pakalpojumus. openid_signin=Iespējot pieteikšanos ar OpenID openid_signin.description=Ļaut lietotājiem pieteikties ar OpenID. @@ -370,9 +374,9 @@ show_only_archived=Attēlot tikai arhivētos show_only_unarchived=Attēlot tikai nearhivētos show_private=Privāts -show_both_private_public=Attēlot gan publiskos, gan privātos +show_both_private_public=Rāda gan atklātās, gan privātās show_only_private=Attēlot tikai privātos -show_only_public=Attēlot tikai publiskos +show_only_public=Attēlo tikai atklātās issues.in_your_repos=Manās glabātavās @@ -470,7 +474,7 @@ authorize_title=Pilnvarot "%s" piekļuvi Tavam kontam? authorization_failed=Pilnvarošana neizdevās authorization_failed_desc=Pilnvarošana neizdevās, jo tika noteikts nederīgs pieprasījums. Lūgums sazināties ar lietotnes, no kuras tika veikts pilnvarošanas pieprasījums, uzturētāju. sspi_auth_failed=SSPI autentifikācija neizdevās -password_pwned=Izvēlētā parole ir nozagto paroļu sarakstā, kas iepriekš ir atklāts publiskās datu noplūdēs. Lūgums mēģināt vēlreiz ar citu paroli un apsvērt to nomainīt arī citur. +password_pwned=Izvēlētā parole ir nozagto paroļu sarakstā, kas iepriekš ir atklāts pieejamās datu noplūdēs. Lūgums mēģināt vēlreiz ar citu paroli un apsvērt to nomainīt arī citur. password_pwned_err=Neizdevās pabeigt pieprasījumu uz HaveIBeenPwned back_to_sign_in = Atpakaļ uz pieteikšanos unauthorized_credentials = Pieteikšanās dati ir nepareizi vai ir izbeigušies. Jāizpilda komanda atkārtoti vai jāizmanto %s, lai iegūtu vairāk informācijas @@ -686,7 +690,7 @@ email_domain_is_not_allowed = Lietotāja e-pasta adreses %s domēna vārd change_avatar=Mainīt profila attēlu… joined_on=Pievienojās %s repositories=Glabātavas -activity=Publiskie notikumi +activity=Atklāti notikumi followers_few=%d sekotāji starred=Izlasei pievienotās glabātavas watched=Vērotās glabātavas @@ -745,7 +749,7 @@ organization=Apvienības uid=UID webauthn=Divpakāpju pieteikšanās (drošības atslēgas) -public_profile=Publiskais profils +public_profile=Visiem pieejamais profils biography_placeholder=Pastāsti citiem mazliet par sevi! (Tiek atbalstīts Markdown) location_placeholder=Kopīgot savu aptuveno atrašanās vietu ar citiem profile_desc=Par Tevi @@ -859,7 +863,7 @@ add_new_principal=Pievienot identitāti ssh_key_been_used=Šī SSH atslēga jau ir pievienota šajā serverī. ssh_key_name_used=SSH atslēga ar tādu pašu nosaukumu jau ir kontā. ssh_principal_been_used=Šāda identitāte jau ir pievienota šājā serverī. -gpg_key_id_used=Jau pastāv publiskā GPG atslēga ar tādu pašu identifikatoru. +gpg_key_id_used=Jau pastāv publiska GPG atslēga ar tādu pašu identifikatoru. gpg_no_key_email_found=Šī GPG atslēga neatbilst nevienai ar kontu saistītajai e-pasta adresei. To joprojām var pievienot, ja tiek parakstīta norādītā pilnvara. gpg_key_matched_identities=Atbilstošās identitātes: gpg_key_matched_identities_long=Šajā atslēgā iegultās identitātes atbilst zemāk uzskaitītājām aktivētajām šī lietotāja e-pasta adresēm. Iesūtījumus, kas atbilst šīm e-pasta adresēm, var apliecināt ar šo atslēgu. @@ -936,8 +940,8 @@ access_token_deletion_confirm_action=Dzēst access_token_deletion_desc=Pilnvaras izdzēšana atsauks lietotņu, kas to izmanto, piekļuvi kontam. Šo darbību nevar atsaukt. Turpināt? delete_token_success=Pilnvara tika izdzēsta. Lietotnēm, kas to izmanto, vairs nav piekļuves kontam. repo_and_org_access=Glabātavas un apvienības piekļuve -permissions_public_only=Tikai publiskās -permissions_access_all=Visas (publiskā, privātās un ierobežotās) +permissions_public_only=Tikai atklātās +permissions_access_all=Visas (atklātās, privātās un ierobežotās) select_permissions=Atlasīt atļaujas permission_no_access=Nav piekļuves permission_read=Lasīt @@ -1031,14 +1035,14 @@ email_notifications.submit=Iestatīt e-pasta iestatījumus email_notifications.andyourown=Un manus paziņojumus visibility=Lietotāja redzamība -visibility.public=Publiska +visibility.public=Atklāta visibility.public_tooltip=Redzams ikvienam visibility.limited=Ierobežota visibility.limited_tooltip=Redzams tikai lietotājiem, kuri ir pieteikušies visibility.private=Privāta visibility.private_tooltip=Redzams tikai apvienību, kurās pievienojies, dalībniekiem change_password = Mainīt paroli -keep_activity_private.description = Tavas publiskās darbības būs redzamas tikai Tev un servera pārvaldītājiem. +keep_activity_private.description = Tavas atklātās darbības būs redzamas tikai Tev un servera pārvaldītājiem. update_hints = Atjaunināt norādes update_hints_success = Norādes tika atjauninātas. user_block_success = Lietotājs tika sekmīgi liegts. @@ -1061,6 +1065,26 @@ change_username_redirect_prompt.with_cooldown.one = Vecais lietotājvārds būs change_username_redirect_prompt.with_cooldown.few = Vecais lietotājvārds būs pieejams visiem pēc noilguma, kas ir %[1]d dienas. Šajā laikā ir iespējams to atkal sākt izmantot. keep_pronouns_private = Vietniekvārdus rādīt tikai lietotājiem, kuri ir pieteikušies keep_pronouns_private.description = Šis paslēps vietniekvārdus no apmeklētājiem, kuri nav pieteikušies. +quota.sizes.assets.all = Līdzekļi +quota.sizes.git.lfs = Git LFS +quota.applies_to_user = Uz kontu attiecas zemāk esošās ierobežojuma kārtulas +quota.rule.exceeded.helper = Kopējais šīs kārtulas objektu izmērs pārsniedz ierobežojumu. +quota.sizes.git.all = Git saturs +quota.rule.exceeded = Pārsniegts +quota.sizes.assets.attachments.all = Pielikumi +quota.sizes.assets.attachments.issues = Pieteikumu pielikumi +quota.sizes.assets.attachments.releases = Laidienu pielikumi +quota.sizes.assets.artifacts = Artefakti +quota.sizes.assets.packages.all = Pakotnes +quota.sizes.wiki = Vikivietne +storage_overview = Krātuves pārskats +quota = Ierobežojums +quota.applies_to_org = Uz apvienību attiecas zemāk esošās ierobežojuma kārtulas +quota.rule.no_limit = Neierobežots +quota.sizes.all = Viss +quota.sizes.repos.all = Glabātavas +quota.sizes.repos.public = Atklātās glabātavas +quota.sizes.repos.private = Privātās glabātavas [repo] new_repo_helper=Glabātava satur visas projekta datnes, tajā skaitā izmaiņu vēsturi. Jau tiek izmantota kaut kur citur? Pārcelt glabātavu. @@ -1167,7 +1191,7 @@ transfer.no_permission_to_accept=Nav atļaujas pieņemt šo nodošanu. transfer.no_permission_to_reject=Nav atļaujas noraidīt šo nodošanu. desc.private=Privāts -desc.public=Publisks +desc.public=Atklāts desc.template=Sagatave desc.internal=Iekšējs desc.archived=Arhivēts @@ -1513,7 +1537,7 @@ issues.choose.open_external_link=Atvērt issues.choose.blank=Noklusējuma issues.choose.blank_about=Izveidot pieteikumu no noklusējuma sagataves. issues.choose.ignore_invalid_templates=Kļūdainās sagataves tika izlaistas -issues.choose.invalid_templates=atrasta(s) %v ķļūdaina(s) sagatave(s) +issues.choose.invalid_templates=atrasta(s) %v nederīgas(s) sagatave(s) issues.choose.invalid_config=Pieteikumu konfigurācija satur kļūdas: issues.no_ref=Nav norādīts zars/birka issues.create=Izveidot pieteikumu @@ -1581,8 +1605,8 @@ issues.filter_sort.mostcomment=Visvairāk piebilžu issues.filter_sort.leastcomment=Vismazāk piebilžu issues.filter_sort.nearduedate=Tuvākais termiņš issues.filter_sort.farduedate=Tālākais termiņš -issues.filter_sort.moststars=Visvairāk atzīmētie -issues.filter_sort.feweststars=Vismazāk atzīmētie +issues.filter_sort.moststars=Visvairāk zvaigžņu +issues.filter_sort.feweststars=Vismazāk zvaigžņu issues.filter_sort.mostforks=Visvairāk atzarojumu issues.filter_sort.fewestforks=Vismazāk atzarojumu issues.keyword_search_unavailable=Meklēšana pēc atslēgvārda pašreiz nav pieejama. Lūgums sazināties ar vietnes administratoru. @@ -1594,7 +1618,7 @@ issues.action_milestone_no_select=Nav atskaites punkta issues.action_assignee=Atbildīgais issues.action_assignee_no_select=Nav atbildīgā issues.action_check=Atzīmēt/Notīrīt -issues.action_check_all=Atzīmēt/Notīrīt visus ierakstus +issues.action_check_all=Atzīmēt/Notīrīt visus vienumus issues.opened_by=%[3]s atvēra %[1]s pulls.merged_by=%[3]s iekļāva %[1]s pulls.merged_by_fake=%[2]s iekļāva %[1]s @@ -1722,7 +1746,7 @@ issues.del_time=Izdzēst šo laika žurnāla ierakstu issues.add_time_short=Pievienot laiku issues.add_time_cancel=Atcelt issues.add_time_history=` pievienoja patērēto laiku %s` -issues.del_time_history=`izdzēsa patērētais laiks %s` +issues.del_time_history=`izdzēsa patērēto laiku %s` issues.add_time_hours=Stundas issues.add_time_minutes=Minūtes issues.add_time_sum_to_small=Nav norādīts laiks. @@ -1805,7 +1829,7 @@ issues.review.show_resolved=Rādīt atrisināto issues.review.hide_resolved=Paslēpt atrisināto issues.review.resolve_conversation=Atrisināt sarunu issues.review.un_resolve_conversation=Atcelt sarunas atrisinājumu -issues.review.resolved_by=atzīmēja sarunu kā atrisinātu +issues.review.resolved_by=atzīmēja šo sarunu kā atrisinātu issues.assignee.error=Ne visi atbildīgie tika pievienoti, jo radās neparedzēta kļūda. issues.reference_issue.body=Saturs issues.content_history.deleted=izdzēsts @@ -2000,7 +2024,7 @@ signing.wont_sign.error=Atgadījās kļūda pārbaudot, vai iesūtījums var tik signing.wont_sign.nokey=Nav pieejamas atslēgas, ar ko parakstīt šo iesūtījumu. signing.wont_sign.never=Iesūtījumi nekad netiek parakstīti. signing.wont_sign.always=Iesūtījumi vienmēr tiek parakstīti. -signing.wont_sign.pubkey=Iesūtījums netiks parakstīts, jo kontam nav piesaistīta publiskā atslēga. +signing.wont_sign.pubkey=Iesūtījums netiks parakstīts, jo kontam nav piesaistīta publiska atslēga. signing.wont_sign.twofa=Jābūt iespējotai divpakāpju autentificēšanai, lai parakstītu iesūtījumus. signing.wont_sign.parentsigned=Iesūtījums netiks parakstīts, jo nav parakstīts cilmes iesūtījums. signing.wont_sign.basesigned=Apvienošana netiks parakstīta, jo pamata iesūtījums nav parakstīts. @@ -2081,7 +2105,7 @@ activity.unresolved_conv_desc=Šie nesen mainītie pieteikumi un izmaiņu piepra activity.unresolved_conv_label=Atvērts activity.title.releases_1=%d laidiens activity.title.releases_n=%d laidieni -activity.title.releases_published_by=%s publicēja %s +activity.title.releases_published_by=%s laida klājā %s activity.published_release_label=Laidiens activity.no_git_activity=Šajā laika periodā nav notikušas nekādas izmaiņas. activity.git_stats_exclude_merges=Neskaitot apvienošanas iesūtījumus, @@ -2444,7 +2468,7 @@ settings.protect_status_check_patterns_desc=Jāievada paraugi, lai norādītu, k settings.protect_check_status_contexts_desc=Pirms apvienošanas ir nepieciešama sekmīga stāvokļa pārbaužu izpilde. Kad iespējots, iesūtījumiem vispirms jābūt aizgādātiem citā zarā, tad pēc stāvokļa pārbaužu sekmīgas izpildes iekļautiem vai aizgādātiem tieši zarā, kas atbilst šai kārtulai. Ja nav atbilstošu kontekstu, pēdējam iesūtījumam jābūt sekmīgam neatkarīgi no konteksta. settings.protect_check_status_contexts_list=Stāvokļa pārbaudes, kas šajā glabātavā atrastas pēdējās nedēļas laikā settings.protect_status_check_matched=Atbilst -settings.protect_invalid_status_check_pattern=Kļūdains statusa pārbaudes šablons: "%s". +settings.protect_invalid_status_check_pattern=Nederīgs stāvokļa pārbaudes paraugs: "%s". settings.protect_no_valid_status_check_patterns=Nav derīgu stāvokļa pārbaužu paraugu. settings.protect_required_approvals=Nepieciešamie apstiprinājumi settings.protect_required_approvals_desc=Atļaut iekļaut izmaiņu pieprasījumu tikai ar pietiekamu daudzumu apstiprinošu izskatīšanu. @@ -2458,7 +2482,7 @@ settings.require_signed_commits=Pieprasīt parakstītus iesūtījumus settings.require_signed_commits_desc=Noraidīt aizgādāšanu uz šo zaru, ja iesūtījumi nav parakstīti vai apliecināmi. settings.protect_branch_name_pattern=Aizsargātā zara nosaukuma paraugs settings.protect_branch_name_pattern_desc=Aizsargāto zaru nosaukumu paraugi. Paraugu pierakstu skatīt dokumentācijā. Piemēri: main, release/** -settings.protect_patterns=Šabloni +settings.protect_patterns=Paraugi settings.protect_protected_file_patterns=Aizsargāto datņu paraugs (vairākus atdala ar semikolu ";") settings.protect_protected_file_patterns_desc=Aizsargātās datnes nav ļauts tiešā veidā mainīt, pat ja lietotājam šajā zarā ir tiesības pievienot, labot vai izdzēst datnes. Vairākus paraugus var atdalīt ar semikolu (";"). Paraugu pieraksts ir skatāms %[2]s dokumentācijā. Piemēri: .drone.yml, /docs/**/*.txt. settings.protect_unprotected_file_patterns=Neaizsargāto datņu paraugs (vairākus atdala ar semikolu ";") @@ -2517,7 +2541,7 @@ settings.unarchive.error=Glabātavas arhivēšanas atcelšanas laikā atgadījā settings.update_avatar_success=Glabātavas attēls tika atjaunināts. settings.lfs=LFS settings.lfs_filelist=Šajā glabātavā uzglabātās LFS datnes -settings.lfs_no_lfs_files=Šajā glabātavā nav uzglabātu LFS datņu +settings.lfs_no_lfs_files=Šajā glabātavā netiek glabātas LFS datnes settings.lfs_findcommits=Atrast iesūtījumus settings.lfs_lfs_file_no_commits=Šai LFS datnei netika atrasts neviens iesūtījums settings.lfs_noattribute=Šim ceļam noklusējuma zarā nav slēdzamības atribūta @@ -2533,8 +2557,8 @@ settings.lfs_lock_path=Slēdzamās datnes ceļš... settings.lfs_locks_no_locks=Nav slēdzeņu settings.lfs_lock_file_no_exist=Aizslēgtā datne nepastāv noklusējuma zarā settings.lfs_force_unlock=Uzspiest atslēgšanu -settings.lfs_pointers.found=Atrasta(s) %d binārā objekta norāde(s) - %d saistītas, %d nesaistītas (%d trūkstošas glabātuvē) -settings.lfs_pointers.sha=Binārā objekta jaucējkods +settings.lfs_pointers.found=Atrasta(s) %d binārā objekta norāde(s) - %d saistīta(s), %d nesaistīta(s) (%d trūkst krātuvē) +settings.lfs_pointers.sha=Binārā objekta jaucējvirkne settings.lfs_pointers.oid=OID settings.lfs_pointers.inRepo=Glabātavā settings.lfs_pointers.exists=Pastāv krātuvē @@ -2564,7 +2588,7 @@ diff.whitespace_ignore_all_whitespace=Neņemt vērā atstarpes, kad tiek salīdz diff.whitespace_ignore_amount_changes=Neņemt vērā atstarpju daudzuma izmaiņas diff.whitespace_ignore_at_eol=Neņemt vērā atstarpju izmaiņas rindu beigās diff.stats_desc=%d izmainītas datnes ar %d papildinājumiem un %d izdzēšanām -diff.stats_desc_file=%d izmaiņas: %d pievienotas un %d dzēstas +diff.stats_desc_file=%d izmaiņas: %d pievienošanas un %d izdzēšanas diff.bin=Binārs diff.bin_not_shown=Binārā datne netiek rādīta. diff.view_file=Apskatīt datni @@ -2615,7 +2639,7 @@ release.stable=Stabila release.compare=Salīdzināt release.edit=Labot release.ahead.commits=%d iesūtījumi -release.ahead.target=no %s kopš laidiena publicēšanas +release.ahead.target=%s kopš šī laidiena laišanas klajā tag.ahead.target=%s kopš šīs birkas release.source_code=Pirmkods release.new_subheader=Laidieni apkopo projekta versijas. @@ -2629,7 +2653,7 @@ release.title=Laidiena nosaukums release.title_empty=Nosaukums nevar būt tukšs. release.message=Aprakstīt šo laidienu release.prerelease_desc=Atzīmēt kā pirmsizlaidi -release.prerelease_helper=Atzīmēt, ka šo laidienu nav ieteicams lietot produkcijā. +release.prerelease_helper=Atzīmēt šo laidienu kā nepiemērotu izmantošanai produkcijā. release.cancel=Atcelt release.publish=Laist klajā laidienu release.save_draft=Saglabāt melnrakstu @@ -2640,10 +2664,10 @@ release.deletion=Izdzēst laidienu release.deletion_desc=Laidiena izdzēšana tikai noņem to no Forgejo. Tā neietekmēs Git birku, glabātavas saturu vai vēsturi. Turpināt? release.deletion_success=Laidiens tika izdzēsts. release.deletion_tag_desc=Šī birka tiks izdzēsta no glabātavas. Glabātavas saturs un vēsture paliks nemainīta. Turpināt? -release.deletion_tag_success=Tags tika izdzēsts. +release.deletion_tag_success=Birka tika izdzēsta. release.tag_name_already_exist=Laidiens ar šādu birkas nosaukumu jau pastāv. release.tag_name_invalid=Nederīgs birkas nosaukums. -release.tag_name_protected=Taga nosaukums ir aizsargāts. +release.tag_name_protected=Birkas nosaukums ir aizsargāts. release.tag_already_exist=Šāds birkas nosaukums jau pastāv. release.downloads=Lejupielādes release.download_count=Lejupielādes: %s @@ -2693,7 +2717,7 @@ tag.create_tag_operation=Izveidot birku tag.confirm_create_tag=Izveidot birku tag.create_tag_from=Izveidot jaunu birku no "%s" -tag.create_success=Tags "%s" tika izveidots. +tag.create_success=Birka "%s" tika izveidota. topic.manage_topics=Pārvaldīt tēmas topic.done=Gatavs @@ -2875,6 +2899,10 @@ editor.commit_email = Iesūtījuma e-pasta adrese commits.view_single_diff = Apskatīt šajā datnē veiktās izmaiņas šajā iesūtījumā pulls.editable = Labojams pulls.editable_explanation = Šis izmaiņu pieprasījums pieļauj labojumus no uzturētājiem. Tu vari tieši līdzdarboties tajā. +issues.reopen.blocked_by_user = Tu nevari atkārtoti atvērt šo pieteikumu, jo tā izveidotājs vai glabātavas īpašnieks ir liedzis Tevi. +pulls.comment.blocked_by_user = Tu šim izmaiņu pieprasījumam nevari pievienot piebildi, jo tā izveidotājs vai glabātavas īpašnieks ir liedzis Tevi. +issues.filter_no_results = Nav vienumu +issues.filter_no_results_placeholder = Jāmēģina pielāgot meklēšanas atlasītāji. [graphs] component_loading=Ielādē %s... @@ -2921,7 +2949,7 @@ settings.location=Atrašanās vieta settings.permission=Tiesības settings.repoadminchangeteam=Glabātavas pārvaldītājs var pievienot un noņemt komandu piekļuvi settings.visibility=Redzamība -settings.visibility.public=Publiska +settings.visibility.public=Atklāta settings.visibility.limited=Ierobežota (redzama tikai lietotājiem, kuri ir pieteikušies) settings.visibility.limited_shortname=Ierobežota settings.visibility.private=Privāta (redzama tikai apvienības dalībniekiem) @@ -2946,19 +2974,19 @@ members.membership_visibility=Dalībnieku redzamība: members.public=Redzams members.public_helper=Padarīt slēptu members.private=Slēpts -members.private_helper=Padarīt redzemu +members.private_helper=Padarīt redzamu members.member_role=Dalībnieka loma: members.owner=Īpašnieks members.member=Dalībnieks members.remove=Noņemt members.remove.detail=Noņemt %[1]s no %[2]s? -members.leave=Atstāt +members.leave=Pamest members.leave.detail=Vai tiešām pamest apvienību "%s"? -members.invite_desc=Pievienot jaunu dalībnieku pie %s: +members.invite_desc=Pievienot jaunu dalībnieku %s: members.invite_now=Uzaicināt tagad teams.join=Pievienoties -teams.leave=Atstāt +teams.leave=Pamest teams.leave.detail=Vai tiešām pamest komandu "%s"? teams.can_create_org_repo=Izveidot glabātavas teams.can_create_org_repo_helper=Dalībnieki apvienībā var izveidot jaunas glabātavas. Izveidotājs iegūs jaunās glabātavas pārvaldītāja piekļuvi. @@ -2966,11 +2994,11 @@ teams.none_access=Nav piekļuves teams.none_access_helper="Nav piekļuve" iespēja iedarbojas tikai privātās glabātavās. teams.general_access=Pielāgota piekļuve teams.general_access_helper=Komandas tiesības tiks noteiktas pēc tabulas zemāk. -teams.read_access=Skatīšanās +teams.read_access=Lasīt teams.read_access_helper=Komanda varēs skatīties un klonēt šīs organizācijas repozitorijus. -teams.write_access=Rakstīšanas +teams.write_access=Rakstīt teams.write_access_helper=Šī komanda varēs lasīt un nosūtīt izmaiņas uz tās repozitorijiem. -teams.admin_access=Pārvaldītaja piekļuve +teams.admin_access=Pārvaldītāja piekļuve teams.admin_access_helper=Dalībnieki var atgādāt un aizgādāt izmaiņas uz komandas glabātavām un pievienot tām līdzdalībniekus. teams.no_desc=Komandai nav apraksta teams.settings=Iestatījumi @@ -3027,7 +3055,7 @@ authentication=Autentificēšanas avoti emails=Lietotāju e-pasta adreses config=Konfigurācija notices=Sistēmas paziņojumi -monitor=Uzraudzība +monitor=Pārraudzīšana first_page=Pirmā last_page=Pēdējā total=Kopā: %d @@ -3178,8 +3206,8 @@ users.details=Lietotāja informācija emails.email_manage_panel=Pārvaldīt lietotāju e-pasta adreses emails.primary=Galvenā emails.activated=Aktivēta -emails.filter_sort.email=E-pasts -emails.filter_sort.email_reverse=E-pasta adrese (pretēji alfabētiski) +emails.filter_sort.email=E-pasta adrese +emails.filter_sort.email_reverse=E-pasta adrese (apvērsti) emails.filter_sort.name=Lietotājvārds emails.filter_sort.name_reverse=Lietotāja vārds (apvērsti) emails.updated=E-pasta adrese atjaunināta @@ -3195,11 +3223,11 @@ orgs.members=Dalībnieki orgs.new_orga=Jauna apvienība repos.repo_manage_panel=Pārvaldīt glabātavas -repos.unadopted=Nepieemtās glabātavas +repos.unadopted=Nepieņemtās glabātavas repos.unadopted.no_more=Nav atrasta neviena nepieņemta glabātava. repos.owner=Īpašnieks repos.name=Nosaukums -repos.private=Privāts +repos.private=Privāta repos.watches=Vērošana repos.stars=Zvaigznes repos.forks=Atdalītie @@ -3219,7 +3247,7 @@ packages.version=Versija packages.type=Veids packages.repository=Glabātava packages.size=Izmērs -packages.published=Publicēts +packages.published=Laista klajā defaulthooks=Noklusējuma tīmekļa aizķeres defaulthooks.desc=Tīmekļa aizķeres automātiski nosūta HTTP POST pieprasījumus serverim, kad iestājas noteikti Forgejo notikumi. Šeit esošās tīmekļa aizķeres ir noklusējuma, un tās tiks ievietotas visās jaunajās glabātavās. Vairāk ir lasāms norādēs par tīmekļa aizķerēm. @@ -3306,7 +3334,7 @@ auths.oauth2_group_claim_name=Prasības nosaukums, kas šim avotam nodrošina gr auths.oauth2_admin_group=Kopas prasības vērtība pārvaldītājiem. (Izvēles - nepieciešams augstāk esošais prasības nosaukums) auths.oauth2_restricted_group=Grupas prasības vērtība ierobežotajiem lietotājiem. (Izvēles - nepieciešams augstāk esošais prasības nosaukums) auths.oauth2_map_group_to_team=Sasaistīt pieprasītās kopas ar apvienības komandām. (Izvēles - nepieciešams augstāk esošais prasības nosaukums) -auths.oauth2_map_group_to_team_removal=Noņemt lietotājus no sinhronizētajām komandām, ja lietotājs nav piesaistīts attiecīgajai grupai. +auths.oauth2_map_group_to_team_removal=Noņemt lietotājus no sinhronizētajām komandām, ja lietotājs nav attiecīgajā grupai. auths.enable_auto_register=Iespējot automātisko reģistrāciju auths.sspi_auto_create_users=Automātiski izveidot lietotājus auths.sspi_auto_create_users_helper=Ļauj SSPI autentificēšanās veidam automātiski izveidot jaunus kontus lietotājiem, kas piesakās pirmo reizi @@ -3334,20 +3362,20 @@ auths.tip.twitter=Jādodas uz %s, jāizveido lietotne un jānodrošina, ka iesp auths.tip.discord=Jāizveido jauna lietotne %s auths.tip.gitea=Pievienot jaunu OAuth2 lietotni. Norādes ir atrodamas %s auths.tip.yandex=%s jāizveido jauna lietotne. Sadaļā "Yandex.Passport API" jāatlasa šīs atļaujas: "Access to email address", "Access to user avatar" un "Access to username, first name and surname, gender" -auths.tip.mastodon=Jāievada pielāgota Mastodon servera URL, ar kuru vēlies autentificēties (vai jāizmanto noklusējuma) +auths.tip.mastodon=Jāievada pielāgota Mastodon servera URL, ar kuru ir vēlēšanās autentificēties (vai jāizmanto noklusējuma) auths.edit=Labot autentificēšanas avotu -auths.activated=Autentificēšanas avots ir atkivēts -auths.new_success=Jauna autentifikācija "%s" tika pievienota. +auths.activated=Šis autentificēšanas avots ir atkivēts +auths.new_success=Autentificēšanās "%s" tika pievienota. auths.update_success=Autentificēšanās avots tika atjaunināts. auths.update=Atjaunināt autentificēšanās avotu auths.delete=Izdzēst autentificēšanas avotu auths.delete_auth_title=Izdzēst autentificēšanas avotu -auths.delete_auth_desc=Izdzēšot autentifikācijas avotu, tā lietotājiem nebūs iespējams pieteikties. Vai turpināt? +auths.delete_auth_desc=Autentificēšanās avota izdzēšana liedz lietotājiem to izmantot, lai pieteiktos. Turpināt? auths.still_in_used=Šo autentificēšanās avotu joprojām izmanto viens vai vairāki lietotāji, tos nepieciešams izdzēst vai pārvietot uz citu autentificēšanās avotu. auths.deletion_success=Autentificēšanās avots tika izdzēsts. auths.login_source_exist=Jau pastāv autentificēšanās avots "%s". auths.login_source_of_type_exist=Jau pastāv šāda veida autentificēšanās avots. -auths.unable_to_initialize_openid=Nevarēja inicializēt OpenID Connect sliedzēju: %s +auths.unable_to_initialize_openid=Nevarēja sāknēt OpenID Connect sniedzēju: %s auths.invalid_openIdConnectAutoDiscoveryURL=Nederīgs automātiskās atklāšanas URL (tam jābūt derīgam URL, kas sākas ar http:// vai https://) config.server_config=Servera konfigurācija @@ -3500,7 +3528,7 @@ monitor.download_diagnosis_report=Lejupielādēt diagnostikas atskaiti monitor.desc=Apraksts monitor.start=Sākuma laiks monitor.execute_time=Izpildes laiks -monitor.last_execution_result=Rezultāts +monitor.last_execution_result=Iznākums monitor.process.cancel=Atcelt procesu monitor.process.cancel_desc=Procesa atcelšana var radīt datu zaudējumus monitor.process.cancel_notices=Atcelt: %s? @@ -3520,7 +3548,7 @@ monitor.queue.settings.title=Pūla iestatījumi monitor.queue.settings.desc=Pūli dinamiski palielinās atkarībā no to strādņu rindu aizturēšanas. monitor.queue.settings.maxnumberworkers=Maksimālais strādņu skaits monitor.queue.settings.maxnumberworkers.placeholder=Pašalaik %[1]d -monitor.queue.settings.maxnumberworkers.error=Maksimālajam strādņu skaitam ir jābūt skaitlim +monitor.queue.settings.maxnumberworkers.error=Lielākajam pieļaujamajam strādņu skaitam ir jābūt skaitlim monitor.queue.settings.submit=Atjaunināt iestatījumus monitor.queue.settings.changed=Iestatījumi atjaunināti monitor.queue.settings.remove_all_items=Noņemt visus @@ -3539,7 +3567,7 @@ notices.type_1=Glabātava notices.type_2=Uzdevums notices.desc=Apraksts notices.op=Op. -notices.delete_success=Sistēmas paziņojumi ir dzēsti. +notices.delete_success=Sistēmas paziņojumi tika dzēsti. self_check.no_problem_found=Vēl nav atrasts neviens sarežģījums. config_summary = Kopsavilkums @@ -3556,7 +3584,7 @@ self_check.database_collation_mismatch = Sagaidīt, ka datubāzē tiek izmantota self_check.database_fix_mysql = MySQL/MariaDB lietotāji var izmantot komandu "forgejo doctor convert", lai novērstu salīdzināšanas sarežģījumus, vai arī tos var pašrocīgi novērst ar "ALTER ... COLLATE ..." vaicājumiem. config.app_slogan = Servera sauklis config.allow_dots_in_usernames = Ļaut lietotājiem izmantot punktus savā lietotājvārdā. Neietekmē esošos kontus. -users.restricted.description = Ļaut mijiedarbību tikai ar glabātavām un apvienībām, kurās šis lietotājs ir pievienots kā līdzdalībnieks. Tas neļauj piekļūt šī servera publiskajām glabātavām. +users.restricted.description = Ļaut mijiedarbību tikai ar glabātavām un apvienībām, kurās šis lietotājs ir pievienots kā līdzdalībnieks. Tas neļauj piekļūt šī servera atklātajām glabātavām. dashboard.sync_tag.started = Uzsākta birku sinhronizēšana users.organization_creation.description = Ļaut jaunu apvienību izveidošanu. users.block.description = Liegt šī lietotāja mijiedarbību ar šo serveri caur tā kontu un neļaut pieteikšanos. @@ -3603,7 +3631,7 @@ review_dismissed=`atmeta izskatīšanu no %[4]s %[3]s#%[2 review_dismissed_reason=Iemesls: create_branch=izveidoja zaru %[3]s glabātavā %[4]s starred_repo=pievienoja izlasē %[2]s -watched_repo=sāka sekot %[2]s +watched_repo=sāka vērot %[2]s [tool] now=tagad @@ -3635,11 +3663,11 @@ remove_file=Noņemt datni notifications=Paziņojumi unread=Neizlasītie read=Izlasītie -no_unread=Nav nelasītu paziņojumu. +no_unread=Nav neizlasītu paziņojumu. no_read=Nav izlasītu paziņojumu. pin=Piespraust paziņojumu mark_as_read=Atzīmēt kā izlasītu -mark_as_unread=Atzīmēt kā nelasītu +mark_as_unread=Atzīmēt kā neizlasītu mark_all_as_read=Atzīmēt visus kā izlasītus subscriptions=Abonementi watching=Skatās @@ -3671,9 +3699,9 @@ registry.documentation=Vairāk informācijas par %s reģistru ir %[3]s %[1]s +filter.container.tagged=Ar birku +filter.container.untagged=Bez birkas +published_by=Laida klajā %[3]s %[1]s published_by_in=%[3]s laida klajā %[1]s %[5]s installation=Uzstādīšana about=Par šo pakotni @@ -3782,7 +3810,7 @@ settings.delete.error=Neizdevās izdzēst pakotni. owner.settings.cargo.title=Cargo reģistra inkdess owner.settings.cargo.initialize=Sāknēt indeksu owner.settings.cargo.initialize.description=Ir nepieciešams īpaša indeksa Git glabātava, lai izmantotu Cargo reģistru. Šīs iespējas izmantošana (atkārtoti) izveidos glabātavu un automātiski to iestatīs. -owner.settings.cargo.initialize.error=Neizdevās inicializēt Cargo indeksu: %v +owner.settings.cargo.initialize.error=Neizdevās sāknēt Cargo indeksu: %v owner.settings.cargo.initialize.success=Cargo indekss tika sekmīgi izveidots. owner.settings.cargo.rebuild=Pārbūvēt indeksu owner.settings.cargo.rebuild.description=Pārbūvēšana var būt noderīga, ja indekss nav sinhronizēts ar saglabātajām Cargo pakotnēm. @@ -3793,12 +3821,12 @@ owner.settings.cleanuprules.add=Pievienot notīrīšanas kārtulu owner.settings.cleanuprules.edit=Labot notīrīšanas kārtulu owner.settings.cleanuprules.none=Vēl nav pieejama neviena tīrīšanas kārtula. owner.settings.cleanuprules.preview=Attīrīšanas kārtulas priekšskatījums -owner.settings.cleanuprules.preview.overview=Ir ieplānota %d paku dzēšana. -owner.settings.cleanuprules.preview.none=Notīrīšanas kārtulai neatbilst neviena pakotne. +owner.settings.cleanuprules.preview.overview=Ir paredzēta %d pakotņu noņemšana. +owner.settings.cleanuprules.preview.none=Attīrīšanas kārtulai neatbilst neviena pakotne. owner.settings.cleanuprules.enabled=Iespējots -owner.settings.cleanuprules.pattern_full_match=Piešķirt šablonu visam pakotnes nosaukumam +owner.settings.cleanuprules.pattern_full_match=Pielietot paraugu visam pakotnes nosaukumam owner.settings.cleanuprules.keep.title=Versijas, kas atbilst šīm kārtulām, tiks paturētas, pat ja tās atbildīs zemāk esošajai noņemšanas kārtulai. -owner.settings.cleanuprules.keep.count=Saglabāt jaunāko versiju +owner.settings.cleanuprules.keep.count=Paturēt visjaunāko owner.settings.cleanuprules.keep.count.1=1 versija katrai pakotnei owner.settings.cleanuprules.keep.count.n=%d versijas katrai pakotnei owner.settings.cleanuprules.keep.pattern=Paturēt versijas, kas atbilst @@ -3835,24 +3863,24 @@ search_in_external_registry = Meklēt %s alt.registry = Šī reģistra uzstādīšana komandrindā: alt.registry.install = Lai uzstādītu pakotni, jāizpilda šī komanda: alt.install = Uzstādīt pakotni -alt.setup = Pievienot glabātavu savienoto glabātavu sarakstā ('_arch_' vietā jāizvēlas nepieciešamā arhitektūra): +alt.setup = Pievienot glabātavu savienoto glabātavu sarakstā ("_arch_" vietā jāizvēlas nepieciešamā arhitektūra): alt.repository = Informācija par glabātavu alt.repository.architectures = Arhitektūras alt.repository.multiple_groups = Šī pakotne ir pieejama vairākās kopās. [secrets] secrets=Noslēpumi -description=Noslēpumi tiks padoti atsevišķām darbībām un citādi nevar tikt nolasīti. +description=Noslēpumi tiks padoti noteiktām darbībām, un citādāk tos nevar nolasīt. none=Pagaidām nav neviena noslēpuma. creation=Pievienot noslēpumu creation.name_placeholder=reģistrnejutīgs, tikai burti, cipari un apakšsvītras, nevar sākties ar GITEA_ vai GITHUB_ creation.value_placeholder=Jāievada jebkāds saturs. Atstarpes sākumā un beigās tiks izlaistas. creation.success=Noslēpums "%s" tika pievienots. creation.failed=Neizdevās pievienot noslēpumu. -deletion=Dzēst noslēpumu -deletion.description=Noslēpuma dzēšana ir neatgriezeniska. Vai turpināt? -deletion.success=Noslēpums tika izdzēsts. -deletion.failed=Neizdevās dzēst noslēpumu. +deletion=Noņemt noslēpumu +deletion.description=Noslēpuma izdzēšana ir neatgriezeniska un nav atsaucama. Turpināt? +deletion.success=Noslēpums tika noņemts. +deletion.failed=Neizdevās noņemt noslēpumu. management=Pārvaldīt noslēpumus [actions] @@ -3873,7 +3901,7 @@ runners=Izpildītāji runners.runner_manage_panel=Pārvaldīt izpildītājus runners.new=Izveidot jaunu izpildītāju runners.new_notice=Kā uzstādīt izpildītāju -runners.status=Statuss +runners.status=Stāvoklis runners.id=ID runners.name=Nosaukums runners.owner_type=Veids @@ -3884,7 +3912,7 @@ runners.runner_title=Izpildītājs runners.task_list=Pēdējās darbības, kas izpildītas runners.task_list.no_tasks=Vēl nav uzdevumu. runners.task_list.run=Izpildījums -runners.task_list.status=Statuss +runners.task_list.status=Stāvoklis runners.task_list.repository=Glabātava runners.task_list.commit=Iesūtījums runners.task_list.done_at=Beigu laiks @@ -3896,11 +3924,11 @@ runners.delete_runner=Dzēst izpildītāju runners.delete_runner_success=Izpildītājs sekmīgi izdzēsts runners.delete_runner_failed=Neizdevās izdzēst izpildītāju runners.delete_runner_header=Apstiprināt izpildītāja izdzēšanu -runners.delete_runner_notice=Ja šis izpildītājs veic kādus uzdevumus, tad tie tiks apturēti un atzīmēti kā neizdevušies. Tas var sabojāt būvēšanas darbaplūsmas. +runners.delete_runner_notice=Ja šis izpildītājs veic kādus uzdevumus, tad tie tiks apturēti un atzīmēti kā neizdevušies. Tas var sabojāt būvēšanas darbplūsmas. runners.none=Nav pieejami izpildītāji runners.status.unspecified=Nezināms runners.status.idle=Dīkstāvē -runners.status.active=Aktīvs +runners.status.active=Darbojas runners.status.offline=Bezsaistē runners.version=Versija runners.reset_registration_token=Atiestatīt reģistrācijas pilnvaru @@ -3913,7 +3941,7 @@ runs.pushed_by=aizgādāja runs.invalid_workflow_helper=Darbplūsmas konfigurācijas datne ir nederīga. Lūgums pārbaudīt konfigurācijas datni: %s runs.no_matching_online_runner_helper=Nav tiešsaistē esošu izpildītāju, kas atbilstu iezīmei: %s runs.actor=Izraisītājs -runs.status=Statuss +runs.status=Stāvoklis runs.actors_no_select=Visi izraisītāji runs.status_no_select=Visi stāvokļi runs.no_results=Netika atrasts nekas atbilstošs. diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index ee374a2549..9715d76e86 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -1473,7 +1473,7 @@ issues.label.filter_sort.reverse_alphabetically = Umdreiht na de Alphabeet issues.label.filter_sort.by_size = Lüttste Grött issues.num_participants_one = %d Mitmaker issues.num_participants_few = %d Mitmakers -issues.ref_pull_from = `hett deeses Haalvörslag %[4]s %[2]s benöömt` +issues.ref_pull_from = `hett deesen Haalvörslag %[4]s %[2]s benöömt` issues.label_title = Naam issues.label_archived_filter = Archiveert Vermarkens wiesen issues.archived_label_description = (Archiveert) %s @@ -1581,7 +1581,7 @@ issues.start_tracking_history = `hett %s to warken begunnen` issues.lock.notice_2 = - Du un anner Mitarbeiders mit Togriep to deesem Repositorium köönt wiederhen Kommentaren schrieven, wat elkeenwell sücht. issues.due_date_modified = hett dat Anstahns-Datum vun %[2]s to %[1]s %[3]s ännert issues.dependency.issue_remove_text = Dat word de Ofhangen vun deesem Gefall wegdoon. Wiedermaken? -issues.review.approve = hett deese Ännerns %s tostimmt +issues.review.approve = hett %s deesen Ännerns tostimmt issues.review.dismissed = hett %[2]s dat Nakieken vun %[1]s ofseggt issues.lock.title = Snack up deesem Gefall tosluten. issues.unlock.title = Snack up deesem Gefall upsluten. @@ -1692,12 +1692,12 @@ pulls.waiting_count_1 = %d Nakieken staht ut issues.content_history.deleted = lösket issues.content_history.created = maakt issues.content_history.delete_from_history_confirm = Ut Histoorje lösken? -issues.blocked_by_user = Du kannst up deesem Repositorium keenen Gefall opmaken, denn de Repositoriums-Eegner hett di blockeert. +issues.blocked_by_user = Du kannst in deesem Repositorium keene Gefallens opmaken, denn de Repositoriums-Eegner hett di blockeert. pulls.merged_title_desc_few = hett %[1]d Kommitterens vun %[2]s na %[3]s %[4]s tosamenföhrt pulls.reject_count_1 = %d Bidde um Ännerns pulls.blocked_by_user = Du kannst in deesem Repositorium keenen Haalvörslag opmaken, denn de Repositoriums-Eegner hett di blockeert. pulls.no_merge_access = Du hest nich dat Recht, deesen Haalvörslag tosamentoföhren. -issues.comment.blocked_by_user = Du kannst up deesem Gefall keenen Kommentaar schrieven, denn de Repositoriums-Eegner of de Autor vun de Gefall hett di blockeert. +issues.comment.blocked_by_user = Du kannst up deesem Gefall nich kommenteren, denn de Repositoriums-Eegner of de Autor vun de Gefall hett di blockeert. pulls.switch_comparison_type = Verglieks-Aard ännern pulls.showing_only_single_commit = Blots Ännerns vun Kommitteren %[1]s wiesen pulls.blocked_by_changed_protected_files_n = Deeser Haalvörslag is blockeert, denn dat ännert beschütt Dateien: @@ -2600,6 +2600,10 @@ archive.pull.noreview = Deeses Repositorium is archiveert. Du kannst keene Haalv commits.view_single_diff = Ännerns an deeser Datei in deesem Kommitteren wiesen pulls.editable = Bewarkbaar pulls.editable_explanation = Deeser Haalvörslag verlöövt Bewarkens vun Liddmaten. Du kannst stracks daarto bidragen. +issues.reopen.blocked_by_user = Du kannst deeses Gefall nich weer opmaken, denn de Repositoriums-Eegner of de Autor vun de Gefall hett di blockeert. +pulls.comment.blocked_by_user = Du kannst up deesem Haalvörslag nich kommenteren, denn de Repositoriums-Eegner of de Autor vun de Haalvörslag hett di blockeert. +issues.filter_no_results = Keene Resultaten +issues.filter_no_results_placeholder = Versöök, diene Söök-Filters antopassen. [repo.permissions] code.read = Lesen: De Quelltext vun deesem Repositorium ankieken un klonen. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 415ca8948e..8b5db6ea50 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -4,14 +4,14 @@ dashboard=Overzicht explore=Verkennen help=Help logo=Logo -sign_in=Inloggen +sign_in=Aanmelden sign_in_or=of sign_out=Uitloggen sign_up=Registreren link_account=Account Koppelen register=Registreren version=Versie -powered_by=Aangedreven door %s +powered_by=Mogelijk gemaakt door %s page=Pagina template=Sjabloon language=Taal @@ -28,24 +28,24 @@ username=Gebruikersnaam email=E-mailadres password=Wachtwoord access_token=Toegangstoken -re_type=Verifieer wachtwoord +re_type=Bevestig wachtwoord captcha=CAPTCHA twofa=Twee-factor authenticatie twofa_scratch=Twee-factor krascode -passcode=PIN +passcode=Code webauthn_insert_key=Voer uw beveiligingssleutel in -webauthn_sign_in=Druk op de knop van uw beveiligingssleutel. Als uw beveiligingssleutel geen knop heeft, voeg deze dan opnieuw in. +webauthn_sign_in=Druk op de knop van uw beveiligingssleutel. Als uw beveiligingssleutel geen knop heeft, voer deze dan opnieuw in. webauthn_press_button=Druk alstublieft op de knop van uw beveiligingssleutel… webauthn_use_twofa=Gebruik een twee-factor code van uw telefoon webauthn_error=Kon uw beveiligingssleutel niet lezen. webauthn_unsupported_browser=Uw browser ondersteunt momenteel geen WebAuthn. webauthn_error_unknown=Er is een onbekende fout opgetreden. Probeer het opnieuw. -webauthn_error_insecure=WebAuthn ondersteunt alleen beveiligde verbindingen. Om te testen via HTTP, kan je de oorsprong "localhost" of "127.0.0.1" gebruiken +webauthn_error_insecure=WebAuthn ondersteunt alleen beveiligde verbindingen. Om te testen via HTTP, kan je als systeemnaam "localhost" of "127.0.0.1" gebruiken webauthn_error_unable_to_process=De server kon uw verzoek niet verwerken. -webauthn_error_duplicated=De beveiligingssleutel is niet toegestaan voor dit verzoek. Zorg er alstublieft voor dat de sleutel niet al geregistreerd is. +webauthn_error_duplicated=De beveiligingssleutel is voor dit verzoek niet toegestaan. Controleer alstublieft of de sleutel niet al is geregistreerd. webauthn_error_empty=U moet een naam voor deze sleutel instellen. -webauthn_error_timeout=Time-out bereikt voordat uw sleutel kon worden gelezen. Laad deze pagina opnieuw en probeer het opnieuw. +webauthn_error_timeout=Time-out bereikt voordat uw sleutel kon worden gelezen. Herlaad deze pagina en probeer het opnieuw. webauthn_reload=Vernieuwen repository=Repository @@ -56,9 +56,9 @@ new_migrate=Nieuwe migratie new_mirror=Nieuwe mirror new_fork=Nieuwe repository fork new_org=Nieuwe organisatie -new_project=Nieuwe project +new_project=Nieuw project manage_org=Beheer organisaties -admin_panel=Website administratie +admin_panel=Site beheer account_settings=Accountinstellingen settings=Instellingen your_profile=Profiel @@ -66,12 +66,12 @@ your_starred=Favoriet your_settings=Instellingen all=Alles -sources=Bronnen +sources=Broncode mirrors=Mirrors -collaborative=Samenwerkend +collaborative=Samenwerkende forks=Forks -activities=Activiteiten +activities=Activiteit pull_requests=Pull requests issues=Issues milestones=Mijlpalen @@ -83,7 +83,7 @@ add=Toevoegen add_all=Alles toevoegen remove=Verwijder remove_all=Alles verwijderen -edit=Bewerk +edit=Wijzig enabled=Ingeschakeld disabled=Uitgeschakeld @@ -115,8 +115,8 @@ concept_user_organization=Organisatie name=Naam -sign_in_with_provider = Log in met %s -tracked_time_summary = Overzicht van geregistreerde tijd op basis van filters van probleemlijst +sign_in_with_provider = Aanmelden met %s +tracked_time_summary = Overzicht van geregistreerde tijd op basis van filters van issuelijst enable_javascript = Deze website vereist JavaScript. retry = Probeer opnieuw rerun_all = Alle taken opnieuw uitvoeren @@ -140,9 +140,9 @@ confirm_delete_selected = Bevestigen om alle geselecteerde items te verwijderen? copy_type_unsupported = Dit bestandstype kan niet worden gekopieerd pin = Vastpinnen unpin = Ontpinnen -remove_label_str = Verwijder punt "%s" +remove_label_str = Verwijder item "%s" confirm_delete_artifact = Weet u zeker dat u het artefact "%s" wilt verwijderen? -toggle_menu = Menu schakelen +toggle_menu = Menu aan/uit filter.clear = Filter wissen filter.is_archived = Gearchiveerd filter.is_fork = Forks @@ -159,7 +159,7 @@ more_items = Meer items invalid_data = Ongeldige data: %v copy_generic = Kopieer naar klembord test = Test -error413 = U heeft al uw quotum opgebruikt. +error413 = U heeft uw hele quotum gebruikt. new_migrate.title = Nieuwe migratie new_org.title = Nieuwe organisatie new_repo.link = Nieuwe repository @@ -206,6 +206,10 @@ table_modal.placeholder.header = Kop table_modal.placeholder.content = Inhoud table_modal.label.rows = Rijen table_modal.label.columns = Kolommen +link_modal.header = Link toevoegen +link_modal.url = Url +link_modal.description = Beschrijving +link_modal.paste_reminder = Tip: Als u een URL op uw klembord heeft, kun u deze direct in de editor plakken om een koppeling te maken. [filter] string.asc = A - Z @@ -1064,6 +1068,26 @@ change_username_redirect_prompt.with_cooldown.few = De oude gebruikersnaam zal v change_username_redirect_prompt.with_cooldown.one = De oude gebruikersnaam zal voor iedereen beschikbaar zijn na een afkoelperiode van %[1]d dag. U kunt de oude gebruikersnaam nog steeds opeisen tijdens de afkoelperiode. keep_pronouns_private = Toon voornaamwoorden alleen aan geauthenticeerde gebruikers keep_pronouns_private.description = Dit verbergt uw voornaamwoorden voor bezoekers die niet zijn ingelogd. +quota.rule.exceeded.helper = De totale grootte van objecten voor deze regel heeft de quota overschreden. +quota.sizes.repos.private = Privé repositories +storage_overview = Opslagoverzicht +quota = Quotum +quota.applies_to_user = De volgende quotaregels zijn van toepassing op uw account +quota.applies_to_org = De volgende quotaregels zijn van toepassing op deze organisatie +quota.rule.exceeded = Overschreden +quota.rule.no_limit = Onbeperkt +quota.sizes.all = Alle +quota.sizes.repos.all = Repositories +quota.sizes.repos.public = Openbare repositories +quota.sizes.git.all = Git inhoud +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = Bezittingen +quota.sizes.assets.attachments.all = Bijlagen +quota.sizes.assets.attachments.issues = Issue bijlagen +quota.sizes.assets.attachments.releases = Release bijlagen +quota.sizes.assets.artifacts = Artefacten +quota.sizes.assets.packages.all = Pakketten +quota.sizes.wiki = Wiki [repo] owner=Eigenaar @@ -1257,7 +1281,7 @@ tags=Labels issues=Issues pulls=Pull requests project_board=Projecten -packages=Paketten +packages=Pakketten labels=Labels org_labels_desc=Organisatielabel dat gebruikt kan worden met alle repositories onder deze organisatie org_labels_desc_manage=beheren @@ -2397,8 +2421,8 @@ find_file.no_matching = Geen overeenkomstige bestanden gevonden error.csv.too_large = Kan dit bestand niet renderen omdat het te groot is. error.csv.unexpected = Kan dit bestand niet renderen omdat het een onverwacht karakter bevat in regel %d en kolom %d. error.csv.invalid_field_count = Kan dit bestand niet renderen omdat het een verkeerd aantal velden heeft in regel %d. -issues.comment.blocked_by_user = U kunt geen reactie op deze issue plaatsen omdat u geblokkeerd bent door de eigenaar van de repository of door de persoon die de issue heeft gepost. -issues.blocked_by_user = U kunt geen issue op deze repository maken omdat u geblokkeerd bent door de eigenaar van de repository. +issues.comment.blocked_by_user = U kunt niet reageren op deze issue omdat u geblokkeerd bent door de eigenaar van de repository of de poster van de issue. +issues.blocked_by_user = U kunt geen issues aanmaken in deze repository omdat u geblokkeerd bent door de eigenaar van deze repository. issues.label_archived_filter = Gearchiveerde labels bekijken issues.label_archive_tooltip = Gearchiveerde labels zijn standaard uitgezonderd van de suggesties als men op een label zoekt. issues.max_pinned = U kunt geen issues meer vastpinnen @@ -2874,6 +2898,10 @@ archive.pull.noreview = Deze repository is gearchiveerd. U kunt geen pull reques commits.view_single_diff = Bekijk de veranderingen aan dit bestand die in deze commit zijn geïntroduceerd pulls.editable_explanation = Deze pull request staat bewerkingen toe van beheerders. Je kunt er direct aan bijdragen. pulls.editable = Bewerkbaar +issues.reopen.blocked_by_user = U kunt deze issue niet heropenen omdat u geblokkeerd bent door de eigenaar van de repository of de poster van de issue. +pulls.comment.blocked_by_user = U kunt niet reageren op deze pull request omdat u geblokkeerd bent door de eigenaar van de repository of de poster van de issue. +issues.filter_no_results_placeholder = Probeer uw zoekfilters aan te passen. +issues.filter_no_results = Geen resultaten @@ -2951,7 +2979,7 @@ members.invite_desc=Voeg nieuw lid toe aan %s: members.invite_now=Nu uitnodigen teams.join=Lid worden -teams.leave=Vertlaat +teams.leave=Verlaat teams.can_create_org_repo=Maak repositories teams.can_create_org_repo_helper=Leden kunnen nieuwe repositories aanmaken in de organisatie. De maker krijgt beheerder toegang tot de nieuwe repository. teams.read_access=Gelezen @@ -3840,7 +3868,7 @@ alt.repository.architectures = Architecturen alt.repository.multiple_groups = Dit pakket is beschikbaar in meerdere groepen. alt.registry = Stel dit register in vanaf de opdrachtregel: alt.install = Pakket installeren -alt.setup = Voeg een repository toe aan de lijst met gekoppelde repositories (kies de benodigde architectuur in plaats van '_arch_'): +alt.setup = Voeg een repository toe aan de lijst met gekoppelde repositories (kies de benodigde architectuur in plaats van "_arch_"): [secrets] secrets = Geheimen diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 6ba696573a..7098481fee 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -55,7 +55,7 @@ organization=Organização mirror=Espelhamento new_repo=Novo repositório new_migrate=Nova migração -new_mirror=Novo espelhamento +new_mirror=Novo espelho new_fork=Novo fork do repositório new_org=Nova organização new_project=Novo projeto @@ -109,7 +109,7 @@ preview=Pré-visualização loading=Carregando… error=Erro -error404=A página que você está tentando acessar não existe ou você não está autorizado a visualizá-la. +error404=A página que você está tentando acessar não existe, foi removida ou você não tem autorização para visualizá-la. never=Nunca unknown=Desconhecido @@ -206,6 +206,10 @@ table_modal.placeholder.header = Cabeçalho table_modal.placeholder.content = Conteúdo table_modal.label.rows = Linhas table_modal.label.columns = Colunas +link_modal.header = Adicionar um link +link_modal.url = URL +link_modal.description = Descrição +link_modal.paste_reminder = Dica: Com uma URL na sua área de transferência, você pode colar diretamente no editor para criar um link. [filter] string.asc=A - Z @@ -723,8 +727,8 @@ following.title.one = seguindo followers.title.one = seguidor followers.title.few = seguidores public_activity.visibility_hint.self_private = Sua atividade está visível apenas para você e para os administradores da instância. Configurar. -public_activity.visibility_hint.self_public = Sua atividade está visível para todos, exceto o engajamento em espaços privados. Configurar. -public_activity.visibility_hint.admin_public = Sua atividade está visível para todos, mas como um administrador você também pode ver o engajamento em espaços privados. +public_activity.visibility_hint.self_public = Sua atividade está visível para todos, exceto interações em espaços privados. Configurar. +public_activity.visibility_hint.admin_public = Sua atividade está visível para todos, mas como um administrador você também pode ver interações em espaços privados. public_activity.visibility_hint.admin_private = Essa atividade está visível para você porque você é um administrador, mas o usuário dejesa que ela seja mantida em privado. public_activity.visibility_hint.self_private_profile = Sua atividade só é visível para você e para os administradores do servidor porque seu perfil é privado. Configurar. @@ -993,7 +997,7 @@ scan_this_image=Escaneie esta imagem com o seu aplicativo de autenticação: or_enter_secret=Ou digite esse código: %s then_enter_passcode=E insira a senha mostrada no aplicativo: passcode_invalid=Esse código de acesso é inválido. Tente novamente. -twofa_enrolled=Sua conta foi inscrita na autenticação de dois fatores. Armazene seu token de backup (%s) em um local seguro, pois ele é exibido apenas uma vez! +twofa_enrolled=Sua conta foi inscrita na autenticação de dois fatores. Armazene seu token de recuperação de uso único (%s) em um local seguro, pois ele não será exibido novamente. twofa_failed_get_secret=Falha ao obter o segredo. webauthn_desc=Chaves de segurança são dispositivos de hardware que contém chaves de criptografia. Elas podem ser usadas para autenticação de dois fatores. A chave de segurança deve suportar o padrão WebAuthnn Authenticator. @@ -1061,6 +1065,28 @@ user_block_yourself = Você não pode se bloquear. pronouns_custom_label = Pronomes personalizados change_username_redirect_prompt.with_cooldown.one = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dia, você ainda pode recuperar o nome de usuário antigo durante este período de espera. change_username_redirect_prompt.with_cooldown.few = O nome de usuário antigo ficará disponível para qualquer pessoa após um período de espera de %[1]d dias, você ainda pode recuperar o nome de usuário antigo durante este período de espera. +quota.applies_to_user = As seguintes regras de cota se aplicam à sua conta +quota.rule.exceeded.helper = O tamanho total de objetos para esta regra excedeu a cota. +keep_pronouns_private = Mostrar pronomes apenas para usuários autenticados +keep_pronouns_private.description = Isto irá esconder seus pronomes de visitantes que não fizeram login. +storage_overview = Visão geral de armazenamento +quota = Cota +quota.applies_to_org = As seguintes regras de cota se aplicam a esta organização +quota.rule.exceeded = Excedido +quota.rule.no_limit = Ilimitado +quota.sizes.all = Tudo +quota.sizes.repos.all = Repositórios +quota.sizes.repos.public = Repositórios públicos +quota.sizes.repos.private = Repositórios privados +quota.sizes.git.all = Conteúdo Git +quota.sizes.git.lfs = LFS Git +quota.sizes.assets.all = Assets +quota.sizes.assets.attachments.all = Anexos +quota.sizes.assets.attachments.issues = Anexos de issue +quota.sizes.assets.attachments.releases = Anexos de release +quota.sizes.assets.artifacts = Artefatos +quota.sizes.assets.packages.all = Pacotes +quota.sizes.wiki = Wiki [repo] owner=Proprietário @@ -1175,8 +1201,8 @@ template.issue_labels=Etiquetas de issue template.one_item=Deve-se selecionar pelo menos um item de modelo template.invalid=Deve-se selecionar um repositório de modelo -archive.title=Este repositório está arquivado. Você pode visualizar arquivos e cloná-lo, mas não pode fazer push, abrir issues ou pull requests. -archive.title_date=Este repositório foi arquivado em %s. Você pode visualizar arquivos e cloná-lo, mas não pode fazer push, abrir issues ou pull requests. +archive.title=Este repositório está arquivado. Você pode visualizar arquivos e cloná-lo, mas não pode fazer alterações, tais como push, novos issues, pull requests ou comentários. +archive.title_date=Este repositório foi arquivado em %s. Você pode visualizar arquivos e cloná-lo, mas não pode fazer alterações, tais como push, abrir issues, pull requests ou comentários. archive.issue.nocomment=Este repositório está arquivado. Você não pode comentar em issues. archive.pull.nocomment=Este repositório está arquivado. Você não pode comentar em pull requests. @@ -2660,7 +2686,7 @@ pulls.cmd_instruction_checkout_title = Checkout settings.wiki_globally_editable = Permitir que qualquer pessoa edite a wiki settings.transfer_abort_success = A transferência de repositório para %s foi cancelada. settings.enter_repo_name = Digite os nomes do dono e do repositório exatamente neste formato: -issues.blocked_by_user = Você não pode criar uma questão neste repositório porque você foi bloqueado pelo dono do repositório. +issues.blocked_by_user = Você não pode criar issues neste repositório porque você foi bloqueado pelo dono do repositório. settings.new_owner_blocked_doer = Você foi bloqueado pelo novo dono do repositório. settings.wiki_rename_branch_main_notices_1 = NÃO É POSSÍVEL desfazer esta ação. tree_path_not_found_commit = O caminho %[1]s não existe no commit %[2]s @@ -2751,8 +2777,8 @@ release.system_generated = Este anexo foi gerado automaticamente. settings.wiki_branch_rename_failure = Falha ao regularizar o nome do ramo da wiki do repositório. settings.add_collaborator_blocked_them = Não foi possível adicionar o(a) colaborador(a) porque ele(a) bloqueou o(a) proprietário(a) do repositório. settings.thread_id = ID da discussão -issues.edit.already_changed = Não foi possível salvar as alterações desta questão porque o conteúdo foi alterado por outro(a) usuário(a). Atualize a página e tente novamente para evitar sobrescrever as alterações. -pulls.edit.already_changed = Não foi possível salvar as alterações deste pedido de integração porque o conteúdo foi alterado por outro(a) usuário(a). Atualize a página e tente novamente para evitar sobrescrever as alterações. +issues.edit.already_changed = Não foi possível salvar as alterações desta questão. O conteúdo parece já ter sido alterado por outro(a) usuário(a). Atualize a página e tente novamente para evitar sobrescrever estas alterações. +pulls.edit.already_changed = Não foi possível salvar as alterações deste pull request. Parece que o conteúdo já foi alterado por outro(a) usuário(a). Atualize a página e tente novamente para evitar sobrescrever estas alterações. editor.commit_id_not_matching = O arquivo foi alterado durante a edição. Salve as alterações em um novo ramo e realize a mesclagem. blame.ignore_revs = As revisões em .git-blame-ignore-revs foram ignoradas. Clique aqui para retornar à visualização normal. topic.format_prompt = Os tópicos devem começar com um caracter alfanumérico, podem incluir hífens ("-") e pontos ("."), e podem ter até 35 caracteres. As letras devem ser minúsculas. @@ -2767,7 +2793,7 @@ pulls.fast_forward_only_merge_pull_request = Apenas fast-forward pulls.has_merged = Falha: O pull request foi merged, você não pode merge novamente ou mudar o branch destino. issues.author.tooltip.pr = Esse usuário é o autor dessa solicitação de pull. editor.push_out_of_date = O push parece estar desatualizado. -issues.comment.blocked_by_user = Você não pode criar um comentário nesse problema porque você está bloqueado pelo dono do repositório ou pelo autor do problema. +issues.comment.blocked_by_user = Você não pode comentar neste issue porque você foi bloqueado pelo dono do repositório ou pelo autor deste issue. pulls.blocked_by_user = Você não pode criar uma solicitação de pull nesse repositório porque você está bloqueado pelo dono do repositório. mirror_use_ssh.helper = Forgejo irá espelhar o repositório via Git através de SSH e criar um par de chaves para você ao escolher essa opção. Você deverá garantir que a chave pública gerada está autorizada a fazer push para o repositório de destino. Você não pode usar autorização baseada em senha ao escolher essa opção. mirror_denied_combination = Não é possível combinar o uso de chave pública e autenticação baseada em senha. @@ -2873,6 +2899,8 @@ editor.commit_email = Email de commit commits.view_single_diff = Ver modificações neste arquivo introduzidas neste commit pulls.editable = Editável pulls.editable_explanation = Este pull request permite edições de mantenedores. Voçê pode contribuir diretamenta para ele. +issues.reopen.blocked_by_user = Você não pode reabrir este issue porque você foi bloqueado pelo dono do repositório ou pelo criador deste issue. +pulls.comment.blocked_by_user = Você não pode comentar neste pull request porque você foi bloqueado pelo dono do repositório ou pelo autor do pull request. [graphs] component_loading = Carregando %s... @@ -3181,7 +3209,7 @@ orgs.new_orga=Nova organização repos.repo_manage_panel=Gerenciar repositórios repos.unadopted=Repositórios não adotados -repos.unadopted.no_more=Não foram encontrados repositórios não adotados +repos.unadopted.no_more=Não foram encontrados repositórios não adotados. repos.owner=Proprietário(a) repos.name=Nome repos.private=Privado @@ -3837,7 +3865,7 @@ alt.install = Instalar pacote alt.repository = Informação do repositório alt.repository.architectures = Arquiteturas alt.repository.multiple_groups = Este pacote está disponível em múltiplos grupos. -alt.setup = Adicionar um repositório à lista de repositórios conectados (escolha a arquitetura necessária em vez de '_arch_'): +alt.setup = Adicionar um repositório à lista de repositórios conectados (escolha a arquitetura necessária em vez de "_arch_"): [secrets] secrets=Segredos @@ -3998,7 +4026,7 @@ commit_kind = Buscar commits… runner_kind = Pesquisar runners... code_search_unavailable = A pesquisa de código não está disponível no momento. Entre em contato com o administrador do site. milestone_kind = Pesquisar marcos... -union_tooltip = Incluir resultados que coincidam com quaisquer palavras-chave separadas por espaços em branco +union_tooltip = Incluir resultados que correspondam a quaisquer palavras-chave separadas por espaços em branco union = União exact = Exato exact_tooltip = Incluir apenas resultados que correspondam exatamente ao termo de pesquisa diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 7bbb23ee55..bf0bab6fd9 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -112,7 +112,7 @@ preview=Pré-visualizar loading=Carregando… error=Erro -error404=A página que pretende aceder não existe ou não tem autorização para a ver. +error404=A página que pretende aceder não existe, foi removida ou não tem autorização para a ver. go_back=Voltar never=Nunca @@ -206,6 +206,10 @@ table_modal.placeholder.header = Cabeçalho table_modal.placeholder.content = Conteúdo table_modal.label.rows = Linhas table_modal.label.columns = Colunas +link_modal.header = Adicionar uma ligação +link_modal.url = URL +link_modal.description = Descrição +link_modal.paste_reminder = Sugestão: Com um URL na área de transferência, pode colar diretamente no editor para criar uma ligação. [filter] string.asc=A - Z @@ -1061,6 +1065,28 @@ pronouns_custom_label = Pronomes personalizados user_block_yourself = Não se pode bloquear a si próprio. change_username_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. change_username_redirect_prompt.with_cooldown.few = O nome de utilizador antigo ficará disponível para todos após um período de espera de %[1]d dias, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. +quota.applies_to_user = As seguintes regras de quotas aplicam-se à sua conta +quota.sizes.assets.artifacts = Artefactos +quota.rule.exceeded.helper = O tamanho total dos objectos para esta regra excedeu a quota. +keep_pronouns_private = Mostrar os pronomes apenas aos utilizadores autenticados +keep_pronouns_private.description = Isto irá ocultar os seus pronomes dos visitantes que não tenham iniciado sessão. +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = Ativos +storage_overview = Panorama geral do armazenamento +quota = Quota +quota.applies_to_org = As seguintes regras de quotas aplicam-se a esta organização +quota.rule.exceeded = Excedido +quota.rule.no_limit = Ilimitado +quota.sizes.all = Tudo +quota.sizes.repos.all = Repositórios +quota.sizes.repos.public = Repositórios públicos +quota.sizes.repos.private = Repositórios privados +quota.sizes.git.all = Conteúdo Git +quota.sizes.assets.attachments.all = Anexos +quota.sizes.assets.attachments.issues = Anexos de questões +quota.sizes.assets.attachments.releases = Anexos de lançamentos +quota.sizes.assets.packages.all = Pacotes +quota.sizes.wiki = Wiki [repo] new_repo_helper=Um repositório contém todos os ficheiros do trabalho, incluindo o histórico das revisões. Já tem um hospedado noutro sítio? Migre o repositório. @@ -1187,8 +1213,8 @@ template.issue_labels=Rótulos das questões template.one_item=Tem que escolher pelo menos um item do modelo template.invalid=Tem que escolher um repositório modelo -archive.title=Este repositório está arquivado. Pode ver os ficheiros e cloná-lo, mas não pode fazer envios ou abrir questões ou pedidos de integração. -archive.title_date=Este repositório foi arquivado em %s. Pode ver os ficheiros e cloná-lo, mas não pode fazer envios ou abrir questões/pedidos de integração. +archive.title=Este repositório está arquivado. Pode ver os ficheiros e cloná-lo, mas não pode fazer quaisquer alterações ao seu estado, tais como fazer envios e criar novas questões, pedidos de integração ou comentários. +archive.title_date=Este repositório foi arquivado em %s. Pode ver os ficheiros e cloná-lo, mas não pode fazer quaisquer alterações ao seu estado, tais como fazer envios e criar novas questões, pedidos de integração ou comentários. archive.issue.nocomment=Este repositório está arquivado. Não pode comentar nas questões. archive.pull.nocomment=Este repositório está arquivado. Não pode comentar nos pedidos de integração. @@ -2721,7 +2747,7 @@ find_file.no_matching=Não foi encontrado qualquer ficheiro correspondente error.csv.too_large=Não é possível apresentar este ficheiro por ser demasiado grande. error.csv.unexpected=Não é possível apresentar este ficheiro porque contém um caractere inesperado na linha %d e coluna %d. error.csv.invalid_field_count=Não é possível apresentar este ficheiro porque tem um número errado de campos na linha %d. -issues.blocked_by_user = Não pode criar uma questão neste repositório porque foi bloqueado/a pelo/a proprietário/a do repositório. +issues.blocked_by_user = Não pode criar questões neste repositório porque foi bloqueado(a) pelo(a) proprietário(a) do repositório. issues.num_participants_one = %d participante stars = Favoritos editor.invalid_commit_mail = Email inválido para criar um cometimento. @@ -2787,7 +2813,7 @@ settings.sourcehut_builds.secrets = Segredos settings.matrix.room_id_helper = O ID da Sala pode ser obtido no cliente web Element > Configurações da sala > Avançado > ID interno da sala. Exemplo: %s. settings.web_hook_name_sourcehut_builds = Construções do SourceHut settings.enter_repo_name = Insira o nome do/a proprietário/a e do repositório tal como é apresentado: -issues.comment.blocked_by_user = Não pode criar um comentário nesta questão porque foi bloqueado/a pelo/a proprietário/a ou pelo remetente da questão. +issues.comment.blocked_by_user = Não pode comentar nesta questão porque foi bloqueado(a) pelo(a) proprietário(a) ou pelo autor da questão. pulls.merged_title_desc_one = integrou %[1]d cometimento do ramo %[2]s no ramo %[3]s %[4]s pulls.agit_explanation = Criado usando a sequência de trabalho AGit. AGit deixa os contribuidores proporem alterações usando "git push" sem criar uma derivação ou um ramo novo. settings.new_owner_blocked_doer = O/A novo/a proprietário/a bloqueou-o/a. @@ -2872,6 +2898,11 @@ summary_card_alt = Cartão de resumo do repositório %s release.summary_card_alt = Cartão de resumo de um lançamento com o título "%s" no repositório %s archive.pull.noreview = Este repositório está arquivado. Não é possível rever os pedidos de integração. editor.commit_email = Endereço de email do cometimento +commits.view_single_diff = Ver alterações a este ficheiro introduzidas neste cometimento +pulls.comment.blocked_by_user = Não pode comentar este pedido de integração porque está bloqueado pelo(a) proprietário(a) do repositório ou pelo(a) autor(a) do pedido de integração. +issues.reopen.blocked_by_user = Não pode reabrir esta questão porque está bloqueado pelo(a) proprietário(a) do repositório ou pelo autor da questão. +pulls.editable = Editável +pulls.editable_explanation = Este pedido de integração permite edições dos responsáveis. Pode contribuir diretamente para ele. [graphs] component_loading=A carregar %s... @@ -3007,8 +3038,8 @@ teams.invite.by=Convidado(a) por %s teams.invite.description=Clique no botão abaixo para se juntar à equipa. follow_blocked_user = Não pode seguir esta organização porque esta organização bloqueou-o/a. open_dashboard = Abrir painel de controlo -settings.change_orgname_redirect_prompt.with_cooldown.one = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dia, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. -settings.change_orgname_redirect_prompt.with_cooldown.few = O nome de utilizador antigo estará disponível para todos após um período de espera de %[1]d dias, podendo ainda reivindicar o nome de utilizador antigo durante o período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.one = O nome antigo da organização estará disponível para todos após um período de espera de %[1]d dia, podendo ainda reivindicar o nome antigo durante o período de espera. +settings.change_orgname_redirect_prompt.with_cooldown.few = O nome antigo da organização estará disponível para todos após um período de espera de %[1]d dias, podendo ainda reivindicar o nome antigo durante o período de espera. [admin] dashboard=Painel de controlo @@ -3835,7 +3866,7 @@ alt.install = Instalar pacote alt.repository = Informação do repositório alt.repository.architectures = Arquiteturas alt.repository.multiple_groups = Este pacote está disponível em vários grupos. -alt.setup = Adicionar um repositório à lista de repositórios ligados (escolha a arquitetura necessária em vez de '_arch_'): +alt.setup = Adicionar um repositório à lista de repositórios ligados (escolha a arquitetura necessária em vez de "_arch_"): [secrets] secrets=Segredos @@ -3956,6 +3987,7 @@ workflow.dispatch.invalid_input_type = Tipo de entrada "%s" inválido. runs.expire_log_message = Os registos foram purgados por serem demasiado antigos. runs.no_workflows.help_no_write_access = Para aprender sobre Forgejo Actions, vejaa documentação. runs.no_workflows.help_write_access = Não sabe como começar com o Forgejo Actions? Consulte o início rápido na documentação do utilizador para escrever a sua primeira sequência de trabalho, depois prepare um executor Forgejo para executar os seus trabalhos. +variables.not_found = Não foi possível encontrar a variável. [projects] type-1.display_name=Planeamento individual diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 74ab9c3943..dc5100481c 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -110,7 +110,7 @@ preview=Предпросмотр loading=Загрузка… error=Ошибка -error404=Cтраница, которую вы пытаетесь открыть, не существует, либо у вас недостаточно прав для ее просмотра. +error404=Cтраница, которую вы пытаетесь открыть, не существует, была удалена, либо у вас недостаточно прав для её просмотра. go_back=Назад never=Никогда @@ -206,6 +206,10 @@ table_modal.header = Создание таблицы table_modal.placeholder.header = Заголовок table_modal.placeholder.content = Содержимое table_modal.label.rows = Кол-во строк +link_modal.header = Добавить ссылку +link_modal.url = Ссылка +link_modal.description = Описание +link_modal.paste_reminder = Имея ссылку в буфере обмена, вы можете вставить её напрямую в текст, чтобы создать ссылку с описанием. [filter] string.asc=A - Я @@ -1063,6 +1067,26 @@ change_username_redirect_prompt.with_cooldown.one = Прежнее имя буд change_username_redirect_prompt.with_cooldown.few = Прежнее имя будет доступно для использования другим пользователям после истечения защиты в %[1]d дней. Вы сможете вернуть его себе во время срока защиты. keep_pronouns_private = Показывать местоимения только зарегистрированным пользователям keep_pronouns_private.description = Местоимения будут скрыты от пользователей, не имеющих учётных записей на сервере. +quota.applies_to_user = Эти ограничения хранилища применяются к вашей учётной записи +quota.applies_to_org = Эти ограничения хранилища применяются к этой организации +quota.sizes.repos.public = Общедоступные репозитории +storage_overview = Использование места +quota = Ограничения хранилища +quota.rule.exceeded = Превышено +quota.rule.exceeded.helper = Суммарный объём объектов в этом правиле превысил допустимый. +quota.rule.no_limit = Неограниченно +quota.sizes.all = Всё +quota.sizes.repos.all = Репозитории +quota.sizes.repos.private = Частные репозитории +quota.sizes.git.all = Содержимое Git +quota.sizes.git.lfs = Git LFS +quota.sizes.wiki = Вики +quota.sizes.assets.packages.all = Пакеты +quota.sizes.assets.all = Объекты +quota.sizes.assets.attachments.all = Все прикреплённые файлы +quota.sizes.assets.attachments.releases = Файлы выпусков +quota.sizes.assets.attachments.issues = Файлы задач +quota.sizes.assets.artifacts = Артефакты [repo] owner=Владелец @@ -2684,7 +2708,7 @@ object_format = Формат объекта clone_in_vscodium = Клонировать в VSCodium mirror_sync = синхронизирован blame.ignore_revs = Правки в .git-blame-ignore-revs проигнорированы. Нажмите здесь, чтобы обойти этот файл и просмотреть авторов полноценно. -issues.blocked_by_user = Невозможно создать задачу в этом репозитории, т.к. вы заблокированы его владельцем. +issues.blocked_by_user = Создание задач невозможно в этом репозитории, т.к. вы заблокированы его владельцем. settings.new_owner_blocked_doer = Вы заблокированы новым владельцем. settings.add_collaborator_blocked_them = Невозможно добавить соучастника, т.к. им заблокирован владелец репозитория. pulls.blocked_by_changed_protected_files_1 = Этот запрос на слияние заблокирован, т.к. им изменяется защищённый файл: @@ -2878,6 +2902,10 @@ editor.commit_email = Эл. почта автора commits.view_single_diff = Посмотреть изменения в этом файле из этого коммита pulls.editable = Изменяемый pulls.editable_explanation = Автор разрешил изменения от соучастников. Вы можете напрямую отправлять в него изменения. +issues.reopen.blocked_by_user = Повторное открытие задачи невозможно, т.к. вы заблокированы владельцем репозитория или автором задачи. +pulls.comment.blocked_by_user = Вы не можете комментировать под этим запросом слияния, т.к. вы заблокированы владельцем репозитория или автором задачи. +issues.filter_no_results = Ничего не нашлось +issues.filter_no_results_placeholder = Попробуйте поискать по-другому. [graphs] component_loading_failed = Не удалось загрузить %s @@ -3699,7 +3727,7 @@ details.project_site=Веб-сайт проекта details.repository_site=Веб-сайт репозитория details.documentation_site=Веб-сайт документации details.license=Лицензия -assets=Ресурсы +assets=Объекты versions=Версии versions.view_all=Показать всё dependency.id=ID diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 85b5db5391..cbb42b3caa 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -77,7 +77,7 @@ write=Skriv preview=Förhandsgranska loading=Laddar… -error404=Sidan du försöker nå finns inte eller så har du inte behörighet att se den. +error404=Sidan du försöker nå finns inte, har tagits bort eller så har du inte behörighet att se den. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 0639371c0f..18fd4dbb5d 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -207,6 +207,9 @@ table_modal.placeholder.header = Заголовок table_modal.placeholder.content = Вміст table_modal.label.rows = Рядки link_modal.description = Опис +link_modal.url = URL +link_modal.header = Додати посилання +link_modal.paste_reminder = Підказка: якщо скопіювати URL-адресу в буфер обміну, можна створювати посилання, вставляючи її безпосередньо в редакторі. [filter] string.asc = А - Я @@ -1056,7 +1059,7 @@ quota = Квота quota.sizes.repos.private = Приватні репозиторії quota.sizes.repos.public = Публічні репозиторії quota.sizes.git.all = Вміст Git -quota.sizes.git.lfs = Вміст LFS +quota.sizes.git.lfs = Git LFS quota.sizes.assets.packages.all = Пакунки quota.sizes.assets.artifacts = Артефакти quota.sizes.assets.attachments.issues = Вкладення задач @@ -1068,7 +1071,7 @@ quota.sizes.all = Усе quota.sizes.repos.all = Репозиторії quota.applies_to_user = До вашого облікового запису застосовано такі квоти quota.sizes.assets.attachments.all = Вкладення -quota.applies_to_org = До вашої організації застосовано такі квоти +quota.applies_to_org = До цієї організації застосовано такі квоти quota.rule.exceeded = Перевищено [repo] @@ -1786,8 +1789,8 @@ wiki.save_page=Зберегти сторінку wiki.last_commit_info=%s редагував цю сторінку %s wiki.edit_page_button=Редагувати wiki.new_page_button=Нова сторінка -wiki.file_revision=Ревізії сторінки -wiki.wiki_page_revisions=Ревізії вікі сторінок +wiki.file_revision=Версія сторінки +wiki.wiki_page_revisions=Версії сторінок wiki.back_to_wiki=Повернутись на сторінку Вікі wiki.delete_page_button=Видалити сторінку wiki.page_already_exists=Вікі-сторінка з таким самим ім'ям вже існує. @@ -2050,7 +2053,7 @@ settings.event_issue_assign_desc=Задачу призначено або ска settings.event_issue_label=Мітки settings.event_issue_label_desc=Додавання або видалення міток задач. settings.event_issue_milestone=Задача з етапом -settings.event_issue_milestone_desc=Задача призначена на етап або видалена з етапу. +settings.event_issue_milestone_desc=Етап призначено, видалено або змінено. settings.event_issue_comment=Коментарі settings.event_issue_comment_desc=Коментар задачі створено, видалено чи відредаговано. settings.event_header_pull_request=Події запиту на злиття @@ -2060,8 +2063,8 @@ settings.event_pull_request_assign=Призначення settings.event_pull_request_assign_desc=Запит про злиття призначено або скасовано. settings.event_pull_request_label=Мітки settings.event_pull_request_label_desc=Мітки запиту на злиття оновлено або очищено. -settings.event_pull_request_milestone=Запит на злиття призначений на етап -settings.event_pull_request_milestone_desc=Запит на злиття призначений на етап або видалений з етапу. +settings.event_pull_request_milestone=Етапи +settings.event_pull_request_milestone_desc=Етап призначено, видалено або змінено. settings.event_pull_request_comment=Коментарі settings.event_pull_request_comment_desc=Коментар запиту на злиття створено, відредаговано чи видалено. settings.event_pull_request_review=Відгуки @@ -2336,7 +2339,7 @@ issues.author_helper = Цей користувач - автор. issues.close = Закрити задачу issues.role.owner_helper = Цей користувач є власником цього репозиторію. settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push та pull дзеркала можна дізнатися тут: -issues.comment.blocked_by_user = Ви не можете створити коментар до цієї задачі, оскільки вас заблокував власник репозиторію або автор цієї задачі. +issues.comment.blocked_by_user = Ви не можете коментувати цю задачу, оскільки вас заблокував власник репозиторію або автор цієї задачі. editor.add_file = Додати файл from_comment = (коментар) editor.add = Додати %s @@ -2579,6 +2582,15 @@ n_release_few = %s випусків release.releases_for = Випуски %s release.type_attachment = Вкладення n_release_one = %s випуск +pulls.comment.blocked_by_user = Ви не можете коментувати цей запит на злиття, оскільки вас заблокував власник репозиторію або автор запиту на злиття. +issues.reopen.blocked_by_user = Ви не можете знову відкрити цю задачу, оскільки вас заблокував власник репозиторію або автор задачі. +settings.actions_desc = Увімкнути вбудовані конвеєри CI/CD з Діями Forgejo +tree_path_not_found_commit = Шлях %[1]s не існує в коміті %[2]s +pulls.blocked_by_user = Ви не можете створити запит на злиття в цьому репозиторії, оскільки вас заблокував власник репозиторію. +issues.blocked_by_user = Ви не можете створювати задачі в цьому репозиторії, оскільки вас заблокував власник репозиторію. +activity.commit = К-ть комітів +issues.filter_no_results_placeholder = Спробуйте змінити пошукові фільтри. +issues.filter_no_results = Нічого не знайдено [graphs] contributors.what = внески @@ -2703,6 +2715,7 @@ teams.invite.title = Вас запрошено приєднатися до ко form.name_reserved = Назву організації «%s» зарезервовано. settings.change_orgname_redirect_prompt.with_cooldown.one = Стара назва буде доступна всім після періоду захисту, який триватиме %[1]d день. Протягом періоду захисту ви ще можете повернути стару назву. settings.change_orgname_redirect_prompt.with_cooldown.few = Стара назва буде доступна всім після періоду захисту, який триватиме %[1]d днів. Протягом періоду захисту ви ще можете повернути стару назву. +teams.none_access = Немає доступу [admin] dashboard=Панель управління @@ -2966,7 +2979,7 @@ auths.tip.google_plus=Отримайте облікові дані клієнт auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (/.well-known/openid-configuration) для автоматичної настройки входу OAuth auths.tip.twitter=Перейдіть на %s, створіть програму і переконайтеся, що включена опція «Дозволити цю програму для входу в систему за допомогою Twitter» auths.tip.discord=Зареєструйте новий додаток на %s -auths.tip.yandex=Створіть новий додаток на %s. Виберіть наступні дозволи з «Yandex.Passport API»: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» +auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) auths.edit=Редагувати джерело автентифікації auths.activated=Це джерело авторизації активоване @@ -3188,6 +3201,7 @@ users.reserved = Зарезервовано systemhooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки спрацьовуватимуть для всіх репозиторіїв системи, тож врахуйте всі ймовірні наслідки для швидкодії. Докладніше — в посібнику з вебхуків. dashboard.cleanup_actions = Очистити прострочені журнали й артефакти від дій dashboard.gc_lfs = Виконати очистку сміття метаоб'єктів LFS +dashboard.new_version_hint = Вийшла %s версія Forgejo, ви використовуєте %s. Докладніше читайте у блозі. [action] @@ -3296,8 +3310,8 @@ arch.version.conflicts = Суперечки arch.version.replaces = Заміни arch.version.provides = Надає arch.version.groups = Група -conda.install = Аби встановити пакунок, використовуючи Conda, запустіть команду: -cargo.install = Аби встановити пакунок, використовуючи Cargo, запустіть команду: +conda.install = Аби встановити пакунок, використовуючи Conda, виконайте команду: +cargo.install = Аби встановити пакунок, використовуючи Cargo, виконайте команду: versions.view_all = Переглянути всі generic.download = Завантажте пакунок із командного рядка: details = Подробиці @@ -3305,7 +3319,7 @@ arch.version.optdepends = Необовʼязково залежить installation = Установлення details.license = Ліцензія filter.type.all = Усі -conan.install = Аби встановити пакунок, використовуючи Conan, запустіть команду: +conan.install = Аби встановити пакунок, використовуючи Conan, виконайте команду: container.layers = Шари образу details.project_site = Вебсторінка проєкту details.documentation_site = Вебсторінка документації @@ -3314,12 +3328,12 @@ requirements = Вимоги dependencies = Залежності empty.repo = Ви опублікували пакунок, але він не показаний тут? Перейдіть до налаштувань пакунків та привʼяжіть його до цього репозиторію. alpine.repository = Про репозиторій -alpine.install = Аби встановити цей пакунок, запустіть команду: -cran.install = Аби встановити пакунок, запустіть команду: +alpine.install = Аби встановити цей пакунок, виконайте команду: +cran.install = Аби встановити пакунок, виконайте команду: composer.dependencies.development = Залежності розробки container.labels.key = Ключ container.labels.value = Значення -composer.install = Аби встановити пакунок, використовуючи Composer, запустіть команду: +composer.install = Аби встановити пакунок, використовуючи Composer, виконайте команду: debian.repository.components = Складові filter.container.tagged = Відмічений filter.container.untagged = Невідмічений @@ -3329,20 +3343,20 @@ arch.pacman.sync = Синхронізуйте пакунок з pacman: arch.pacman.conf = Додайте сервер з повʼязаним дострибутивом та архітектурою до /etc/pacman.conf : arch.version.properties = Властивості версії arch.version.description = Опис -chef.install = Аби встановити пакунок, запустіть команду: +chef.install = Аби встановити пакунок, виконайте команду: container.details.platform = Платформа container.details.type = Тип образу container.pull = Завантажити образ із командного рядка: details.repository_site = Вебсторінка репозиторію composer.dependencies = Залежності -debian.install = Аби встановити пакунок, запустіть команду: +debian.install = Аби встановити пакунок, виконайте команду: debian.repository = Про репозиторій debian.repository.distributions = Дистрибутиви alpine.repository.architectures = Архітектури arch.version.depends = Залежить go.install = Встановити пакунок із командного рядка: debian.repository.architectures = Архітектури -helm.install = Аби встановити пакунок, запустіть команду: +helm.install = Аби встановити пакунок, виконайте команду: keywords = Ключові слова assets = Ресурси versions = Версії @@ -3377,6 +3391,17 @@ alt.install = Встановити пакунок alt.registry = Налаштуйте цей реєстр із командного рядка: debian.registry = Налаштуйте цей реєстр із командного рядка: debian.registry.info = Виберіть $distribution і $component зі списку нижче. +npm.install = Аби встановити пакунок, використовуючи npm, виконайте команду: +alt.registry.install = Щоб установити пакунок, виконайте команду: +swift.install2 = і виконайте команду: +rubygems.install = Аби встановити пакунок, використовуючи gem, виконайте команду: +alt.setup = Додайте репозиторій до списку підключених репозиторіїв (виберіть потрібну архітектуру замість «_arch_»): +pypi.install = Аби встановити пакунок, використовуючи pip, виконайте команду: +nuget.install = Аби встановити пакунок, використовуючи NuGet, виконайте команду: +pub.install = Аби встановити пакунок, використовуючи Dart, виконайте команду: +rpm.install = Щоб установити пакунок, виконайте команду: +maven.install = Для використання пакунка включіть у блок dependencies у файлі pom.xml таке: +vagrant.install = Щоб додати скриньку Vagrant, виконайте команду: [secrets] deletion = Видалити секрет @@ -3454,6 +3479,9 @@ need_approval_desc = Потрібне схвалення для запуску variables.not_found = Не вдалося знайти змінну. runners.task_list.done_at = Завершено runners.last_online = Востаннє в мережі +runs.no_workflows.help_no_write_access = Щоб дізнатися більше про Дії Forgejo, читайте документацію. +runs.no_workflows.help_write_access = Не знаєте, як почати роботу з Діями Forgejo? Перегляньте посібник для початківців у документації, щоб написати свій перший робочий потік, а потім налаштуйте ранер Forgejo для виконання завдань. +unit.desc = Керування вбудованими конвеєрами CI/CD з Діями Forgejo. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index e7d2ac973a..b9fbc4b0ff 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -2750,8 +2750,8 @@ rss.must_be_on_branch = 您必须处于一个分支上才能拥有一个RSS订 admin.manage_flags = 管理标志 admin.failed_to_replace_flags = 替换仓库标志失败 clone_in_vscodium = 在 VSCodium 中克隆 -issues.blocked_by_user = 你无法在此仓库创建工单,因为你已被仓库所有者屏蔽。 -issues.comment.blocked_by_user = 你无法对此工单进行评论,因为你已被仓库所有者或此工单的发布者屏蔽。 +issues.blocked_by_user = 由于你已被仓库所有者屏蔽,你无法在此仓库创建工单。 +issues.comment.blocked_by_user = 因为你已被仓库所有者或工单作者屏蔽,你无法对此工单进行评论。 settings.wiki_rename_branch_main_desc = 将百科内部使用的分支重命名为“%s”。此更改是永久性的且不可撤销。 editor.invalid_commit_mail = 用于创建提交的邮件地址无效。 pulls.blocked_by_user = 你无法在此仓库上创建合并请求,因为您已被仓库所有者屏蔽。 @@ -2901,6 +2901,10 @@ archive.pull.noreview = 此仓库已存档,您无法评审合并请求。 commits.view_single_diff = 查看该提交对本文件的更改 pulls.editable = 可编辑 pulls.editable_explanation = 此合并请求允许维护者进行编辑。你可以直接向其贡献。 +issues.reopen.blocked_by_user = 由于你已被仓库所有者或工单作者屏蔽,你不能重新打开此工单。 +pulls.comment.blocked_by_user = 由于你已被仓库所有者或合并请求作者屏蔽,你不能在此合并请求发表评论。 +issues.filter_no_results = 无结果 +issues.filter_no_results_placeholder = 尝试调整搜索筛选条件。 [graphs] component_loading=正在加载 %s… @@ -3860,7 +3864,7 @@ search_in_external_registry = 在 %s 中搜索 alt.registry.install = 要安装此软件包,请运行以下命令: alt.install = 安装软件包 alt.registry = 通过命令行配置此注册表: -alt.setup = 添加一个仓库到连接的仓库列表(选择必要的架构而非'_arch_'): +alt.setup = 添加一个仓库到连接的仓库列表(选择必要的架构而非“_arch_”): alt.repository = 仓库信息 alt.repository.architectures = 架构 alt.repository.multiple_groups = 此软件包在多个组中可用。 diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 8d028b8367..8ae2abdb93 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -1,11 +1,19 @@ { - "repo.pulls.merged_title_desc": { - "one": "sloučil %[1]d commit z %[2]s do %[3]s %[4]s", - "other": "sloučil %[1]d commity z větve %[2]s do větve %[3]s před %[4]s" - }, - "repo.pulls.title_desc": { - "one": "žádá o sloučení %[1]d commitu z %[2]s do %[3]s", - "other": "chce sloučit %[1]d commity z větve %[2]s do %[3]s" - }, - "search.milestone_kind": "Hledat milníky..." + "repo.pulls.merged_title_desc": { + "one": "sloučil/a %[1]d revizi z větve %[2]s do větve %[3]s %[4]s", + "few": "sloučil/a %[1]d revize z větve %[2]s do větve %[3]s před %[4]s", + "other": "sloučil/a %[1]d revizí z větve %[2]s do větve %[3]s před %[4]s" + }, + "repo.pulls.title_desc": { + "one": "žádá o sloučení %[1]d revize z větve %[2]s do větve %[3]s", + "few": "žádá o sloučení %[1]d revizí z větve %[2]s do větve %[3]s", + "other": "žádá o sloučení %[1]d revizí z větve %[2]s do větve %[3]s" + }, + "search.milestone_kind": "Hledat milníky…", + "home.welcome.no_activity": "Žádná aktivita", + "home.welcome.activity_hint": "Váš feed je zatím prázdný. Zde se budou zobrazovat vaše akce a aktivity z repozitářů, které sledujete.", + "home.explore_repos": "Procházet repozitáře", + "home.explore_users": "Procházet uživatele", + "home.explore_orgs": "Procházet organizace", + "incorrect_root_url": "Tato instance Forgejo je nastavena tak, aby běžela na adrese „%s“. Vy si momentálně prohlížíte Forgejo na jiné adrese, což může způsobit rozbití některých částí aplikace. Správná adresa je ovládána správci Forgejo pomocí nastavení ROOT_URL v souboru app.ini." } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index 0c2c9a25ea..36eebe9c93 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -1,3 +1,17 @@ { - "search.milestone_kind": "Søg milepæle..." + "search.milestone_kind": "Søg milepæle...", + "repo.pulls.title_desc": { + "one": "ønsker at flette %[1]d commit fra %[2]s til %[3]s", + "other": "ønsker at flette %[1]d commits fra %[2]s til %[3]s" + }, + "repo.pulls.merged_title_desc": { + "one": "flettet %[1]d commit fra %[2]s til %[3]s %[4]s", + "other": "flettet %[1]d commits fra %[2]s til %[3]s %[4]s" + }, + "incorrect_root_url": "Denne Forgejo-instans er konfigureret til at blive serveret på \"%s\". Du ser i øjeblikket Forgejo gennem en anden URL, hvilket kan få dele af applikationen til at gå i stykker. Den kanoniske URL styres af Forgejo-administratorer via indstillingen ROOT_URL i app.ini.", + "home.welcome.no_activity": "Ingen aktivitet", + "home.welcome.activity_hint": "Der er intet i dit feed endnu. Dine handlinger og aktivitet fra depoterne, som du ser, vises her.", + "home.explore_repos": "Udforsk depoter", + "home.explore_users": "Udforsk brugere", + "home.explore_orgs": "Udforsk organisationer" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index f4a15ecdb9..a39f5b12bc 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -1,11 +1,16 @@ { - "repo.pulls.merged_title_desc": { - "one": "hat %[1]d Commit von %[2]s nach %[3]s %[4]s zusammengeführt", - "other": "hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt" - }, - "repo.pulls.title_desc": { - "one": "möchte %[1]d Commit von %[2]s nach %[3]s zusammenführen", - "other": "möchte %[1]d Commits von %[2]s nach %[3]s zusammenführen" - }, - "search.milestone_kind": "Meilensteine suchen …" + "repo.pulls.merged_title_desc": { + "one": "hat %[1]d Commit von %[2]s nach %[3]s %[4]s zusammengeführt", + "other": "hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt" + }, + "repo.pulls.title_desc": { + "one": "möchte %[1]d Commit von %[2]s nach %[3]s zusammenführen", + "other": "möchte %[1]d Commits von %[2]s nach %[3]s zusammenführen" + }, + "search.milestone_kind": "Meilensteine suchen …", + "home.welcome.no_activity": "Keine Aktivität", + "home.explore_repos": "Repositorys erkunden", + "home.explore_users": "Benutzer erkunden", + "home.explore_orgs": "Organisationen erkunden", + "home.welcome.activity_hint": "Es gibt noch nichts in deinem Feed. Deine Aktionen und Aktivitäten aus Repositorys, die du beobachtest, werden hier auftauchen." } diff --git a/options/locale_next/locale_fa-IR.json b/options/locale_next/locale_fa-IR.json index 0a703d22d7..78d6944203 100644 --- a/options/locale_next/locale_fa-IR.json +++ b/options/locale_next/locale_fa-IR.json @@ -1,8 +1,16 @@ { - "repo.pulls.merged_title_desc": { - "other": "%[1]d کامیت ادغام شده از %[2]s به %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "other": "قصد ادغام %[1]d تغییر را از %[2]s به %[3]s دارد" - } + "repo.pulls.merged_title_desc": { + "one": "واکشی %[1]d سپرده از %[2]s به %[3]s %[4]s", + "other": "واکشی %[1]d سپرده‌ها از %[2]s به %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "خواست واکشی %[1]d سپرده را از %[2]s به %[3]s دارد", + "other": "خواست واکشی %[1]d سپرده‌ها را از %[2]s به %[3]s دارد" + }, + "home.welcome.activity_hint": "هنوز چیزی در آزوغه شما نیست. در اینجا کنش‌ها و کوشش‌های شما در مخازنی که نگاه‌بانی می‌کنید نمایش داده خواهد شد.", + "search.milestone_kind": "جستجو نقاط عطف...", + "home.welcome.no_activity": "بدون کوشش", + "home.explore_repos": "کاوش مخازن", + "home.explore_users": "کاوش کاربران", + "home.explore_orgs": "کاوش سازمان‌ها" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index a16e3aaf8a..a0d57309ff 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -1,11 +1,19 @@ { - "repo.pulls.merged_title_desc": { - "one": "iekļāva %[1]d iesūtījumu no %[2]s %[3]s %[4]s", - "other": "Iekļāva %[1]d iesūtījumus no %[2]s zarā %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "one": "vēlas iekļaut %[1]d iesūtījumu no %[2]s %[3]s", - "other": "vēlas iekļaut %[1]d iesūtījumus no %[2]s zarā %[3]s" - }, - "search.milestone_kind": "Meklēt atskaites punktus..." + "repo.pulls.merged_title_desc": { + "zero": "iekļāva %[1]d iesūtījumu no %[2]s %[3]s %[4]s", + "one": "Iekļāva %[1]d iesūtījumu no %[2]s zarā %[3]s %[4]s", + "other": "Iekļāva %[1]d iesūtījumus no %[2]s zarā %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "zero": "vēlas iekļaut %[1]d iesūtījumu no %[2]s %[3]s", + "one": "vēlas iekļaut %[1]d iesūtījumu no %[2]s zarā %[3]s", + "other": "vēlas iekļaut %[1]d iesūtījumus no %[2]s zarā %[3]s" + }, + "search.milestone_kind": "Meklēt atskaites punktus...", + "home.welcome.no_activity": "Nav darbību", + "home.welcome.activity_hint": "Barotnē pagaidām nekā nav. Šeit parādīsies darbības un vēroto glabātavu notikumi.", + "home.explore_repos": "Izpētīt glabātavas", + "home.explore_users": "Izpētīt lietotājus", + "home.explore_orgs": "Izpētīt apvienības", + "incorrect_root_url": "Šis Forgejo serveris ir konfigurēts darboties \"%s\". Pašlaik Forgejo tiek apmeklēta ar atšķirīgu URL, kas var radīt atsevišķu lietotnes daļu salūšanu. Kanonisko URL pārrauga Forgejo pārvaldītāji ar app.ini iestatījumu ROOT_URL." } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 564302820a..a587997da3 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -1,11 +1,17 @@ { - "repo.pulls.merged_title_desc": { - "one": "hett %[1]d Kommitteren vun %[2]s na %[3]s %[4]s tosamenföhrt", - "other": "hett %[1]d Kommitterens vun %[2]s na %[3]s %[4]s tosamenföhrt" - }, - "repo.pulls.title_desc": { - "one": "will %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhren", - "other": "will %[1]d Kommitterens vun %[2]s na %[3]s tosamenföhren" - }, - "search.milestone_kind": "In Markstenen söken …" + "repo.pulls.merged_title_desc": { + "one": "hett %[4]s %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhrt", + "other": "hett %[4]s %[1]d Kommitterens vun %[2]s na %[3]s tosamenföhrt" + }, + "repo.pulls.title_desc": { + "one": "will %[1]d Kommitteren vun %[2]s na %[3]s tosamenföhren", + "other": "will %[1]d Kommitterens vun %[2]s na %[3]s tosamenföhren" + }, + "search.milestone_kind": "In Markstenen söken …", + "home.explore_users": "Brukers utförsken", + "home.explore_orgs": "Vereenigungen utförsken", + "home.explore_repos": "Repositoriums utförsken", + "home.welcome.no_activity": "Keen Doon", + "home.welcome.activity_hint": "In dienem Schuuv is noch nix. Dien Doon un dat Doon vun Repositoriums, wat du beluurst, word hier wiest worden.", + "incorrect_root_url": "Deese Forgejo-Instanz is inricht, unner »%s« besöcht to worden. Du bekiekst Forgejo jüüst dör een anner URL, wat daarto föhren kann, dat ’t deelwies nich richtig warkt. De kanonisk URL word vun de Forgejo-Chefs över de ROOT_URL-Instellen in de app.ini kuntrolleert." } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index fbb78e9280..cfa80104e8 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -1,11 +1,17 @@ { - "repo.pulls.merged_title_desc": { - "one": "heeft %[1]d commit van %[2]s samengevoegd in %[3]s %[4]s", - "other": "heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "one": "wilt %[1]d commit van %[2]s samenvoegen in %[3]s", - "other": "wilt %[1]d commits van %[2]s samenvoegen met %[3]s" - }, - "search.milestone_kind": "Zoek mijlpalen..." + "repo.pulls.merged_title_desc": { + "one": "heeft %[1]d commit van %[2]s samengevoegd in %[3]s %[4]s", + "other": "heeft %[1]d commits samengevoegd van %[2]s naar %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "wilt %[1]d commit van %[2]s samenvoegen in %[3]s", + "other": "wilt %[1]d commits van %[2]s samenvoegen met %[3]s" + }, + "search.milestone_kind": "Zoek mijlpalen...", + "home.welcome.no_activity": "Geen activiteit", + "home.welcome.activity_hint": "Er staat nog niets in uw feed. Uw acties en activiteiten van repositories die u bekijkt zullen hier verschijnen.", + "home.explore_repos": "Ontdek repositories", + "home.explore_users": "Ontdek gebruikers", + "home.explore_orgs": "Ontdek organisaties", + "incorrect_root_url": "Deze Forgejo-instantie is geconfigureerd om geserveerd te worden op \"%s\". U bekijkt Forgejo momenteel via een andere URL, waardoor onderdelen van de applicatie kunnen breken. De canonieke URL kan worden gewijzigd door Forgejo admins via de ROOT_URL instelling in de app.ini." } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 4de44582a5..a66d679e96 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -1,11 +1,19 @@ { - "repo.pulls.merged_title_desc": { - "one": "mesclou %[1]d commit de %[2]s em %[3]s %[4]s", - "other": "mesclou %[1]d commits de %[2]s em %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "one": "quer mesclar %[1]d commit de %[2]s em %[3]s", - "other": "quer mesclar %[1]d commits de %[2]s em %[3]s" - }, - "search.milestone_kind": "Pesquisar marcos..." + "repo.pulls.merged_title_desc": { + "one": "mesclou %[1]d commit de %[2]s em %[3]s %[4]s", + "many": "mesclou %[1]d commits de %[2]s em %[3]s %[4]s", + "other": "" + }, + "repo.pulls.title_desc": { + "one": "quer mesclar %[1]d commit de %[2]s em %[3]s", + "many": "quer mesclar %[1]d commits de %[2]s em %[3]s", + "other": "" + }, + "search.milestone_kind": "Pesquisar marcos...", + "home.welcome.no_activity": "Sem atividade", + "home.welcome.activity_hint": "Ainda não tem nada no seu feed. Suas ações e atividade dos seus repositórios vigiados aparecerão aqui.", + "home.explore_repos": "Explorar repositórios", + "home.explore_users": "Explorar usuários", + "home.explore_orgs": "Explorar organizações", + "incorrect_root_url": "Esta instância do Forgejo está configurada para o endereço \"%s\". Você está atualmente vendo o Forgejo através de uma URL diferente, o que pode causar erros em algumas partes da aplicação. A URL oficial é controlada pela administração do Forgejo através da configuração ROOT_URL no arquivo app.ini." } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index cf72ef3e09..4d58ffff73 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -1,11 +1,18 @@ { - "repo.pulls.merged_title_desc": { - "one": "integrou %[1]d cometimento do ramo %[2]s no ramo %[3]s %[4]s", - "other": "integrou %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "one": "quer integrar %[1]d cometimento do ramo %[2]s no ramo %[3]s", - "other": "quer integrar %[1]d cometimento(s) do ramo %[2]s no ramo %[3]s" - }, - "search.milestone_kind": "Procurar etapas..." + "repo.pulls.merged_title_desc": { + "one": "integrou %[1]d cometimento do ramo %[2]s no ramo %[3]s %[4]s", + "many": "integrou %[1]d cometimentos do ramo %[2]s no ramo %[3]s %[4]s", + "other": "" + }, + "repo.pulls.title_desc": { + "one": "quer integrar %[1]d cometimento do ramo %[2]s no ramo %[3]s", + "many": "quer integrar %[1]d cometimentos do ramo %[2]s no ramo %[3]s", + "other": "" + }, + "search.milestone_kind": "Procurar etapas...", + "home.welcome.no_activity": "Sem atividade", + "home.welcome.activity_hint": "Ainda não há nada no seu feed. As suas operações e a atividade dos repositórios que vigia aparecerão aqui.", + "home.explore_repos": "Explorar repositórios", + "home.explore_users": "Explorar utilizadores", + "home.explore_orgs": "Explorar organizações" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 36f54f405b..3db0072760 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -1,11 +1,19 @@ { - "repo.pulls.merged_title_desc": { - "one": "слит %[1]d коммит из %[2]s в %[3]s %[4]s", - "many": "слито %[1]d коммит(ов) из %[2]s в %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "one": "хочет влить %[1]d коммит из %[2]s в %[3]s", - "many": "хочет влить %[1]d коммит(ов) из %[2]s в %[3]s" - }, - "search.milestone_kind": "Найти этапы..." + "repo.pulls.merged_title_desc": { + "one": "слит %[1]d коммит из %[2]s в %[3]s %[4]s", + "few": "слито %[1]d коммита из %[2]s в %[3]s %[4]s", + "many": "слито %[1]d коммитов из %[2]s в %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "хочет влить %[1]d коммит из %[2]s в %[3]s", + "few": "хочет влить %[1]d коммита из %[2]s в %[3]s", + "many": "хочет влить %[1]d коммитов из %[2]s в %[3]s" + }, + "search.milestone_kind": "Найти этапы...", + "home.explore_repos": "Каталог репозиториев", + "home.explore_users": "Каталог пользователей", + "home.explore_orgs": "Каталог организаций", + "home.welcome.activity_hint": "В вашей ленте пока ничего нет. Ваши действия и активность из отслеживаемых вами репозиториев будут отображены здесь.", + "home.welcome.no_activity": "Нет событий", + "incorrect_root_url": "Этот сервер Forgejo расположен по адресу «%s», но вы просматриваете страницу с другого адреса. Это может приводить к поломкам частей приложения. Канонический адрес указывается администратором сервера в файле конфигурации app.ini - ROOT_URL." } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index ce37b9bef3..f76e502124 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -1,11 +1,19 @@ { - "repo.pulls.merged_title_desc": { - "one": "об'єднав %[1]d коміт з %[2]s в %[3]s %[4]s", - "many": "об'єднав %[1]d комітів з %[2]s в %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "one": "хоче об'єднати %[1]d коміт з %[2]s в %[3]s", - "many": "хоче об'єднати %[1]d комітів з %[2]s в %[3]s" - }, - "search.milestone_kind": "Шукати віхи..." + "repo.pulls.merged_title_desc": { + "one": "об'єднує %[1]d коміт з %[2]s в %[3]s %[4]s", + "few": "об'єднує %[1]d коміти з %[2]s в %[3]s %[4]s", + "many": "об'єднує %[1]d комітів з %[2]s в %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "one": "хоче об'єднати %[1]d коміт з %[2]s в %[3]s", + "few": "хоче об'єднати %[1]d коміти з %[2]s в %[3]s", + "many": "хоче об'єднати %[1]d комітів з %[2]s в %[3]s" + }, + "search.milestone_kind": "Шукати етапи...", + "home.welcome.activity_hint": "У вашій стрічці ще нічого немає. Тут з'являтимуться ваші дії та активність із відстежуваних репозиторіїв.", + "home.welcome.no_activity": "Немає подій", + "home.explore_repos": "Огляд репозиторіїв", + "home.explore_users": "Огляд користувачів", + "home.explore_orgs": "Огляд організацій", + "incorrect_root_url": "Цей екземпляр Forgejo налаштовано на відвідування з «%s». Зараз ви переглядаєте Forgejo за іншою URL-адресою, що може призвести до збоїв деяких частин програми. Канонічна URL-адреса встановлюється адміністраторами Forgejo за допомогою параметра ROOT_URL у файлі app.ini." } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 10b1c9a4be..944b3dbf47 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -1,9 +1,11 @@ { - "repo.pulls.merged_title_desc": { - "other": "于 %[4]s 将 %[1]d 次代码提交从 %[2]s合并至 %[3]s" - }, - "repo.pulls.title_desc": { - "other": "请求将 %[1]d 次代码提交从 %[2]s 合并至 %[3]s" - }, - "search.milestone_kind": "搜索里程碑…" + "repo.pulls.merged_title_desc": "于 %[4]s 将 %[1]d 次代码提交从 %[2]s合并至 %[3]s", + "repo.pulls.title_desc": "请求将 %[1]d 次代码提交从 %[2]s 合并至 %[3]s", + "search.milestone_kind": "搜索里程碑…", + "home.welcome.no_activity": "无活动", + "home.welcome.activity_hint": "您的订阅中还没有任何内容。您关注的仓库中的操作和活动将显示在此处。", + "home.explore_repos": "探索仓库", + "home.explore_users": "探索用户", + "home.explore_orgs": "探索组织", + "incorrect_root_url": "此 Forgejo 实例配置为在“%s”上提供服务。您当前正在通过不同的网址查看 Forgejo,这可能会导致应用程序的某些部分损坏。Forgejo 管理员通过 app.ini 中的 ROOT_URL 设置控制规范网址。" } From 7b16ac9b8558e152673d3be8f7fb9e314973d204 Mon Sep 17 00:00:00 2001 From: christopher-besch Date: Fri, 7 Mar 2025 17:54:26 +0000 Subject: [PATCH 249/669] feat: parse inline attachments for incoming mail (#7136) - Some email clients send inline attachments using the `multipart/related` Mime-Type and enmime collects these in the `Envelope.OtherParts` list; until now only Envelope.Attachments and Envelope.Inline were considered while parsing incoming mail. - As some email clients send attachments without filename, especially in the multipart/related case, this PR implements `constructFilename`, which guesses the filename based on the ContentType. How the issue was disovered: I implemented an otherwise unrelated application written in go that parses emails with enmime just like Forgejo does. I noticed that in a few occasions that system would fail to detect all attachments. Investigating this issue led me to realize the above described issue. After implementing a fix for that application, I looked through the Forgejo email parsing code and discovered the same problem. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7136 Reviewed-by: Gusted Co-authored-by: christopher-besch Co-committed-by: christopher-besch --- services/mailer/incoming/incoming.go | 39 ++++- services/mailer/incoming/incoming_test.go | 188 ++++++++++++++++++++++ 2 files changed, 224 insertions(+), 3 deletions(-) diff --git a/services/mailer/incoming/incoming.go b/services/mailer/incoming/incoming.go index 092f738cd9..1b1be4c656 100644 --- a/services/mailer/incoming/incoming.go +++ b/services/mailer/incoming/incoming.go @@ -7,8 +7,10 @@ import ( "context" "crypto/tls" "fmt" + "mime" net_mail "net/mail" "regexp" + "slices" "strings" "time" @@ -374,25 +376,56 @@ type Attachment struct { // getContentFromMailReader grabs the plain content and the attachments from the mail. // A potential reply/signature gets stripped from the content. func getContentFromMailReader(env *enmime.Envelope) *MailContent { + // get attachments attachments := make([]*Attachment, 0, len(env.Attachments)) for _, attachment := range env.Attachments { attachments = append(attachments, &Attachment{ - Name: attachment.FileName, + Name: constructFilename(attachment), Content: attachment.Content, }) } + // get inlines inlineAttachments := make([]*Attachment, 0, len(env.Inlines)) for _, inline := range env.Inlines { if inline.FileName != "" && inline.ContentType != "text/plain" { inlineAttachments = append(inlineAttachments, &Attachment{ - Name: inline.FileName, + Name: constructFilename(inline), Content: inline.Content, }) } } + // get other parts (mostly multipart/related files, these are for example embedded images in an html mail) + otherParts := make([]*Attachment, 0, len(env.Inlines)) + for _, otherPart := range env.OtherParts { + otherParts = append(otherParts, &Attachment{ + Name: constructFilename(otherPart), + Content: otherPart.Content, + }) + } return &MailContent{ Content: reply.FromText(env.Text), - Attachments: append(attachments, inlineAttachments...), + Attachments: slices.Concat(attachments, inlineAttachments, otherParts), } } + +// constructFilename interprets the mime part as an (inline) attachment and returns its filename +// If no filename is given it guesses a sensible filename for it based on the filetype. +func constructFilename(part *enmime.Part) string { + if strings.TrimSpace(part.FileName) != "" { + return part.FileName + } + + filenameWOExtension := "unnamed_file" + if strings.TrimSpace(part.ContentID) != "" { + filenameWOExtension = part.ContentID + } + + fileExtension := ".unknown" + mimeExtensions, err := mime.ExtensionsByType(part.ContentType) + if err == nil && len(mimeExtensions) != 0 { + // just use the first one we find + fileExtension = mimeExtensions[0] + } + return filenameWOExtension + fileExtension +} diff --git a/services/mailer/incoming/incoming_test.go b/services/mailer/incoming/incoming_test.go index fe11c9e5c6..2ffaac57ae 100644 --- a/services/mailer/incoming/incoming_test.go +++ b/services/mailer/incoming/incoming_test.go @@ -4,6 +4,7 @@ package incoming import ( + "encoding/base64" "strings" "testing" @@ -194,4 +195,191 @@ func TestGetContentFromMailReader(t *testing.T) { require.NoError(t, err) assert.Equal(t, "mail content without signature", content.Content) assert.Empty(t, content.Attachments) + + // Some versions of Outlook send inline attachments like this, inside a multipart/related part. + // the attached image is from: https://openmoji.org/library/emoji-1F684 + mailString = "Content-Type: multipart/related; boundary=\"=_related boundary=\"\r\n" + + "\r\n" + + "This text is for clients unable to decode multipart/related with multipart/alternative.\r\n" + + "\r\n" + + "--=_related boundary=\r\n" + + "Content-Type: multipart/alternative; boundary=\"=_alternative boundary=\"\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "--=_alternative boundary=\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "This is the plaintext.\r\n" + + "\r\n" + + "--=_alternative boundary=\r\n" + + "Content-Type: text/html\r\n" + + "\r\n" + + "

This is a mail with multipart/related. Here is an image sent with a filename.

\r\n" + + "\r\n" + + "\r\n" + + "--=_alternative boundary=--\r\n" + + "\r\n" + + "--=_related boundary=\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "Content-Type: image/png;\r\n" + + " name=\"image001.png\"\r\n" + + "Content-ID: <_1_2845>\r\n" + + "\r\n" + + "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" + + "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" + + "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" + + "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" + + "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" + + "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" + + "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" + + "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" + + "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" + + "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" + + "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" + + "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" + + "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" + + "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" + + "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" + + "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" + + "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" + + "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" + + "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" + + "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" + + "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" + + "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" + + "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" + + "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" + + "exgAAAAASUVORK5CYII=\r\n" + + "\r\n" + + "--=_related boundary=--\r\n" + + "\r\n" + + env, err = enmime.ReadEnvelope(strings.NewReader(mailString)) + require.NoError(t, err) + content = getContentFromMailReader(env) + assert.Equal(t, "This is the plaintext.", content.Content) + assert.Len(t, content.Attachments, 1) + assert.Equal(t, "image001.png", content.Attachments[0].Name) + expectedAttachment, err := base64.StdEncoding.DecodeString( + "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" + + "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" + + "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" + + "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" + + "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" + + "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" + + "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" + + "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" + + "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" + + "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" + + "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" + + "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" + + "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" + + "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" + + "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" + + "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" + + "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" + + "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" + + "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" + + "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" + + "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" + + "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" + + "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" + + "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" + + "exgAAAAASUVORK5CYII=\r\n") + require.NoError(t, err) + assert.Equal(t, expectedAttachment, content.Attachments[0].Content) + + // HCL Notes inlines attachments like this: without a filename. + // the attached image is from: https://openmoji.org/library/emoji-1F684 + mailString = "Content-Type: multipart/related; boundary=\"=_related boundary=\"\r\n" + + "\r\n" + + "This text is for clients unable to decode multipart/related with multipart/alternative.\r\n" + + "\r\n" + + "--=_related boundary=\r\n" + + "Content-Type: multipart/alternative; boundary=\"=_alternative boundary=\"\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "--=_alternative boundary=\r\n" + + "Content-Type: text/plain\r\n" + + "\r\n" + + "This is the plaintext.\r\n" + + "\r\n" + + "--=_alternative boundary=\r\n" + + "Content-Type: text/html\r\n" + + "\r\n" + + "

This is a mail with multipart/related. Here is an image sent without a filename.

\r\n" + + "\r\n" + + "\r\n" + + "--=_alternative boundary=--\r\n" + + "\r\n" + + "--=_related boundary=\r\n" + + "Content-Transfer-Encoding: base64\r\n" + + "Content-Type: image/png\r\n" + + "Content-ID: <_1_2845>\r\n" + + "\r\n" + + "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" + + "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" + + "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" + + "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" + + "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" + + "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" + + "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" + + "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" + + "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" + + "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" + + "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" + + "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" + + "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" + + "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" + + "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" + + "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" + + "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" + + "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" + + "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" + + "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" + + "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" + + "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" + + "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" + + "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" + + "exgAAAAASUVORK5CYII=\r\n" + + "\r\n" + + "--=_related boundary=--\r\n" + + "\r\n" + + env, err = enmime.ReadEnvelope(strings.NewReader(mailString)) + require.NoError(t, err) + content = getContentFromMailReader(env) + assert.Equal(t, "This is the plaintext.", content.Content) + assert.Len(t, content.Attachments, 1) + assert.Equal(t, "_1_2845.png", content.Attachments[0].Name) + expectedAttachment, err = base64.StdEncoding.DecodeString( + "iVBORw0KGgoAAAANSUhEUgAAAEAAAAAiCAYAAADvVd+PAAAFLUlEQVRo3t2ZX0iTXxjHP3u35qvT\r\n" + + "6ZzhzKFuzPQq9WKQZS6FvLQf3Wh30ViBQXnViC5+LVKEiC6DjMQgCCy6NChoIKwghhcR1bJ5s5Ei\r\n" + + "LmtNs/05XYT7Vercaps/94Xn4uU95znvOc/3+XdehRBCsM1YXl7G6/Xi8Xh49uwZMzMzhEIhFhcX\r\n" + + "+fbtW87WbW1tRbVdmxZC8PTpU8bGxrh//z5fv37dcJxGo2HXrl1ZWVOhUPzybDAYUOSbAYlEgjt3\r\n" + + "7nD58mVmZ2cBkCSJ1tZWDhw4wP79+2lpaUGv16PX61Gr1Tm3RN7w/Plz0d7eLgABCKPRKJxOp3j/\r\n" + + "/r3YLuTlAD5+/ChOnDiR3HhdXZ24e/euiMfjYruRcxe4evUqV65c4fPnz6hUKrq7uzl06NA6v157\r\n" + + "19bWlrbueDzOq1evmJ6eJhQKZRww9+3blzsXWFpaEqdOnUpaPV2ZmJjYUveLFy+Ew+EQFRUVGev/\r\n" + + "WTQaTW4Y8OjRIxwOB4FAAEmS0Gq1lJWVpZwTjUaZm5vDZrPhdrs3HOP3+3E6nTx48IC1zy4uLqas\r\n" + + "rAy1Wr0uym8FnU6X3TT46dMnzp8/z82bNwHQarU0NTVRUlKScl44HMbn8wFQU1Oz7n0sFuP69etc\r\n" + + "unSJ5eVllEole/bswWAwbKk7FSRJyl4a/NnqSqWS+vp6jEZjSqskEglmZ2cJBoMIIbBYLExNTWEw\r\n" + + "GJJjvF4vDoeD6elpAKqrqzGbzVlJj5Ik/T0D/tTqS0tL+Hw+VlZWUKlUDAwMMDQ0RGlpKQArKyu4\r\n" + + "XC6uXbtGLBZDlmUaGxuprKzMajGmyrfVY7EYfr+fDx8+ANDS0sLo6ChWqzU5xu12c/r0aXw+HwqF\r\n" + + "gtraWkwmE0qlMutZSpVPq8/NzeH3+4lGo5SUlOByuRgcHESl+u8zLly4wMjICAClpaU0NTUlWZEL\r\n" + + "ZBwDfo/wDQ0NKa0ej8dZWFggEAgQiUQA6Onp4caNG5jN5l/GTk1N0dnZmTab8sqA+fl5jh07hsfj\r\n" + + "AUCWZXbv3g1AIBBYR/NoNEokEuHLly8kEgkATCYTQ0NDHD9+fFOGrKW2jfRmGxqNJr1CaHJyUuj1\r\n" + + "+j8qNiRJEp2dneL27dtidXU15TrhcFhYLJa/Km4ykeLi4tSF0O++Xl9fz5EjR9Dr9SlPtry8nKqq\r\n" + + "KsxmM1arFa1Wm7ZVQqEQDx8+5N27dznvTG022+YMmJycFEajUQBClmUxMjIiYrGYKDSwVQ3f3t4u\r\n" + + "3rx5k1LJy5cvhd1uF83NzaKoqChvFP5b6e3t/fUAMrX64uKiOHnypFAoFDtm0z9Ll14nVACRSIQz\r\n" + + "Z84wPj4OwMGDBxkbG6OxsXFT/7l37x6Dg4PMz89TJEn0VVXwT2U5dUVq1DlOXdmCsrwcVTQapaen\r\n" + + "B4/HgyzLDA8Pc+7cOSRJ2nTixYsXGR4eBsBaWsK/xmrqitTsSIyOjgpAmEwm8fbt27QCx969e3ck\r\n" + + "5TdyAfr6+gQgxsfH046ct27dEjqdbscfQG9vr1CtNRiZVMR2ux273U4hQOru7gbA5XLh9Xr5H/wn\r\n" + + "yS9WV1dFR0dHQfh0ptLR0fGjDgiHw8LpdIqGhoYdm9P/RCwWS3qXok+ePOHs2bO8fv06eZ3c39+X\r\n" + + "7AZ3MlK2wzMzMwwMDPD48WMAamtr6e/vo7m5uWBCwKYMWFhYoK2tjWAwiEaj4ejRXmw2W8oCqaAO\r\n" + + "wO1209XVhSzLHD5s+3F5UGAwGo2bt8OhUEjU1NQUdBDc8s9QMBhkYmIieVVVaLDZbHwHmmIQk3rD\r\n" + + "exgAAAAASUVORK5CYII=\r\n") + require.NoError(t, err) + assert.Equal(t, expectedAttachment, content.Attachments[0].Content) } From 52422bff27378ef63c3e3370ab09e519f1aacbc8 Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Sat, 8 Mar 2025 00:43:41 +0000 Subject: [PATCH 250/669] Drop SSPI auth support and more Windows files (#7148) ## Dropping SSPI auth support SSPI authentication relied on Microsoft Windows support, removal started in https://codeberg.org/forgejo/forgejo/pulls/5353, because it was broken anyway. We have no knowledge of any users using SSPI authentication. However, if you somehow managed to run Forgejo on Windows, or want to upgrade from a Gitea version which does, please ensure that you do not use SSPI as an authentication mechanism for user accounts. Feel free to reach out if you need assistance. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7148 Reviewed-by: Gusted Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Otto Richter Co-committed-by: Otto Richter --- Makefile | 27 +-- models/asymkey/ssh_key_authorized_keys.go | 21 +- models/auth/source.go | 22 +- models/migrations/test/tests.go | 4 - modules/git/blame.go | 2 +- modules/git/command.go | 12 - modules/git/git.go | 27 +-- modules/graceful/manager_unix.go | 2 - modules/graceful/net_unix.go | 2 - modules/graceful/restart_unix.go | 2 - modules/log/color_console_other.go | 2 - modules/log/color_console_windows.go | 42 ---- modules/markup/external/external.go | 4 - modules/process/manager_unix.go | 2 - modules/repository/hooks.go | 5 - modules/setting/path.go | 6 +- modules/setting/setting.go | 8 +- modules/user/user.go | 9 +- modules/user/user_test.go | 5 - modules/util/file_unix.go | 2 - modules/util/file_unix_test.go | 2 - modules/util/path.go | 41 +--- modules/util/path_test.go | 52 +--- modules/util/remove.go | 21 -- options/locale/locale_en-US.ini | 15 +- routers/api/shared/middleware.go | 6 - routers/install/install.go | 11 +- routers/private/manager_unix.go | 2 - routers/web/admin/auths.go | 65 ----- routers/web/auth/auth.go | 2 - routers/web/web.go | 6 - services/auth/signin.go | 1 - .../auth/source/sspi/assert_interface_test.go | 18 -- services/auth/source/sspi/source.go | 39 --- services/auth/sspi.go | 223 ------------------ services/auth/sspiauth_posix.go | 30 --- services/forms/auth_form.go | 5 - templates/admin/auth/edit.tmpl | 45 ---- templates/admin/auth/new.tmpl | 3 - templates/admin/auth/source/sspi.tmpl | 43 ---- templates/user/auth/oauth_container.tmpl | 6 - tests/test_utils.go | 3 - web_src/js/features/admin/common.js | 10 +- 43 files changed, 39 insertions(+), 816 deletions(-) delete mode 100644 modules/log/color_console_windows.go delete mode 100644 services/auth/source/sspi/assert_interface_test.go delete mode 100644 services/auth/source/sspi/source.go delete mode 100644 services/auth/sspi.go delete mode 100644 services/auth/sspiauth_posix.go delete mode 100644 templates/admin/auth/source/sspi.tmpl diff --git a/Makefile b/Makefile index 4f88b2b0a5..7993d06c3d 100644 --- a/Makefile +++ b/Makefile @@ -59,20 +59,8 @@ ifeq ($(HAS_GO), yes) CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS) endif -ifeq ($(GOOS),windows) - IS_WINDOWS := yes -else ifeq ($(patsubst Windows%,Windows,$(OS)),Windows) - ifeq ($(GOOS),) - IS_WINDOWS := yes - endif -endif -ifeq ($(IS_WINDOWS),yes) - GOFLAGS := -v -buildmode=exe - EXECUTABLE ?= gitea.exe -else - GOFLAGS := -v - EXECUTABLE ?= gitea -endif +GOFLAGS := -v +EXECUTABLE ?= gitea ifeq ($(shell sed --version 2>/dev/null | grep -q GNU && echo gnu),gnu) SED_INPLACE := sed -i @@ -498,13 +486,6 @@ lint-go-fix: $(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS) --fix $(RUN_DEADCODE) > .deadcode-out -# workaround step for the lint-go-windows CI task because 'go run' can not -# have distinct GOOS/GOARCH for its build and run steps -.PHONY: lint-go-windows -lint-go-windows: - @GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE) - golangci-lint run - .PHONY: lint-go-vet lint-go-vet: @echo "Running go vet..." @@ -877,10 +858,6 @@ sources-tarbal: frontend generate vendor release-sources release-check $(DIST_DIRS): mkdir -p $(DIST_DIRS) -.PHONY: release-windows -release-windows: | $(DIST_DIRS) - CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) . - .PHONY: release-linux release-linux: | $(DIST_DIRS) CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out forgejo-$(VERSION) . diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go index d3f9f3f3be..57baf89c0c 100644 --- a/models/asymkey/ssh_key_authorized_keys.go +++ b/models/asymkey/ssh_key_authorized_keys.go @@ -87,19 +87,16 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error { } defer f.Close() - // Note: chmod command does not support in Windows. - if !setting.IsWindows { - fi, err := f.Stat() - if err != nil { - return err - } + fi, err := f.Stat() + if err != nil { + return err + } - // .ssh directory should have mode 700, and authorized_keys file should have mode 600. - if fi.Mode().Perm() > 0o600 { - log.Error("authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String()) - if err = f.Chmod(0o600); err != nil { - return err - } + // .ssh directory should have mode 700, and authorized_keys file should have mode 600. + if fi.Mode().Perm() > 0o600 { + log.Error("authorized_keys file has unusual permission flags: %s - setting to -rw-------", fi.Mode().Perm().String()) + if err = f.Chmod(0o600); err != nil { + return err } } diff --git a/models/auth/source.go b/models/auth/source.go index 214eb3afa0..d7ed6b97a8 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -32,7 +32,7 @@ const ( PAM // 4 DLDAP // 5 OAuth2 // 6 - SSPI // 7 + _ // 7 (was SSPI) Remote // 8 ) @@ -53,7 +53,6 @@ var Names = map[Type]string{ SMTP: "SMTP", PAM: "PAM", OAuth2: "OAuth2", - SSPI: "SPNEGO with SSPI", Remote: "Remote", } @@ -178,11 +177,6 @@ func (source *Source) IsOAuth2() bool { return source.Type == OAuth2 } -// IsSSPI returns true of this source is of the SSPI type. -func (source *Source) IsSSPI() bool { - return source.Type == SSPI -} - func (source *Source) IsRemote() bool { return source.Type == Remote } @@ -265,20 +259,6 @@ func (opts FindSourcesOptions) ToConds() builder.Cond { return conds } -// IsSSPIEnabled returns true if there is at least one activated login -// source of type LoginSSPI -func IsSSPIEnabled(ctx context.Context) bool { - exist, err := db.Exist[Source](ctx, FindSourcesOptions{ - IsActive: optional.Some(true), - LoginType: SSPI, - }.ToConds()) - if err != nil { - log.Error("IsSSPIEnabled: failed to query active SSPI sources: %v", err) - return false - } - return exist -} - // GetSourceByID returns login source by given ID. func GetSourceByID(ctx context.Context, id int64) (*Source, error) { source := new(Source) diff --git a/models/migrations/test/tests.go b/models/migrations/test/tests.go index 0e37233471..177e33c56a 100644 --- a/models/migrations/test/tests.go +++ b/models/migrations/test/tests.go @@ -11,7 +11,6 @@ import ( "os" "path" "path/filepath" - "runtime" "strings" "testing" "time" @@ -123,9 +122,6 @@ func MainTest(m *testing.M) { os.Exit(1) } giteaBinary := "gitea" - if runtime.GOOS == "windows" { - giteaBinary += ".exe" - } setting.AppPath = path.Join(giteaRoot, giteaBinary) if _, err := os.Stat(setting.AppPath); err != nil { fmt.Printf("Could not find gitea binary at %s\n", setting.AppPath) diff --git a/modules/git/blame.go b/modules/git/blame.go index 69e1b08f93..d62a8ca0a2 100644 --- a/modules/git/blame.go +++ b/modules/git/blame.go @@ -139,7 +139,7 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath cmd := NewCommandContextNoGlobals(ctx, "blame", "--porcelain") if ignoreRevsFile != nil { // Possible improvement: use --ignore-revs-file /dev/stdin on unix - // There is no equivalent on Windows. May be implemented if Gitea uses an external git backend. + // This was not done in Gitea because it would not have been compatible with Windows. cmd.AddOptionValues("--ignore-revs-file", *ignoreRevsFile) } cmd.AddDynamicArguments(commit.ID.String()). diff --git a/modules/git/command.go b/modules/git/command.go index 605816b7a2..72640a2a94 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -12,7 +12,6 @@ import ( "io" "os" "os/exec" - "runtime" "runtime/trace" "strings" "time" @@ -359,17 +358,6 @@ func (c *Command) Run(opts *RunOpts) error { log.Debug("slow git.Command.Run: %s (%s)", c, elapsed) } - // We need to check if the context is canceled by the program on Windows. - // This is because Windows does not have signal checking when terminating the process. - // It always returns exit code 1, unlike Linux, which has many exit codes for signals. - if runtime.GOOS == "windows" && - err != nil && - err.Error() == "" && - cmd.ProcessState.ExitCode() == 1 && - ctx.Err() == context.Canceled { - return ctx.Err() - } - if err != nil && ctx.Err() != context.DeadlineExceeded { return err } diff --git a/modules/git/git.go b/modules/git/git.go index f1174e67b9..576609068a 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -59,15 +59,7 @@ func loadGitVersion() error { return fmt.Errorf("invalid git version output: %s", stdout) } - var versionString string - - // Handle special case on Windows. - i := strings.Index(fields[2], "windows") - if i >= 1 { - versionString = fields[2][:i-1] - } else { - versionString = fields[2] - } + versionString := fields[2] var err error gitVersion, err = version.NewVersion(versionString) @@ -280,24 +272,11 @@ func syncGitConfig() (err error) { // Thus the owner uid/gid for files on these filesystems will be marked as root. // As Gitea now always use its internal git config file, and access to the git repositories is managed through Gitea, // it is now safe to set "safe.directory=*" for internal usage only. - // Please note: the wildcard "*" is only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later - // Although only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later - this setting is tolerated by earlier versions + // Please note: the wildcard "*" is only supported by Git 2.30.4/2.31.3/2.32.2/2.33.3/2.34.3/2.35.3/2.36 and later, + // but is tolerated by earlier versions if err := configAddNonExist("safe.directory", "*"); err != nil { return err } - if runtime.GOOS == "windows" { - if err := configSet("core.longpaths", "true"); err != nil { - return err - } - if setting.Git.DisableCoreProtectNTFS { - err = configSet("core.protectNTFS", "false") - } else { - err = configUnsetAll("core.protectNTFS", "false") - } - if err != nil { - return err - } - } // By default partial clones are disabled, enable them from git v2.22 if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil { diff --git a/modules/graceful/manager_unix.go b/modules/graceful/manager_unix.go index 931b0f1b62..8147743f79 100644 --- a/modules/graceful/manager_unix.go +++ b/modules/graceful/manager_unix.go @@ -1,8 +1,6 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//go:build !windows - package graceful import ( diff --git a/modules/graceful/net_unix.go b/modules/graceful/net_unix.go index 796e00507c..97b591f98f 100644 --- a/modules/graceful/net_unix.go +++ b/modules/graceful/net_unix.go @@ -3,8 +3,6 @@ // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler -//go:build !windows - package graceful import ( diff --git a/modules/graceful/restart_unix.go b/modules/graceful/restart_unix.go index 98d5c5cc20..a0f3147ec6 100644 --- a/modules/graceful/restart_unix.go +++ b/modules/graceful/restart_unix.go @@ -3,8 +3,6 @@ // This code is heavily inspired by the archived gofacebook/gracenet/net.go handler -//go:build !windows - package graceful import ( diff --git a/modules/log/color_console_other.go b/modules/log/color_console_other.go index c08b38c674..6573d093a5 100644 --- a/modules/log/color_console_other.go +++ b/modules/log/color_console_other.go @@ -1,8 +1,6 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//go:build !windows - package log import ( diff --git a/modules/log/color_console_windows.go b/modules/log/color_console_windows.go deleted file mode 100644 index 3f59e934da..0000000000 --- a/modules/log/color_console_windows.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package log - -import ( - "os" - - "github.com/mattn/go-isatty" - "golang.org/x/sys/windows" -) - -func enableVTMode(console windows.Handle) bool { - mode := uint32(0) - err := windows.GetConsoleMode(console, &mode) - if err != nil { - return false - } - - // EnableVirtualTerminalProcessing is the console mode to allow ANSI code - // interpretation on the console. See: - // https://docs.microsoft.com/en-us/windows/console/setconsolemode - // It only works on Windows 10. Earlier terminals will fail with an err which we will - // handle to say don't color - mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - err = windows.SetConsoleMode(console, mode) - return err == nil -} - -func init() { - if isatty.IsTerminal(os.Stdout.Fd()) { - CanColorStdout = enableVTMode(windows.Stdout) - } else { - CanColorStdout = isatty.IsCygwinTerminal(os.Stderr.Fd()) - } - - if isatty.IsTerminal(os.Stderr.Fd()) { - CanColorStderr = enableVTMode(windows.Stderr) - } else { - CanColorStderr = isatty.IsCygwinTerminal(os.Stderr.Fd()) - } -} diff --git a/modules/markup/external/external.go b/modules/markup/external/external.go index 122517ed11..59f0397515 100644 --- a/modules/markup/external/external.go +++ b/modules/markup/external/external.go @@ -9,7 +9,6 @@ import ( "io" "os" "os/exec" - "runtime" "strings" "code.gitea.io/gitea/modules/graceful" @@ -70,9 +69,6 @@ func (p *Renderer) DisplayInIFrame() bool { } func envMark(envName string) string { - if runtime.GOOS == "windows" { - return "%" + envName + "%" - } return "$" + envName } diff --git a/modules/process/manager_unix.go b/modules/process/manager_unix.go index c5be906b35..54dd6dc485 100644 --- a/modules/process/manager_unix.go +++ b/modules/process/manager_unix.go @@ -1,8 +1,6 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//go:build !windows - package process import ( diff --git a/modules/repository/hooks.go b/modules/repository/hooks.go index 95849789ab..75a21a09dd 100644 --- a/modules/repository/hooks.go +++ b/modules/repository/hooks.go @@ -7,7 +7,6 @@ import ( "fmt" "os" "path/filepath" - "runtime" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -146,10 +145,6 @@ func CreateDelegateHooks(repoPath string) (err error) { } func checkExecutable(filename string) bool { - // windows has no concept of a executable bit - if runtime.GOOS == "windows" { - return true - } fileInfo, err := os.Stat(filename) if err != nil { return false diff --git a/modules/setting/path.go b/modules/setting/path.go index 85d0e06302..b99f1977bb 100644 --- a/modules/setting/path.go +++ b/modules/setting/path.go @@ -34,11 +34,7 @@ var ( func getAppPath() (string, error) { var appPath string var err error - if IsWindows && filepath.IsAbs(os.Args[0]) { - appPath = filepath.Clean(os.Args[0]) - } else { - appPath, err = exec.LookPath(os.Args[0]) - } + appPath, err = exec.LookPath(os.Args[0]) if err != nil { if !errors.Is(err, exec.ErrDot) { return "", err diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 8350b914c5..487f2bb0d5 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -8,7 +8,6 @@ package setting import ( "fmt" "os" - "runtime" "strings" "time" @@ -34,7 +33,6 @@ var ( RunMode string RunUser string IsProd bool - IsWindows bool // IsInTesting indicates whether the testing is running. A lot of unreliable code causes a lot of nonsense error logs during testing // TODO: this is only a temporary solution, we should make the test code more reliable @@ -42,22 +40,18 @@ var ( ) func init() { - IsWindows = runtime.GOOS == "windows" if AppVer == "" { AppVer = "dev" } - // We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically // By default set this logger at Info - we'll change it later, but we need to start with something. log.SetConsoleLogger(log.DEFAULT, "console", log.INFO) } // IsRunUserMatchCurrentUser returns false if configured run user does not match // actual user that runs the app. The first return value is the actual user name. -// This check is ignored under Windows since SSH remote login is not the main -// method to login on Windows. func IsRunUserMatchCurrentUser(runUser string) (string, bool) { - if IsWindows || SSH.StartBuiltinServer { + if SSH.StartBuiltinServer { return "", true } diff --git a/modules/user/user.go b/modules/user/user.go index eee401a23f..d153413c70 100644 --- a/modules/user/user.go +++ b/modules/user/user.go @@ -6,8 +6,6 @@ package user import ( "os" "os/user" - "runtime" - "strings" ) // CurrentUsername return current login OS user name @@ -16,12 +14,7 @@ func CurrentUsername() string { if err != nil { return fallbackCurrentUsername() } - username := userinfo.Username - if runtime.GOOS == "windows" { - parts := strings.Split(username, "\\") - username = parts[len(parts)-1] - } - return username + return userinfo.Username } // Old method, used if new method doesn't work on your OS for some reason diff --git a/modules/user/user_test.go b/modules/user/user_test.go index 372a675d34..c7eff85c90 100644 --- a/modules/user/user_test.go +++ b/modules/user/user_test.go @@ -5,7 +5,6 @@ package user import ( "os/exec" - "runtime" "strings" "testing" ) @@ -23,10 +22,6 @@ func TestCurrentUsername(t *testing.T) { if len(user) == 0 { t.Errorf("expected non-empty user, got: %s", user) } - // Windows whoami is weird, so just skip remaining tests - if runtime.GOOS == "windows" { - t.Skip("skipped test because of weird whoami on Windows") - } whoami, err := getWhoamiOutput() if err != nil { t.Errorf("failed to run whoami to test current user: %f", err) diff --git a/modules/util/file_unix.go b/modules/util/file_unix.go index 79a29c8b3b..b722eee97d 100644 --- a/modules/util/file_unix.go +++ b/modules/util/file_unix.go @@ -1,8 +1,6 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//go:build !windows - package util import ( diff --git a/modules/util/file_unix_test.go b/modules/util/file_unix_test.go index d60082a034..228c64f980 100644 --- a/modules/util/file_unix_test.go +++ b/modules/util/file_unix_test.go @@ -1,8 +1,6 @@ // Copyright 2022 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//go:build !windows - package util import ( diff --git a/modules/util/path.go b/modules/util/path.go index 185e7cf882..9039f27cbf 100644 --- a/modules/util/path.go +++ b/modules/util/path.go @@ -10,8 +10,6 @@ import ( "os" "path" "path/filepath" - "regexp" - "runtime" "strings" ) @@ -78,11 +76,7 @@ func FilePathJoinAbs(base string, sub ...string) string { // POSIX filesystem can have `\` in file names. Windows: `\` and `/` are both used for path separators // to keep the behavior consistent, we do not allow `\` in file names, replace all `\` with `/` - if isOSWindows() { - elems[0] = filepath.Clean(base) - } else { - elems[0] = filepath.Clean(strings.ReplaceAll(base, "\\", pathSeparator)) - } + elems[0] = filepath.Clean(strings.ReplaceAll(base, "\\", pathSeparator)) if !filepath.IsAbs(elems[0]) { // This shouldn't happen. If there is really necessary to pass in relative path, return the full path with filepath.Abs() instead panic(fmt.Sprintf("FilePathJoinAbs: %q (for path %v) is not absolute, do not guess a relative path based on current working directory", elems[0], elems)) @@ -91,11 +85,7 @@ func FilePathJoinAbs(base string, sub ...string) string { if s == "" { continue } - if isOSWindows() { - elems = append(elems, filepath.Clean(pathSeparator+s)) - } else { - elems = append(elems, filepath.Clean(pathSeparator+strings.ReplaceAll(s, "\\", pathSeparator))) - } + elems = append(elems, filepath.Clean(pathSeparator+strings.ReplaceAll(s, "\\", pathSeparator))) } // the elems[0] must be an absolute path, just join them together return filepath.Join(elems...) @@ -217,12 +207,6 @@ func StatDir(rootPath string, includeDir ...bool) ([]string, error) { return statDir(rootPath, "", isIncludeDir, false, false) } -func isOSWindows() bool { - return runtime.GOOS == "windows" -} - -var driveLetterRegexp = regexp.MustCompile("/[A-Za-z]:/") - // FileURLToPath extracts the path information from a file://... url. // It returns an error only if the URL is not a file URL. func FileURLToPath(u *url.URL) (string, error) { @@ -230,17 +214,7 @@ func FileURLToPath(u *url.URL) (string, error) { return "", errors.New("URL scheme is not 'file': " + u.String()) } - path := u.Path - - if !isOSWindows() { - return path, nil - } - - // If it looks like there's a Windows drive letter at the beginning, strip off the leading slash. - if driveLetterRegexp.MatchString(path) { - return path[1:], nil - } - return path, nil + return u.Path, nil } // HomeDir returns path of '~'(in Linux) on Windows, @@ -249,14 +223,7 @@ func HomeDir() (home string, err error) { // TODO: some users run Gitea with mismatched uid and "HOME=xxx" (they set HOME=xxx by environment manually) // TODO: when running gitea as a sub command inside git, the HOME directory is not the user's home directory // so at the moment we can not use `user.Current().HomeDir` - if isOSWindows() { - home = os.Getenv("USERPROFILE") - if home == "" { - home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") - } - } else { - home = os.Getenv("HOME") - } + home = os.Getenv("HOME") if home == "" { return "", errors.New("cannot get home directory") diff --git a/modules/util/path_test.go b/modules/util/path_test.go index 3699f052d1..b912b76f6e 100644 --- a/modules/util/path_test.go +++ b/modules/util/path_test.go @@ -5,7 +5,6 @@ package util import ( "net/url" - "runtime" "testing" "github.com/stretchr/testify/assert" @@ -17,7 +16,6 @@ func TestFileURLToPath(t *testing.T) { url string expected string haserror bool - windows bool }{ // case 0 { @@ -34,18 +32,9 @@ func TestFileURLToPath(t *testing.T) { url: "file:///path", expected: "/path", }, - // case 3 - { - url: "file:///C:/path", - expected: "C:/path", - windows: true, - }, } for n, c := range cases { - if c.windows && runtime.GOOS != "windows" { - continue - } u, _ := url.Parse(c.url) p, err := FileURLToPath(u) if c.haserror { @@ -177,35 +166,18 @@ func TestCleanPath(t *testing.T) { assert.Equal(t, c.expected, PathJoinRelX(c.elems...), "case: %v", c.elems) } - // for POSIX only, but the result is similar on Windows, because the first element must be an absolute path - if isOSWindows() { - cases = []struct { - elems []string - expected string - }{ - {[]string{`C:\..`}, `C:\`}, - {[]string{`C:\a`}, `C:\a`}, - {[]string{`C:\a/`}, `C:\a`}, - {[]string{`C:\..\a\`, `../b`, `c\..`, `d`}, `C:\a\b\d`}, - {[]string{`C:\a/..\b`}, `C:\b`}, - {[]string{`C:\a`, ``, `b`}, `C:\a\b`}, - {[]string{`C:\a`, `..`, `b`}, `C:\a\b`}, - {[]string{`C:\lfs`, `repo/..`, `user/../path`}, `C:\lfs\path`}, - } - } else { - cases = []struct { - elems []string - expected string - }{ - {[]string{`/..`}, `/`}, - {[]string{`/a`}, `/a`}, - {[]string{`/a/`}, `/a`}, - {[]string{`/../a/`, `../b`, `c/..`, `d`}, `/a/b/d`}, - {[]string{`/a\..\b`}, `/b`}, - {[]string{`/a`, ``, `b`}, `/a/b`}, - {[]string{`/a`, `..`, `b`}, `/a/b`}, - {[]string{`/lfs`, `repo/..`, `user/../path`}, `/lfs/path`}, - } + cases = []struct { + elems []string + expected string + }{ + {[]string{`/..`}, `/`}, + {[]string{`/a`}, `/a`}, + {[]string{`/a/`}, `/a`}, + {[]string{`/../a/`, `../b`, `c/..`, `d`}, `/a/b/d`}, + {[]string{`/a\..\b`}, `/b`}, + {[]string{`/a`, ``, `b`}, `/a/b`}, + {[]string{`/a`, `..`, `b`}, `/a/b`}, + {[]string{`/lfs`, `repo/..`, `user/../path`}, `/lfs/path`}, } for _, c := range cases { assert.Equal(t, c.expected, FilePathJoinAbs(c.elems[0], c.elems[1:]...), "case: %v", c.elems) diff --git a/modules/util/remove.go b/modules/util/remove.go index d1e38faf5f..2a65a6b0aa 100644 --- a/modules/util/remove.go +++ b/modules/util/remove.go @@ -5,13 +5,10 @@ package util import ( "os" - "runtime" "syscall" "time" ) -const windowsSharingViolationError syscall.Errno = 32 - // Remove removes the named file or (empty) directory with at most 5 attempts. func Remove(name string) error { var err error @@ -27,12 +24,6 @@ func Remove(name string) error { continue } - if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { - // try again - <-time.After(100 * time.Millisecond) - continue - } - if unwrapped == syscall.ENOENT { // it's already gone return nil @@ -56,12 +47,6 @@ func RemoveAll(name string) error { continue } - if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { - // try again - <-time.After(100 * time.Millisecond) - continue - } - if unwrapped == syscall.ENOENT { // it's already gone return nil @@ -85,12 +70,6 @@ func Rename(oldpath, newpath string) error { continue } - if unwrapped == windowsSharingViolationError && runtime.GOOS == "windows" { - // try again - <-time.After(100 * time.Millisecond) - continue - } - if i == 0 && os.IsNotExist(err) { return err } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index af7142e7e5..8459d72f9e 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -609,9 +609,6 @@ CommitChoice = Commit choice TreeName = File path Content = Content -SSPISeparatorReplacement = Separator -SSPIDefaultLanguage = Default language - require_error = ` cannot be empty.` alpha_dash_error = ` should contain only alphanumeric, dash ("-") and underscore ("_") characters.` alpha_dash_dot_error = ` should contain only alphanumeric, dash ("-"), underscore ("_") and dot (".") characters.` @@ -3300,16 +3297,6 @@ auths.oauth2_admin_group = Group claim value for administrator users. (Optional auths.oauth2_restricted_group = Group claim value for restricted users. (Optional - requires claim name above) auths.oauth2_map_group_to_team = Map claimed groups to organization teams. (Optional - requires claim name above) auths.oauth2_map_group_to_team_removal = Remove users from synchronized teams if user does not belong to corresponding group. -auths.sspi_auto_create_users = Automatically create users -auths.sspi_auto_create_users_helper = Allow SSPI auth method to automatically create new accounts for users that login for the first time -auths.sspi_auto_activate_users = Automatically activate users -auths.sspi_auto_activate_users_helper = Allow SSPI auth method to automatically activate new users -auths.sspi_strip_domain_names = Remove domain names from usernames -auths.sspi_strip_domain_names_helper = If checked, domain names will be removed from logon names (eg. "DOMAIN\user" and "user@example.org" both will become just "user"). -auths.sspi_separator_replacement = Separator to use instead of \, / and @ -auths.sspi_separator_replacement_helper = The character to use to replace the separators of down-level logon names (eg. the \ in "DOMAIN\user") and user principal names (eg. the @ in "user@example.org"). -auths.sspi_default_language = Default user language -auths.sspi_default_language_helper = Default language for users automatically created by SSPI auth method. Leave empty if you prefer language to be automatically detected. auths.tips = Tips auths.tips.gmail_settings = Gmail settings: auths.tips.oauth2.general = OAuth2 authentication @@ -3965,4 +3952,4 @@ filepreview.lines = Lines %[1]d to %[2]d in %[3]s filepreview.truncated = Preview has been truncated [translation_meta] -test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :) \ No newline at end of file +test = This is a test string. It is not displayed in Forgejo UI but is used for testing purposes. Feel free to enter "ok" to save time (or a fun fact of your choice) to hit that sweet 100% completion mark :) diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go index e2ff004024..5e863cb9eb 100644 --- a/routers/api/shared/middleware.go +++ b/routers/api/shared/middleware.go @@ -6,8 +6,6 @@ package shared import ( "net/http" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/routers/common" @@ -51,10 +49,6 @@ func buildAuthGroup() *auth.Group { group.Add(&auth.ReverseProxy{}) } - if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) { - group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI - } - return group } diff --git a/routers/install/install.go b/routers/install/install.go index 24db25f459..86e342f1f9 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -29,7 +29,6 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/translation" - "code.gitea.io/gitea/modules/user" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/routers/common" @@ -119,15 +118,7 @@ func Install(ctx *context.Context) { form.AppSlogan = "Beyond coding. We Forge." form.RepoRootPath = setting.RepoRootPath form.LFSRootPath = setting.LFS.Storage.Path - - // Note(unknown): it's hard for Windows users change a running user, - // so just use current one if config says default. - if setting.IsWindows && setting.RunUser == "git" { - form.RunUser = user.CurrentUsername() - } else { - form.RunUser = setting.RunUser - } - + form.RunUser = setting.RunUser form.Domain = setting.Domain form.SSHPort = setting.SSH.Port form.HTTPPort = setting.HTTPPort diff --git a/routers/private/manager_unix.go b/routers/private/manager_unix.go index 0c63ebc918..311bfe6858 100644 --- a/routers/private/manager_unix.go +++ b/routers/private/manager_unix.go @@ -1,8 +1,6 @@ // Copyright 2020 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -//go:build !windows - package private import ( diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index dcdc8e6a2a..8af14f6d52 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -4,11 +4,9 @@ package admin import ( - "errors" "fmt" "net/http" "net/url" - "regexp" "strconv" "strings" @@ -18,14 +16,12 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/ldap" "code.gitea.io/gitea/services/auth/source/oauth2" pam_service "code.gitea.io/gitea/services/auth/source/pam" "code.gitea.io/gitea/services/auth/source/smtp" - "code.gitea.io/gitea/services/auth/source/sspi" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" @@ -38,11 +34,6 @@ const ( tplAuthEdit base.TplName = "admin/auth/edit" ) -var ( - separatorAntiPattern = regexp.MustCompile(`[^\w-\.]`) - langCodePattern = regexp.MustCompile(`^[a-z]{2}-[A-Z]{2}$`) -) - // Authentications show authentication config page func Authentications(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.authentication") @@ -70,7 +61,6 @@ var ( {auth.DLDAP.String(), auth.DLDAP}, {auth.SMTP.String(), auth.SMTP}, {auth.OAuth2.String(), auth.OAuth2}, - {auth.SSPI.String(), auth.SSPI}, } if pam.Supported { items = append(items, dropdownItem{auth.Names[auth.PAM], auth.PAM}) @@ -102,12 +92,6 @@ func NewAuthSource(ctx *context.Context) { oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers - ctx.Data["SSPIAutoCreateUsers"] = true - ctx.Data["SSPIAutoActivateUsers"] = true - ctx.Data["SSPIStripDomainNames"] = true - ctx.Data["SSPISeparatorReplacement"] = "_" - ctx.Data["SSPIDefaultLanguage"] = "" - // only the first as default ctx.Data["oauth2_provider"] = oauth2providers[0].Name() @@ -209,30 +193,6 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source { } } -func parseSSPIConfig(ctx *context.Context, form forms.AuthenticationForm) (*sspi.Source, error) { - if util.IsEmptyString(form.SSPISeparatorReplacement) { - ctx.Data["Err_SSPISeparatorReplacement"] = true - return nil, errors.New(ctx.Locale.TrString("form.SSPISeparatorReplacement") + ctx.Locale.TrString("form.require_error")) - } - if separatorAntiPattern.MatchString(form.SSPISeparatorReplacement) { - ctx.Data["Err_SSPISeparatorReplacement"] = true - return nil, errors.New(ctx.Locale.TrString("form.SSPISeparatorReplacement") + ctx.Locale.TrString("form.alpha_dash_dot_error")) - } - - if form.SSPIDefaultLanguage != "" && !langCodePattern.MatchString(form.SSPIDefaultLanguage) { - ctx.Data["Err_SSPIDefaultLanguage"] = true - return nil, errors.New(ctx.Locale.TrString("form.lang_select_error")) - } - - return &sspi.Source{ - AutoCreateUsers: form.SSPIAutoCreateUsers, - AutoActivateUsers: form.SSPIAutoActivateUsers, - StripDomainNames: form.SSPIStripDomainNames, - SeparatorReplacement: form.SSPISeparatorReplacement, - DefaultLanguage: form.SSPIDefaultLanguage, - }, nil -} - // NewAuthSourcePost response for adding an auth source func NewAuthSourcePost(ctx *context.Context) { form := *web.GetForm(ctx).(*forms.AuthenticationForm) @@ -247,12 +207,6 @@ func NewAuthSourcePost(ctx *context.Context) { oauth2providers := oauth2.GetSupportedOAuth2Providers() ctx.Data["OAuth2Providers"] = oauth2providers - ctx.Data["SSPIAutoCreateUsers"] = true - ctx.Data["SSPIAutoActivateUsers"] = true - ctx.Data["SSPIStripDomainNames"] = true - ctx.Data["SSPISeparatorReplacement"] = "_" - ctx.Data["SSPIDefaultLanguage"] = "" - hasTLS := false var config convert.Conversion switch auth.Type(form.Type) { @@ -279,19 +233,6 @@ func NewAuthSourcePost(ctx *context.Context) { return } } - case auth.SSPI: - var err error - config, err = parseSSPIConfig(ctx, form) - if err != nil { - ctx.RenderWithErr(err.Error(), tplAuthNew, form) - return - } - existing, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{LoginType: auth.SSPI}) - if err != nil || len(existing) > 0 { - ctx.Data["Err_Type"] = true - ctx.RenderWithErr(ctx.Tr("admin.auths.login_source_of_type_exist"), tplAuthNew, form) - return - } default: ctx.Error(http.StatusBadRequest) return @@ -408,12 +349,6 @@ func EditAuthSourcePost(ctx *context.Context) { return } } - case auth.SSPI: - config, err = parseSSPIConfig(ctx, form) - if err != nil { - ctx.RenderWithErr(err.Error(), tplAuthEdit, form) - return - } default: ctx.Error(http.StatusBadRequest) return diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index d592dff077..1d00c97b6e 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -164,7 +164,6 @@ func SignIn(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsLogin"] = true - ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) ctx.Data["EnableInternalSignIn"] = setting.Service.EnableInternalSignIn if setting.Service.EnableCaptcha && setting.Service.RequireCaptchaForLogin { @@ -190,7 +189,6 @@ func SignInPost(ctx *context.Context) { ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" ctx.Data["PageIsSignIn"] = true ctx.Data["PageIsLogin"] = true - ctx.Data["EnableSSPI"] = auth.IsSSPIEnabled(ctx) ctx.Data["EnableInternalSignIn"] = setting.Service.EnableInternalSignIn ctx.Data["DisablePassword"] = !setting.Service.EnableInternalSignIn diff --git a/routers/web/web.go b/routers/web/web.go index a519ff74c1..128d072741 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -8,8 +8,6 @@ import ( "net/http" "strings" - auth_model "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" quota_model "code.gitea.io/gitea/models/quota" "code.gitea.io/gitea/models/unit" @@ -110,10 +108,6 @@ func buildAuthGroup() *auth_service.Group { } group.Add(&auth_service.Session{}) - if setting.IsWindows && auth_model.IsSSPIEnabled(db.DefaultContext) { - group.Add(&auth_service.SSPI{}) // it MUST be the last, see the comment of SSPI - } - return group } diff --git a/services/auth/signin.go b/services/auth/signin.go index e116a088e0..7c69da8f94 100644 --- a/services/auth/signin.go +++ b/services/auth/signin.go @@ -18,7 +18,6 @@ import ( _ "code.gitea.io/gitea/services/auth/source/db" // register the sources (and below) _ "code.gitea.io/gitea/services/auth/source/ldap" // register the ldap source _ "code.gitea.io/gitea/services/auth/source/pam" // register the pam source - _ "code.gitea.io/gitea/services/auth/source/sspi" // register the sspi source ) // UserSignIn validates user name and password. diff --git a/services/auth/source/sspi/assert_interface_test.go b/services/auth/source/sspi/assert_interface_test.go deleted file mode 100644 index 03d836dd6f..0000000000 --- a/services/auth/source/sspi/assert_interface_test.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package sspi_test - -import ( - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/services/auth/source/sspi" -) - -// This test file exists to assert that our Source exposes the interfaces that we expect -// It tightly binds the interfaces and implementation without breaking go import cycles - -type sourceInterface interface { - auth.Config -} - -var _ (sourceInterface) = &sspi.Source{} diff --git a/services/auth/source/sspi/source.go b/services/auth/source/sspi/source.go deleted file mode 100644 index bdd6ef451c..0000000000 --- a/services/auth/source/sspi/source.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2021 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package sspi - -import ( - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/modules/json" -) - -// _________ ___________________.___ -// / _____// _____/\______ \ | -// \_____ \ \_____ \ | ___/ | -// / \/ \ | | | | -// /_______ /_______ / |____| |___| -// \/ \/ - -// Source holds configuration for SSPI single sign-on. -type Source struct { - AutoCreateUsers bool - AutoActivateUsers bool - StripDomainNames bool - SeparatorReplacement string - DefaultLanguage string -} - -// FromDB fills up an SSPIConfig from serialized format. -func (cfg *Source) FromDB(bs []byte) error { - return json.UnmarshalHandleDoubleEncode(bs, &cfg) -} - -// ToDB exports an SSPIConfig to a serialized format. -func (cfg *Source) ToDB() ([]byte, error) { - return json.Marshal(cfg) -} - -func init() { - auth.RegisterTypeConfig(auth.SSPI, &Source{}) -} diff --git a/services/auth/sspi.go b/services/auth/sspi.go deleted file mode 100644 index 64a127e97a..0000000000 --- a/services/auth/sspi.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2019 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package auth - -import ( - "context" - "errors" - "net/http" - "strings" - "sync" - - "code.gitea.io/gitea/models/auth" - "code.gitea.io/gitea/models/db" - user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/services/auth/source/sspi" - gitea_context "code.gitea.io/gitea/services/context" - - gouuid "github.com/google/uuid" -) - -const ( - tplSignIn base.TplName = "user/auth/signin" -) - -type SSPIAuth interface { - AppendAuthenticateHeader(w http.ResponseWriter, data string) - Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) -} - -var ( - sspiAuth SSPIAuth // a global instance of the websspi authenticator to avoid acquiring the server credential handle on every request - sspiAuthOnce sync.Once - sspiAuthErrInit error - - // Ensure the struct implements the interface. - _ Method = &SSPI{} -) - -// SSPI implements the SingleSignOn interface and authenticates requests -// via the built-in SSPI module in Windows for SPNEGO authentication. -// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation -// fails (or if negotiation should continue), which would prevent other authentication methods -// to execute at all. -type SSPI struct{} - -// Name represents the name of auth method -func (s *SSPI) Name() string { - return "sspi" -} - -// Verify uses SSPI (Windows implementation of SPNEGO) to authenticate the request. -// If authentication is successful, returns the corresponding user object. -// If negotiation should continue or authentication fails, immediately returns a 401 HTTP -// response code, as required by the SPNEGO protocol. -func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { - sspiAuthOnce.Do(func() { sspiAuthErrInit = sspiAuthInit() }) - if sspiAuthErrInit != nil { - return nil, sspiAuthErrInit - } - if !s.shouldAuthenticate(req) { - return nil, nil - } - - cfg, err := s.getConfig(req.Context()) - if err != nil { - log.Error("could not get SSPI config: %v", err) - return nil, err - } - - log.Trace("SSPI Authorization: Attempting to authenticate") - userInfo, outToken, err := sspiAuth.Authenticate(req, w) - if err != nil { - log.Warn("Authentication failed with error: %v\n", err) - sspiAuth.AppendAuthenticateHeader(w, outToken) - - // Include the user login page in the 401 response to allow the user - // to login with another authentication method if SSPI authentication - // fails - store.GetData()["Flash"] = map[string]string{ - "ErrorMsg": err.Error(), - } - store.GetData()["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn - store.GetData()["EnableSSPI"] = true - // in this case, the Verify function is called in Gitea's web context - // FIXME: it doesn't look good to render the page here, why not redirect? - gitea_context.GetWebContext(req).HTML(http.StatusUnauthorized, tplSignIn) - return nil, err - } - if outToken != "" { - sspiAuth.AppendAuthenticateHeader(w, outToken) - } - - username := sanitizeUsername(userInfo.Username, cfg) - if len(username) == 0 { - return nil, nil - } - log.Info("Authenticated as %s\n", username) - - user, err := user_model.GetUserByName(req.Context(), username) - if err != nil { - if !user_model.IsErrUserNotExist(err) { - log.Error("GetUserByName: %v", err) - return nil, err - } - if !cfg.AutoCreateUsers { - log.Error("User '%s' not found", username) - return nil, nil - } - user, err = s.newUser(req.Context(), username, cfg) - if err != nil { - log.Error("CreateUser: %v", err) - return nil, err - } - } - - // Make sure requests to API paths and PWA resources do not create a new session - if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) { - handleSignIn(w, req, sess, user) - } - - log.Trace("SSPI Authorization: Logged in user %-v", user) - return user, nil -} - -// getConfig retrieves the SSPI configuration from login sources -func (s *SSPI) getConfig(ctx context.Context) (*sspi.Source, error) { - sources, err := db.Find[auth.Source](ctx, auth.FindSourcesOptions{ - IsActive: optional.Some(true), - LoginType: auth.SSPI, - }) - if err != nil { - return nil, err - } - if len(sources) == 0 { - return nil, errors.New("no active login sources of type SSPI found") - } - if len(sources) > 1 { - return nil, errors.New("more than one active login source of type SSPI found") - } - return sources[0].Cfg.(*sspi.Source), nil -} - -func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) { - shouldAuth = false - path := strings.TrimSuffix(req.URL.Path, "/") - if path == "/user/login" { - if req.FormValue("user_name") != "" && req.FormValue("password") != "" { - shouldAuth = false - } else if req.FormValue("auth_with_sspi") == "1" { - shouldAuth = true - } - } else if middleware.IsAPIPath(req) || isAttachmentDownload(req) { - shouldAuth = true - } - return shouldAuth -} - -// newUser creates a new user object for the purpose of automatic registration -// and populates its name and email with the information present in request headers. -func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) (*user_model.User, error) { - email := gouuid.New().String() + "@localhost.localdomain" - user := &user_model.User{ - Name: username, - Email: email, - Language: cfg.DefaultLanguage, - } - emailNotificationPreference := user_model.EmailNotificationsDisabled - overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: optional.Some(cfg.AutoActivateUsers), - KeepEmailPrivate: optional.Some(true), - EmailNotificationsPreference: &emailNotificationPreference, - } - if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { - return nil, err - } - - return user, nil -} - -// stripDomainNames removes NETBIOS domain name and separator from down-level logon names -// (eg. "DOMAIN\user" becomes "user"), and removes the UPN suffix (domain name) and separator -// from UPNs (eg. "user@domain.local" becomes "user") -func stripDomainNames(username string) string { - if strings.Contains(username, "\\") { - parts := strings.SplitN(username, "\\", 2) - if len(parts) > 1 { - username = parts[1] - } - } else if strings.Contains(username, "@") { - parts := strings.Split(username, "@") - if len(parts) > 1 { - username = parts[0] - } - } - return username -} - -func replaceSeparators(username string, cfg *sspi.Source) string { - newSep := cfg.SeparatorReplacement - username = strings.ReplaceAll(username, "\\", newSep) - username = strings.ReplaceAll(username, "/", newSep) - username = strings.ReplaceAll(username, "@", newSep) - return username -} - -func sanitizeUsername(username string, cfg *sspi.Source) string { - if len(username) == 0 { - return "" - } - if cfg.StripDomainNames { - username = stripDomainNames(username) - } - // Replace separators even if we have already stripped the domain name part, - // as the username can contain several separators: eg. "MICROSOFT\useremail@live.com" - username = replaceSeparators(username, cfg) - return username -} diff --git a/services/auth/sspiauth_posix.go b/services/auth/sspiauth_posix.go deleted file mode 100644 index 49b0ed4a52..0000000000 --- a/services/auth/sspiauth_posix.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2023 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build !windows - -package auth - -import ( - "errors" - "net/http" -) - -type SSPIUserInfo struct { - Username string // Name of user, usually in the form DOMAIN\User - Groups []string // The global groups the user is a member of -} - -type sspiAuthMock struct{} - -func (s sspiAuthMock) AppendAuthenticateHeader(w http.ResponseWriter, data string) { -} - -func (s sspiAuthMock) Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) { - return nil, "", errors.New("not implemented") -} - -func sspiAuthInit() error { - sspiAuth = &sspiAuthMock{} // TODO: we can mock the SSPI auth in tests - return nil -} diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go index 39aae51756..21443ff6a5 100644 --- a/services/forms/auth_form.go +++ b/services/forms/auth_form.go @@ -77,11 +77,6 @@ type AuthenticationForm struct { Oauth2GroupTeamMapRemoval bool Oauth2AttributeSSHPublicKey string SkipLocalTwoFA bool - SSPIAutoCreateUsers bool - SSPIAutoActivateUsers bool - SSPIStripDomainNames bool - SSPISeparatorReplacement string `binding:"AlphaDashDot;MaxSize(5)"` - SSPIDefaultLanguage string GroupTeamMap string `binding:"ValidGroupTeamMap"` GroupTeamMapRemoval bool } diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 34d52ed224..1ca5573cae 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -380,51 +380,6 @@
{{end}} - - {{if .Source.IsSSPI}} - {{$cfg:=.Source.Cfg}} -
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users_helper"}}

-
-
-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_auto_activate_users_helper"}}

-
-
-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_strip_domain_names_helper"}}

-
-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_separator_replacement_helper"}}

-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_default_language_helper"}}

-
- {{end}} {{if .Source.IsLDAP}}
diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index c70bd3ba43..12d3798278 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -50,9 +50,6 @@ {{template "admin/auth/source/oauth" .}} - - {{template "admin/auth/source/sspi" .}} -
diff --git a/templates/admin/auth/source/sspi.tmpl b/templates/admin/auth/source/sspi.tmpl deleted file mode 100644 index 6a3f00f9a8..0000000000 --- a/templates/admin/auth/source/sspi.tmpl +++ /dev/null @@ -1,43 +0,0 @@ -
-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users_helper"}}

-
-
-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_auto_activate_users_helper"}}

-
-
-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_strip_domain_names_helper"}}

-
-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_separator_replacement_helper"}}

-
-
- - -

{{ctx.Locale.Tr "admin.auths.sspi_default_language_helper"}}

-
-
diff --git a/templates/user/auth/oauth_container.tmpl b/templates/user/auth/oauth_container.tmpl index c20273be14..7531320394 100644 --- a/templates/user/auth/oauth_container.tmpl +++ b/templates/user/auth/oauth_container.tmpl @@ -19,12 +19,6 @@ {{ctx.Locale.Tr "auth.sign_in_openid"}} {{end}} - {{if .EnableSSPI}} - - {{svg "fontawesome-windows"}} -  SSPI - - {{end}}
diff --git a/tests/test_utils.go b/tests/test_utils.go index 106e74dc89..c4d87ca619 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -66,9 +66,6 @@ func InitTest(requireGitea bool) { setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom") if requireGitea { giteaBinary := "gitea" - if setting.IsWindows { - giteaBinary += ".exe" - } setting.AppPath = path.Join(giteaRoot, giteaBinary) if _, err := os.Stat(setting.AppPath); err != nil { exitf("Could not find gitea binary at %s", setting.AppPath) diff --git a/web_src/js/features/admin/common.js b/web_src/js/features/admin/common.js index a42d8261f1..9934c3dd17 100644 --- a/web_src/js/features/admin/common.js +++ b/web_src/js/features/admin/common.js @@ -123,9 +123,9 @@ export function initAdminCommon() { // New authentication if (document.querySelector('.admin.new.authentication')) { document.getElementById('auth_type')?.addEventListener('change', function () { - hideElem('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi'); + hideElem('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size'); - for (const input of document.querySelectorAll('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]')) { + for (const input of document.querySelectorAll('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required]')) { input.removeAttribute('required'); } @@ -166,12 +166,6 @@ export function initAdminCommon() { } onOAuth2Change(true); break; - case '7': // SSPI - showElem('.sspi'); - for (const input of document.querySelectorAll('.sspi div.required input')) { - input.setAttribute('required', 'required'); - } - break; } if (authType === '2' || authType === '5') { onSecurityProtocolChange(); From 89c3888b5e998f689583292537f0f93e0a4f9a3a Mon Sep 17 00:00:00 2001 From: Dmitrii Sharshakov Date: Sat, 8 Mar 2025 10:42:36 +0000 Subject: [PATCH 251/669] feat(auth): add ability to regenerate access tokens (#6963) - Add the ability to regenerate existing access tokens in the UI. This preserves the ID of the access token, but generates a new salt and token contents. - Integration test added. - Unit test added. - Resolves #6880 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6963 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Reviewed-by: Gusted Co-authored-by: Dmitrii Sharshakov Co-committed-by: Dmitrii Sharshakov --- models/auth/access_token.go | 34 +++++++++++- models/auth/access_token_test.go | 25 +++++++++ options/locale/locale_en-US.ini | 4 ++ routers/web/user/setting/applications.go | 18 +++++++ routers/web/web.go | 1 + templates/user/settings/applications.tmpl | 15 ++++++ tests/integration/integration_test.go | 22 ++++++-- tests/integration/user_test.go | 64 +++++++++++++++++++++++ 8 files changed, 176 insertions(+), 7 deletions(-) diff --git a/models/auth/access_token.go b/models/auth/access_token.go index 63331b4841..3ac18940a8 100644 --- a/models/auth/access_token.go +++ b/models/auth/access_token.go @@ -98,6 +98,15 @@ func init() { // NewAccessToken creates new access token. func NewAccessToken(ctx context.Context, t *AccessToken) error { + err := generateAccessToken(t) + if err != nil { + return err + } + _, err = db.GetEngine(ctx).Insert(t) + return err +} + +func generateAccessToken(t *AccessToken) error { salt, err := util.CryptoRandomString(10) if err != nil { return err @@ -110,8 +119,7 @@ func NewAccessToken(ctx context.Context, t *AccessToken) error { t.Token = hex.EncodeToString(token) t.TokenHash = HashToken(t.Token, t.TokenSalt) t.TokenLastEight = t.Token[len(t.Token)-8:] - _, err = db.GetEngine(ctx).Insert(t) - return err + return nil } // DisplayPublicOnly whether to display this as a public-only token. @@ -234,3 +242,25 @@ func DeleteAccessTokenByID(ctx context.Context, id, userID int64) error { } return nil } + +// RegenerateAccessTokenByID regenerates access token by given ID. +// It regenerates token and salt, as well as updates the creation time. +func RegenerateAccessTokenByID(ctx context.Context, id, userID int64) (*AccessToken, error) { + t := &AccessToken{} + found, err := db.GetEngine(ctx).Where("id = ? AND uid = ?", id, userID).Get(t) + if err != nil { + return nil, err + } else if !found { + return nil, ErrAccessTokenNotExist{} + } + + err = generateAccessToken(t) + if err != nil { + return nil, err + } + + // Reset the creation time, token is unused + t.UpdatedUnix = timeutil.TimeStampNow() + + return t, UpdateAccessToken(ctx, t) +} diff --git a/models/auth/access_token_test.go b/models/auth/access_token_test.go index e6ea4876e5..976ff37493 100644 --- a/models/auth/access_token_test.go +++ b/models/auth/access_token_test.go @@ -131,3 +131,28 @@ func TestDeleteAccessTokenByID(t *testing.T) { require.Error(t, err) assert.True(t, auth_model.IsErrAccessTokenNotExist(err)) } + +func TestRegenerateAccessTokenByID(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + token, err := auth_model.GetAccessTokenBySHA(db.DefaultContext, "4c6f36e6cf498e2a448662f915d932c09c5a146c") + require.NoError(t, err) + + newToken, err := auth_model.RegenerateAccessTokenByID(db.DefaultContext, token.ID, 1) + require.NoError(t, err) + unittest.AssertNotExistsBean(t, &auth_model.AccessToken{ID: token.ID, UID: token.UID, TokenHash: token.TokenHash}) + newToken = &auth_model.AccessToken{ + ID: newToken.ID, + UID: newToken.UID, + TokenHash: newToken.TokenHash, + } + unittest.AssertExistsAndLoadBean(t, newToken) + + // Token has been recreated, new salt and hash, but should retain the same ID, UID, Name and Scope + assert.Equal(t, token.ID, newToken.ID) + assert.NotEqual(t, token.TokenHash, newToken.TokenHash) + assert.NotEqual(t, token.TokenSalt, newToken.TokenSalt) + assert.Equal(t, token.UID, newToken.UID) + assert.Equal(t, token.Name, newToken.Name) + assert.Equal(t, token.Scope, newToken.Scope) +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8459d72f9e..0cf79445d3 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -943,6 +943,10 @@ delete_token = Delete access_token_deletion = Delete access token access_token_deletion_desc = Deleting a token will revoke access to your account for applications using it. This cannot be undone. Continue? delete_token_success = The token has been deleted. Applications using it no longer have access to your account. +regenerate_token = Regenerate +access_token_regeneration = Regenerate access token +access_token_regeneration_desc = Regenerating a token will revoke access to your account for applications using it. This cannot be undone. Continue? +regenerate_token_success = The token has been regenerated. Applications that use it no longer have access to your account and must be updated with the new token. repo_and_org_access = Repository and Organization Access permissions_public_only = Public only permissions_access_all = All (public, private, and limited) diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index 24ebf9b922..4dfd859a44 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -10,6 +10,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/context" @@ -87,6 +88,23 @@ func DeleteApplication(ctx *context.Context) { ctx.JSONRedirect(setting.AppSubURL + "/user/settings/applications") } +// RegenerateApplication response for regenerating user access token +func RegenerateApplication(ctx *context.Context) { + if t, err := auth_model.RegenerateAccessTokenByID(ctx, ctx.FormInt64("id"), ctx.Doer.ID); err != nil { + if auth_model.IsErrAccessTokenNotExist(err) { + ctx.Flash.Error(ctx.Tr("error.not_found")) + } else { + ctx.Flash.Error(ctx.Tr("error.server_internal")) + log.Error("DeleteAccessTokenByID", err) + } + } else { + ctx.Flash.Success(ctx.Tr("settings.regenerate_token_success")) + ctx.Flash.Info(t.Token) + } + + ctx.JSONRedirect(setting.AppSubURL + "/user/settings/applications") +} + func loadApplicationsData(ctx *context.Context) { ctx.Data["AccessTokenScopePublicOnly"] = auth_model.AccessTokenScopePublicOnly tokens, err := db.Find[auth_model.AccessToken](ctx, auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID}) diff --git a/routers/web/web.go b/routers/web/web.go index 128d072741..9706319b3b 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -586,6 +586,7 @@ func registerRoutes(m *web.Route) { m.Combo("").Get(user_setting.Applications). Post(web.Bind(forms.NewAccessTokenForm{}), user_setting.ApplicationsPost) m.Post("/delete", user_setting.DeleteApplication) + m.Post("/regenerate", user_setting.RegenerateApplication) }) m.Combo("/keys").Get(user_setting.Keys). diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 2aeabc6903..38ede95a77 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -40,6 +40,10 @@
+
+ + diff --git a/templates/base/modal_actions_confirm.tmpl b/templates/base/modal_actions_confirm.tmpl index c44320deff..8c4e346088 100644 --- a/templates/base/modal_actions_confirm.tmpl +++ b/templates/base/modal_actions_confirm.tmpl @@ -1,7 +1,6 @@ {{/* Two buttons (negative, positive): * ModalButtonTypes: "yes" (default) or "confirm" -* ModalButtonColors: "primary" (default) / "blue" / "yellow" * ModalButtonCancelText * ModalButtonOkText @@ -23,13 +22,7 @@ The ".ok.button" and ".cancel.button" selectors are also used by Fomantic Modal {{if .ModalButtonCancelText}}{{$textNegitive = .ModalButtonCancelText}}{{end}} {{if .ModalButtonOkText}}{{$textPositive = .ModalButtonOkText}}{{end}} - {{$stylePositive := "primary"}} - {{if eq .ModalButtonColors "blue"}} - {{$stylePositive = "blue"}} - {{else if eq .ModalButtonColors "yellow"}} - {{$stylePositive = "yellow"}} - {{end}} - + {{end}} diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl index 5cd36721a7..5b94afc4f1 100644 --- a/templates/devtest/fomantic-modal.tmpl +++ b/templates/devtest/fomantic-modal.tmpl @@ -54,18 +54,6 @@ {{template "base/modal_actions_confirm" (dict "ModalButtonTypes" "confirm")}} - - - - diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 38ede95a77..9a806d0665 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -122,7 +122,7 @@

{{ctx.Locale.Tr "settings.access_token_deletion_desc"}}

- {{template "base/modal_actions_confirm" (dict "ModalButtonColors" "primary")}} + {{template "base/modal_actions_confirm"}} {{template "user/settings/layout_footer" .}} From 4150abe9d329a38ba91480d46ad79060b1abf376 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 9 Mar 2025 15:05:24 +0000 Subject: [PATCH 258/669] fix(ui): use usual and consistent size for project icons of 16 (#7175) 18px is not a size used for icons often. In these cases it was also inconsistent with most of the UI. Affected areas: * `filter_list` - dropdown, filtering repo issues by project * `filter_actions` - dropdown, mass-editing issues via issue list * `sidebar/projects` - dropdown in issue view * `new_form` - dropdown when creating issue. Projects must be enabled in repo So all areas are dropdowns. I think these were inconsistent because other dropdowns like Reviewers and Milestones use the usual icons of 16px. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7175 Reviewed-by: Gusted Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- templates/repo/issue/filter_actions.tmpl | 4 ++-- templates/repo/issue/filter_list.tmpl | 4 ++-- templates/repo/issue/new_form.tmpl | 6 +++--- templates/repo/issue/view_content/sidebar/projects.tmpl | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/templates/repo/issue/filter_actions.tmpl b/templates/repo/issue/filter_actions.tmpl index 58b1ef8ecd..60237f225d 100644 --- a/templates/repo/issue/filter_actions.tmpl +++ b/templates/repo/issue/filter_actions.tmpl @@ -85,7 +85,7 @@ {{range .OpenProjects}}
- {{svg .IconName 18 "tw-mr-2"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2"}}{{.Title}}
{{end}} {{end}} @@ -96,7 +96,7 @@ {{range .ClosedProjects}}
- {{svg .IconName 18 "tw-mr-2"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2"}}{{.Title}}
{{end}} {{end}} diff --git a/templates/repo/issue/filter_list.tmpl b/templates/repo/issue/filter_list.tmpl index f59c6d67ce..ae50ac4c46 100644 --- a/templates/repo/issue/filter_list.tmpl +++ b/templates/repo/issue/filter_list.tmpl @@ -60,7 +60,7 @@ {{range .OpenProjects}} - {{svg .IconName 18 "tw-mr-2 tw-shrink-0"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2 tw-shrink-0"}}{{.Title}} {{end}} {{end}} @@ -71,7 +71,7 @@ {{range .ClosedProjects}} - {{svg .IconName 18 "tw-mr-2"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2"}}{{.Title}} {{end}} {{end}} diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl index c2cf4ee7a7..2c7807206e 100644 --- a/templates/repo/issue/new_form.tmpl +++ b/templates/repo/issue/new_form.tmpl @@ -110,7 +110,7 @@ {{range .OpenProjects}} - {{svg .IconName 18 "tw-mr-2"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2"}}{{.Title}} {{end}} {{end}} @@ -121,7 +121,7 @@ {{range .ClosedProjects}} - {{svg .IconName 18 "tw-mr-2"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2"}}{{.Title}} {{end}} {{end}} @@ -133,7 +133,7 @@ diff --git a/templates/repo/issue/view_content/sidebar/projects.tmpl b/templates/repo/issue/view_content/sidebar/projects.tmpl index 91d75f3bd9..3fd58b3340 100644 --- a/templates/repo/issue/view_content/sidebar/projects.tmpl +++ b/templates/repo/issue/view_content/sidebar/projects.tmpl @@ -25,7 +25,7 @@ {{range .OpenProjects}} - {{svg .IconName 18 "tw-mr-2"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2"}}{{.Title}} {{end}} {{end}} @@ -36,7 +36,7 @@ {{range .ClosedProjects}} - {{svg .IconName 18 "tw-mr-2"}}{{.Title}} + {{svg .IconName 16 "tw-mr-2"}}{{.Title}} {{end}} {{end}} @@ -47,7 +47,7 @@ From ee23f44833f144004e7b4e84fcb44fec5659615c Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 9 Mar 2025 15:05:34 +0000 Subject: [PATCH 259/669] chore(ui): improve svg icon margin consistency (#7172) * followup to https://codeberg.org/forgejo/forgejo/pulls/7031#issuecomment-2868277: no visible changes * followup to https://codeberg.org/forgejo/forgejo/pulls/6963: apply a small margin to the icon Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7172 Reviewed-by: Gusted Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-committed-by: 0ko <0ko@noreply.codeberg.org> --- templates/base/head_navbar.tmpl | 8 ++++---- templates/user/settings/applications.tmpl | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 0825dbf7bc..0c13f9e844 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -194,13 +194,13 @@ {{else}} {{if .ShowRegistrationButton}} - {{svg "octicon-person"}} - {{ctx.Locale.Tr "register"}} + {{svg "octicon-person" 16 "tw-mr-1"}} + {{ctx.Locale.Tr "register"}} {{end}} - {{svg "octicon-sign-in"}} - {{ctx.Locale.Tr "sign_in"}} + {{svg "octicon-sign-in" 16 "tw-mr-1"}} + {{ctx.Locale.Tr "sign_in"}} {{end}} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 9a806d0665..a5912c9e0f 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -105,7 +105,7 @@