From 1d838c8d5ec76fcea4dbdc61ac74bfb89ffb4d11 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Mon, 7 Jul 2025 20:23:20 +0200 Subject: [PATCH 01/80] [v12.0/forgejo] fix: corrupted wiki unit default permission (#8234 follow-up) (#8439) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8258 Closes #8119, follow-up of #8234 #8234 "only" fixed the case when a new wiki setting was applied. However it lacked 2 aspects: - fixing the already corrupted unit permission in the database (required a migration) - fixing the API route Both aspects should now be covered. Additionally, I commented out the unused `UnitAccessMode` and indicated that they are only used for the wiki-unit (hopefully saving some time to future code-readers). ### Testing - go to a commit before #8234 (e.g. 285f66b782491d05e35aa81a6e1a0fede85069b4) - create 3 repositories - save the wiki settings of the second, without any change - set the wiki of the third to be globally writable - verify the `default_permissions` column in the database, of the unit `5`: 0, 2, 3 - stop Forgejo, switch to this PR and start Forgejo - verify that the logs writes `Migration[35]: Fix wiki unit default permission` - verify the `default_permissions` column in the database, of the unit `5`: 0, 0, 3 Before: ![2025-06-23-150055.png](/attachments/1eb92507-b8b4-422c-921c-4296a91172e7) After: ![2025-06-23-150853.png](/attachments/c7b53397-54fe-487d-89da-e10b26538cde) - [x] I did not document these changes and I do not expect someone else to do it. - [x] I do not want this change to show in the release notes. Co-authored-by: oliverpool Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8439 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/forgejo_migrations/migrate.go | 2 + models/forgejo_migrations/v36.go | 55 ++++++++++++++++++++++++++++ models/repo/repo_unit.go | 17 +++++---- models/repo/repo_unit_test.go | 4 +- routers/api/v1/repo/repo.go | 2 +- 5 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 models/forgejo_migrations/v36.go diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index bc64f07013..94469b7371 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -109,6 +109,8 @@ var migrations = []*Migration{ NewMigration("Add `notify-email` column to `action_run` table", AddNotifyEmailToActionRun), // v34 -> v35 NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped), + // v35 -> v36 + NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v36.go b/models/forgejo_migrations/v36.go new file mode 100644 index 0000000000..019c177a08 --- /dev/null +++ b/models/forgejo_migrations/v36.go @@ -0,0 +1,55 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package forgejo_migrations //nolint:revive + +import ( + "xorm.io/xorm" +) + +func FixWikiUnitDefaultPermission(x *xorm.Engine) error { + // Type is Unit's Type + type Type int + + // Enumerate all the unit types + const ( + TypeInvalid Type = iota // 0 invalid + TypeCode // 1 code + TypeIssues // 2 issues + TypePullRequests // 3 PRs + TypeReleases // 4 Releases + TypeWiki // 5 Wiki + TypeExternalWiki // 6 ExternalWiki + TypeExternalTracker // 7 ExternalTracker + TypeProjects // 8 Projects + TypePackages // 9 Packages + TypeActions // 10 Actions + ) + + // RepoUnitAccessMode specifies the users access mode to a repo unit + type UnitAccessMode int + + const ( + // UnitAccessModeUnset - no unit mode set + UnitAccessModeUnset UnitAccessMode = iota // 0 + // UnitAccessModeNone no access + UnitAccessModeNone // 1 + // UnitAccessModeRead read access + UnitAccessModeRead // 2 + // UnitAccessModeWrite write access + UnitAccessModeWrite // 3 + ) + _ = UnitAccessModeNone + _ = UnitAccessModeWrite + + type RepoUnit struct { + DefaultPermissions UnitAccessMode `xorm:"NOT NULL DEFAULT 0"` + } + _, err := x.Where("type = ?", TypeWiki). + Where("default_permissions = ?", UnitAccessModeRead). + Cols("default_permissions"). + Update(RepoUnit{ + DefaultPermissions: UnitAccessModeUnset, + }) + return err +} diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index c11ad70627..e50f79e945 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -41,27 +41,30 @@ func (err ErrUnitTypeNotExist) Unwrap() error { } // RepoUnitAccessMode specifies the users access mode to a repo unit +// Only UnitAccessModeWrite is used by the wiki, to mark it as instance-writable type UnitAccessMode int const ( // UnitAccessModeUnset - no unit mode set UnitAccessModeUnset UnitAccessMode = iota // 0 + // UnitAccessModeNone no access - UnitAccessModeNone // 1 + // UnitAccessModeNone UnitAccessMode = 1 // UnitAccessModeRead read access - UnitAccessModeRead // 2 + // UnitAccessModeRead UnitAccessMode = 2 + // UnitAccessModeWrite write access - UnitAccessModeWrite // 3 + UnitAccessModeWrite UnitAccessMode = 3 ) func (mode UnitAccessMode) ToAccessMode(modeIfUnset perm.AccessMode) perm.AccessMode { switch mode { case UnitAccessModeUnset: return modeIfUnset - case UnitAccessModeNone: - return perm.AccessModeNone - case UnitAccessModeRead: - return perm.AccessModeRead + // case UnitAccessModeNone: + // return perm.AccessModeNone + // case UnitAccessModeRead: + // return perm.AccessModeRead case UnitAccessModeWrite: return perm.AccessModeWrite default: diff --git a/models/repo/repo_unit_test.go b/models/repo/repo_unit_test.go index a1964519bd..3d6d408fcb 100644 --- a/models/repo/repo_unit_test.go +++ b/models/repo/repo_unit_test.go @@ -34,8 +34,8 @@ func TestActionsConfig(t *testing.T) { } func TestRepoUnitAccessMode(t *testing.T) { - assert.Equal(t, perm.AccessModeNone, UnitAccessModeNone.ToAccessMode(perm.AccessModeAdmin)) - assert.Equal(t, perm.AccessModeRead, UnitAccessModeRead.ToAccessMode(perm.AccessModeAdmin)) + // assert.Equal(t, perm.AccessModeNone, UnitAccessModeNone.ToAccessMode(perm.AccessModeAdmin)) + // assert.Equal(t, perm.AccessModeRead, UnitAccessModeRead.ToAccessMode(perm.AccessModeAdmin)) assert.Equal(t, perm.AccessModeWrite, UnitAccessModeWrite.ToAccessMode(perm.AccessModeAdmin)) assert.Equal(t, perm.AccessModeRead, UnitAccessModeUnset.ToAccessMode(perm.AccessModeRead)) } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 3d6a40e9ab..42385d54a6 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -861,7 +861,7 @@ func updateRepoUnits(ctx *context.APIContext, owner string, repo *repo_model.Rep if *opts.GloballyEditableWiki { wikiPermissions = repo_model.UnitAccessModeWrite } else { - wikiPermissions = repo_model.UnitAccessModeRead + wikiPermissions = repo_model.UnitAccessModeUnset } } From a1e3bace727653787eb05a4797bf1e1360ea9c2e Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 9 Jul 2025 08:13:05 +0200 Subject: [PATCH 02/80] [v12.0/forgejo] fix: correctly mark reviews as stale for AGit PRs (#8454) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8450 The argument order for `ValidatePullRequest` is to first give the new commitID and then the old commit ID. This results in reviews not being marked as stale when they are not stale and reviews as not stale when they are stale. The test will fail if the fix is not present. Add testing for the following three scenarios: 1. A review is made, the PR is updated and as a consequence the PR's diff is changed. The review is now marked as stale. 2. A review is made but in the meantime the PR is updated and the review is submitted on a older commit ID. If the diff changed the review is marked as stale. 3. A review that was made against a older commit ID is no longer marked as stale if the PR is force-pushed to that older commit ID. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8454 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- services/agit/agit.go | 2 +- tests/integration/pull_review_test.go | 159 ++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 1 deletion(-) diff --git a/services/agit/agit.go b/services/agit/agit.go index 20e87642c3..8ef641629a 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -221,7 +221,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. } // Validate pull request. - pull_service.ValidatePullRequest(ctx, pr, oldCommitID, opts.NewCommitIDs[i], pusher) + pull_service.ValidatePullRequest(ctx, pr, opts.NewCommitIDs[i], oldCommitID, pusher) // TODO: call `InvalidateCodeComments` diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 603252f45f..4af0a1100e 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -5,19 +5,23 @@ package integration import ( + "bytes" "fmt" "io" "net/http" "net/http/httptest" "net/url" + "os" "path" "strconv" "strings" "testing" + "time" "forgejo.org/models/db" issues_model "forgejo.org/models/issues" repo_model "forgejo.org/models/repo" + unit_model "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" "forgejo.org/modules/git" @@ -761,3 +765,158 @@ func updateFileInBranch(user *user_model.User, repo *repo_model.Repository, tree _, err = files_service.ChangeRepoFiles(git.DefaultContext, repo, user, opts) return err } + +func TestPullRequestStaleReview(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, user2.Name) + + // Create temporary repository. + repo, _, f := tests.CreateDeclarativeRepo(t, user2, "", + []unit_model.Type{unit_model.TypePullRequests}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "FUNFACT", + ContentReader: strings.NewReader("Smithy was the runner up to be Forgejo's name"), + }, + }, + ) + defer f() + + // Clone it. + dstPath := t.TempDir() + 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(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + + // Create first commit. + require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o600)) + require.NoError(t, git.AddChanges(dstPath, true)) + require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add README.", + })) + stdout := &bytes.Buffer{} + require.NoError(t, git.NewCommand(t.Context(), "rev-parse", "HEAD").Run(&git.RunOpts{Dir: dstPath, Stdout: stdout})) + firstCommitID := strings.TrimSpace(stdout.String()) + + // Create agit PR. + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath})) + + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Index: 1, BaseRepoID: repo.ID}) + + req := NewRequest(t, "GET", "/"+repo.FullName()+"/pulls/1/files/reviews/new_comment") + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + + t.Run("Mark review as stale", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Create a approved review against against this commit. + req = NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/comments", map[string]string{ + "_csrf": doc.GetCSRF(), + "origin": doc.GetInputValueByName("origin"), + "latest_commit_id": firstCommitID, + "side": "proposed", + "line": "1", + "path": "FUNFACT", + "diff_start_cid": doc.GetInputValueByName("diff_start_cid"), + "diff_end_cid": doc.GetInputValueByName("diff_end_cid"), + "diff_base_cid": doc.GetInputValueByName("diff_base_cid"), + "content": "nitpicking comment", + "pending_review": "", + }) + session.MakeRequest(t, req, http.StatusOK) + + req = NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/submit", map[string]string{ + "_csrf": doc.GetCSRF(), + "commit_id": firstCommitID, + "content": "looks good", + "type": "comment", + }) + session.MakeRequest(t, req, http.StatusOK) + + // Review is not stale. + review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID}) + assert.False(t, review.Stale) + + // Create second commit + require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## I prefer this heading"), 0o600)) + require.NoError(t, git.AddChanges(dstPath, true)) + require.NoError(t, git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add README.", + })) + + // Push to agit PR. + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath})) + + // Review is stale. + review = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID}) + assert.True(t, review.Stale) + }) + + t.Run("Create stale review", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Review based on the first commit, which is a stale review because the + // PR's head is at the seconnd commit. + req := NewRequestWithValues(t, "POST", "/"+repo.FullName()+"/pulls/1/files/reviews/submit", map[string]string{ + "_csrf": doc.GetCSRF(), + "commit_id": firstCommitID, + "content": "looks good", + "type": "approve", + }) + session.MakeRequest(t, req, http.StatusOK) + + // There does not exist a review that is not stale, because all reviews + // are based on the first commit and the PR's head is at the second commit. + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = false") + }) + + t.Run("Mark unstale", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Force push the PR to the first commit. + require.NoError(t, git.NewCommand(t.Context(), "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})) + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr", "-o", "force-push").Run(&git.RunOpts{Dir: dstPath})) + + // There does not exist a review that is stale, because all reviews + // are based on the first commit and thus all reviews are no longer marked + // as stale. + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true") + }) + + t.Run("Diff did not change", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Create a empty commit and push it to the PR. + require.NoError(t, git.NewCommand(t.Context(), "commit", "--allow-empty", "-m", "Empty commit").Run(&git.RunOpts{Dir: dstPath})) + require.NoError(t, git.NewCommand(t.Context(), "push", "origin", "HEAD:refs/for/main", "-o", "topic=agit-pr").Run(&git.RunOpts{Dir: dstPath})) + + // There does not exist a review that is stale, because the diff did not + // change. + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID}, "stale = true") + }) + }) +} From eb543dcbdb7dcce4bbcedea17cd55299aa05cf71 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 9 Jul 2025 10:20:54 +0200 Subject: [PATCH 03/80] [v12.0/forgejo] fix(email): actions notification template confuses branch with PR (#8455) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8448 When a mail notification is sent because of a failed pull request run, the body will show: Branch: #661 (f57df45) where #661 is the number of the pull request and not the branch. This is because run.PrettyRef() is used and has a misleading special case returning a PR number instead of a ref. Remove the textual description as it can easily be discovered from the run web page linked in the mail. Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8455 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- options/locale_next/locale_en-US.json | 2 +- services/mailer/mail_actions.go | 2 -- services/mailer/mail_actions_now_done_test.go | 2 +- templates/mail/actions/now_done.tmpl | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index b1c98e4551..4f1c3904e2 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -92,7 +92,7 @@ "mail.actions.not_successful_run": "Workflow %[1]s failed in repository %[2]s", "mail.actions.run_info_cur_status": "This Run's Status: %[1]s (just updated from %[2]s)", "mail.actions.run_info_previous_status": "Previous Run's Status: %[1]s", - "mail.actions.run_info_ref": "Branch: %[1]s (%[2]s)", + "mail.actions.run_info_sha": "Commit: %[1]s", "mail.actions.run_info_trigger": "Triggered because: %[1]s by: %[2]s", "repo.diff.commit.next-short": "Next", "repo.diff.commit.previous-short": "Prev", diff --git a/services/mailer/mail_actions.go b/services/mailer/mail_actions.go index fa0d2635f1..a99af823b3 100644 --- a/services/mailer/mail_actions.go +++ b/services/mailer/mail_actions.go @@ -60,7 +60,6 @@ func sendMailActionRun(to *user_model.User, run *actions_model.ActionRun, priorS if len(commitSHA) > 7 { commitSHA = commitSHA[:7] } - branch := run.PrettyRef() data := map[string]any{ "locale": locale, @@ -73,7 +72,6 @@ func sendMailActionRun(to *user_model.User, run *actions_model.ActionRun, priorS "LastRun": lastRun, "PriorStatus": priorStatus, "CommitSHA": commitSHA, - "Branch": branch, "IsSuccess": run.Status.IsSuccess(), } diff --git a/services/mailer/mail_actions_now_done_test.go b/services/mailer/mail_actions_now_done_test.go index f4c597c99c..e84441f460 100644 --- a/services/mailer/mail_actions_now_done_test.go +++ b/services/mailer/mail_actions_now_done_test.go @@ -54,7 +54,7 @@ func getActionsNowDoneTestOrg(t *testing.T, name, email string, owner *user_mode } func assertTranslatedLocaleMailActionsNowDone(t *testing.T, msgBody string) { - AssertTranslatedLocale(t, msgBody, "mail.actions.successful_run_after_failure", "mail.actions.not_successful_run", "mail.actions.run_info_cur_status", "mail.actions.run_info_ref", "mail.actions.run_info_previous_status", "mail.actions.run_info_trigger", "mail.view_it_on") + AssertTranslatedLocale(t, msgBody, "mail.actions.successful_run_after_failure", "mail.actions.not_successful_run", "mail.actions.run_info_cur_status", "mail.actions.run_info_sha", "mail.actions.run_info_previous_status", "mail.actions.run_info_trigger", "mail.view_it_on") } func TestActionRunNowDoneStatusMatrix(t *testing.T) { diff --git a/templates/mail/actions/now_done.tmpl b/templates/mail/actions/now_done.tmpl index a890411055..adc990c545 100644 --- a/templates/mail/actions/now_done.tmpl +++ b/templates/mail/actions/now_done.tmpl @@ -21,7 +21,7 @@
{{.locale.Tr "mail.actions.run_info_cur_status" .Run.Status .PriorStatus}}
- {{.locale.Tr "mail.actions.run_info_ref" .Branch .CommitSHA}}
+ {{.locale.Tr "mail.actions.run_info_sha" .CommitSHA}}
{{if .LastRun}} {{.locale.Tr "mail.actions.run_info_previous_status" .LastRun.Status}}
{{end}} From 501836df77d99b54babc642c63f52c8dc6e8ce52 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 10 Jul 2025 01:30:07 +0200 Subject: [PATCH 04/80] [v12.0/forgejo] fix: ASCII equal fold for authorization header (#8459) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8391 For the "Authorization:" header only lowercase "token" was accepted. This change allows uppercase "Token" as well. Signed-off-by: Nis Wechselberg Co-authored-by: Nis Wechselberg Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8459 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/util/string.go | 22 ++++++++++++++++++++++ modules/util/string_test.go | 26 ++++++++++++++++++++++++++ services/auth/oauth2.go | 3 ++- services/auth/oauth2_test.go | 28 ++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/modules/util/string.go b/modules/util/string.go index cf50f591c6..ca3d43ec6e 100644 --- a/modules/util/string.go +++ b/modules/util/string.go @@ -95,3 +95,25 @@ func UnsafeBytesToString(b []byte) string { func UnsafeStringToBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } + +// AsciiEqualFold is taken from Golang, but reimplemented here, since the original is not exposed to public +// Taken from: https://cs.opensource.google/go/go/+/refs/tags/go1.24.4:src/net/http/internal/ascii/print.go +func ASCIIEqualFold(s, t string) bool { + if len(s) != len(t) { + return false + } + for i := 0; i < len(s); i++ { + if ASCIILower(s[i]) != ASCIILower(t[i]) { + return false + } + } + return true +} + +// AsciiLower returns the ASCII lowercase version of b. +func ASCIILower(b byte) byte { + if 'A' <= b && b <= 'Z' { + return b + ('a' - 'A') + } + return b +} diff --git a/modules/util/string_test.go b/modules/util/string_test.go index 0a4a8bbcfb..1012ab32a4 100644 --- a/modules/util/string_test.go +++ b/modules/util/string_test.go @@ -45,3 +45,29 @@ func TestToSnakeCase(t *testing.T) { assert.Equal(t, expected, ToSnakeCase(input)) } } + +func TestASCIIEqualFold(t *testing.T) { + cases := map[string]struct { + First string + Second string + Expected bool + }{ + "Empty String": {First: "", Second: "", Expected: true}, + "Single Letter Ident": {First: "h", Second: "h", Expected: true}, + "Single Letter Equal": {First: "h", Second: "H", Expected: true}, + "Single Letter Unequal": {First: "h", Second: "g", Expected: false}, + "Simple Match Ident": {First: "someString", Second: "someString", Expected: true}, + "Simple Match Equal": {First: "someString", Second: "someSTRIng", Expected: true}, + "Simple Match Unequal": {First: "someString", Second: "sameString", Expected: false}, + "Different Length": {First: "abcdef", Second: "abcdefg", Expected: false}, + "Unicode Kelvin": {First: "ghijklm", Second: "GHIJ\u212ALM", Expected: false}, + } + + for name := range cases { + c := cases[name] + t.Run(name, func(t *testing.T) { + Actual := ASCIIEqualFold(c.First, c.Second) + assert.Equal(t, c.Expected, Actual) + }) + } +} diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 093940aa18..4fdd15d7ec 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -17,6 +17,7 @@ import ( "forgejo.org/modules/log" "forgejo.org/modules/setting" "forgejo.org/modules/timeutil" + "forgejo.org/modules/util" "forgejo.org/modules/web/middleware" "forgejo.org/services/actions" "forgejo.org/services/auth/source/oauth2" @@ -125,7 +126,7 @@ func parseToken(req *http.Request) (string, bool) { // check header token if auHead := req.Header.Get("Authorization"); auHead != "" { auths := strings.Fields(auHead) - if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { + if len(auths) == 2 && (util.ASCIIEqualFold(auths[0], "token") || util.ASCIIEqualFold(auths[0], "bearer")) { return auths[1], true } } diff --git a/services/auth/oauth2_test.go b/services/auth/oauth2_test.go index d6455b33ad..3fce7df50b 100644 --- a/services/auth/oauth2_test.go +++ b/services/auth/oauth2_test.go @@ -4,6 +4,7 @@ package auth import ( + "net/http" "testing" "forgejo.org/models/unittest" @@ -52,3 +53,30 @@ func TestCheckTaskIsRunning(t *testing.T) { }) } } + +func TestParseToken(t *testing.T) { + cases := map[string]struct { + Header string + ExpectedToken string + Expected bool + }{ + "Token Uppercase": {Header: "Token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Token Lowercase": {Header: "token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Token Unicode": {Header: "to\u212Aen 1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false}, + "Bearer Uppercase": {Header: "Bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Bearer Lowercase": {Header: "bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true}, + "Missing type": {Header: "1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false}, + "Three Parts": {Header: "abc 1234567890 test", ExpectedToken: "", Expected: false}, + } + + for name := range cases { + c := cases[name] + t.Run(name, func(t *testing.T) { + req, _ := http.NewRequest("GET", "/", nil) + req.Header.Add("Authorization", c.Header) + ActualToken, ActualSuccess := parseToken(req) + assert.Equal(t, c.ExpectedToken, ActualToken) + assert.Equal(t, c.Expected, ActualSuccess) + }) + } +} From 3db33792499fdef6ef48e9484bc60b2d12192f0c Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 10 Jul 2025 03:08:54 +0200 Subject: [PATCH 05/80] [v12.0/forgejo] chore: do not navigate to same URL in E2E test (#8463) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8461 - In `test.beforeEach` the browser is navigated to `/user2/repo1/pulls/5` and the test environment is prepared by making sure the pull request is not WIP. At the start of `simple toggle` the browser is navigated to the same URL (and the browser is already on this URL) which the Mobile Chrome browser does not handle gracefully and reliably errors with `net::ERR_ABORTED`. - Because we are already at that URL, do not try to navigate to it again. - I cannot offer a rationale why the Mobile Chrome browser does give a error when this happens in subsequent tests of 'Pull: Toggle WIP'. - Ref forgejo/forgejo#8359 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8463 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- tests/e2e/issue-sidebar.test.e2e.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/issue-sidebar.test.e2e.ts b/tests/e2e/issue-sidebar.test.e2e.ts index bc65b0842c..ca0ee5e278 100644 --- a/tests/e2e/issue-sidebar.test.e2e.ts +++ b/tests/e2e/issue-sidebar.test.e2e.ts @@ -50,7 +50,6 @@ test.describe('Pull: Toggle WIP', () => { test('simple toggle', async ({page}, workerInfo) => { test.skip(workerInfo.project.name === 'Mobile Safari', 'Unable to get tests working on Safari Mobile, see https://codeberg.org/forgejo/forgejo/pulls/3445#issuecomment-1789636'); - await page.goto('/user2/repo1/pulls/5'); // toggle to WIP await toggle_wip_to({page}, true); await check_wip({page}, true); From e7301994997a5de499eb5d2738c2f64d49085f47 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 10 Jul 2025 03:30:37 +0200 Subject: [PATCH 06/80] [v12.0/forgejo] chore: disable mismatched root URL e2e test for safari (#8466) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8460 - I cannot give a good rationale why Mobile Safari and webkit is failing this test quite consistently and also succeed quite often. We add a script to ensure that we get a mismatched URL - but these two browsers either ignore this script sometime or delays the execution until after the root URL check is done. - Because it is very hard to run webkit and mobile safari without running a heavily containerized environment (without a graphical interface) it is near impossible to debug this issue properly; save ourselves a headache and disable it instead. I find it more likely to be a problem with my playwright test than it to be a problem with the mismatched root URL code. - Ref forgejo/forgejo#8359 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8466 Reviewed-by: Otto Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- tests/e2e/login.test.e2e.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/login.test.e2e.ts b/tests/e2e/login.test.e2e.ts index 1ffa0b2e5d..01cf4d7b8d 100644 --- a/tests/e2e/login.test.e2e.ts +++ b/tests/e2e/login.test.e2e.ts @@ -10,7 +10,8 @@ import {expect} from '@playwright/test'; import {test, save_visual, test_context} from './utils_e2e.ts'; -test('Mismatched ROOT_URL', async ({browser}) => { +test('Mismatched ROOT_URL', async ({browser}, workerInfo) => { + test.skip(['Mobile Safari', 'webkit'].includes(workerInfo.project.name), 'init script gets randomly ignored'); const context = await test_context(browser); const page = await context.newPage(); From 18b542f8b1f183f97b0d0e2c5cd178fdecabd613 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 10 Jul 2025 14:44:56 +0200 Subject: [PATCH 07/80] [v12.0/forgejo] fix: use parent context for new transactions (#8474) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8464 - Older code (154 places to be exact) that want to do transactions uses `TxContext`. If the context is not already in a transaction then it uses `DefaultContext` for the new transaction. - Not reusing the context it was given leads to two problems: for tracing (forgejo/forgejo#6470) any SQL code that is executed inside this transaction is not shown in the trace. If the context is cancelled then the transaction is not cancelled and will always complete (given there's no SQL error). - When this function was introduced it didn't take a parent context, hence the usage of `DefaultContext`. When `parentCtx` was introduced it was only used to avoid nested transactions and use the parent's transaction. I do not see any reasons why the parent context cannot be reused, it is reused in the newer transaction function `WithTx` without any known problems. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8474 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/db/context.go | 2 +- models/db/context_test.go | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/models/db/context.go b/models/db/context.go index 35526936af..3e035cd733 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -141,7 +141,7 @@ func TxContext(parentCtx context.Context) (*Context, Committer, error) { return nil, nil, err } - return newContext(DefaultContext, sess, true), sess, nil + return newContext(parentCtx, sess, true), sess, nil } // WithTx represents executing database operations on a transaction, if the transaction exist, diff --git a/models/db/context_test.go b/models/db/context_test.go index 7ab327b7e9..d12d79ebe1 100644 --- a/models/db/context_test.go +++ b/models/db/context_test.go @@ -84,4 +84,16 @@ func TestTxContext(t *testing.T) { return nil })) } + + t.Run("Reuses parent context", func(t *testing.T) { + type unique struct{} + + ctx := context.WithValue(db.DefaultContext, unique{}, "yes!") + assert.False(t, db.InTransaction(ctx)) + + require.NoError(t, db.WithTx(ctx, func(ctx context.Context) error { + assert.Equal(t, "yes!", ctx.Value(unique{})) + return nil + })) + }) } From 6e9a2e89e83ca30d7870ebce66bd4d5ab50bd877 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 10 Jul 2025 21:57:46 +0200 Subject: [PATCH 08/80] [v12.0/forgejo] several fixes of ALT Package registry (#8480) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8475 closes #7946 - The `rpmsRepoPattern` regex has been fixed to handle releases with dots correctly. For example, the version `0.9.0-alt1.git.17.g2ba905d` is valid, just like `0.1.0-1.n1` mentioned in the issue (https://codeberg.org/forgejo/forgejo/issues/7946#issue-1628991) - getEntries now returns entry names. In the integration tests, there were lines like: ```go assert.Equal(t, []string{"", ""}, result.ProvideNames) ``` and it’s unclear how such test logic could have ever worked correctly (fixes problems with deps https://codeberg.org/forgejo/forgejo/issues/7946#issuecomment-5109795) - ALT is an acronym for ALT Linux Team, so `Alt` was replaced with `ALT`. Strictly speaking, it should probably be `ALT Linux`, but since we use `Arch` instead of `Arch Linux`, this seems fine. Also, Distrowatch shows `Arch`/`ALT` in its dropdown, so it’s consistent. - The strings `"Alt Linux Team"` and `"Sisyphus"` in the `Origin` and `Suite` fields have been replaced with `setting.AppName` and `"Unknown"`. `Unknown` is a valid value and is set by default, so this won’t cause any issues. - The documentation link has been fixed: (404 docs.gitea.com/usage/packages/alt/ -> 200 forgejo.org/docs/latest/user/packages/alt/) Co-authored-by: Maxim Slipenko Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8480 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/packages/package.go | 2 +- modules/packages/rpm/metadata.go | 3 ++- routers/api/packages/api.go | 2 +- services/packages/alt/repository.go | 12 +++++++----- templates/package/content/alt.tmpl | 2 +- tests/integration/api_packages_alt_test.go | 16 ++++++++-------- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/models/packages/package.go b/models/packages/package.go index bdd1c74cad..c06dcf5eb3 100644 --- a/models/packages/package.go +++ b/models/packages/package.go @@ -125,7 +125,7 @@ func (pt Type) Name() string { case TypeRpm: return "RPM" case TypeAlt: - return "Alt" + return "ALT" case TypeRubyGems: return "RubyGems" case TypeSwift: diff --git a/modules/packages/rpm/metadata.go b/modules/packages/rpm/metadata.go index 4af9af620f..503b7b1a24 100644 --- a/modules/packages/rpm/metadata.go +++ b/modules/packages/rpm/metadata.go @@ -232,9 +232,10 @@ func getEntries(h *rpmutils.RpmHeader, namesTag, versionsTag, flagsTag int, repo case "alt": for i := range names { e := &Entry{ + Name: names[i], AltFlags: uint32(flags[i]), + Version: versions[i], } - e.Version = versions[i] entries = append(entries, e) } } diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 79e61cf352..c53edfbf96 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -631,7 +631,7 @@ func CommonRoutes() *web.Route { baseURLPattern = regexp.MustCompile(`\A(.*?)\.repo\z`) uploadPattern = regexp.MustCompile(`\A(.*?)/upload\z`) baseRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\/base/(\S+)`) - rpmsRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\.(\S+)\/([a-zA-Z0-9_-]+)-([\d.]+-[a-zA-Z0-9_-]+)\.(\S+)\.rpm`) + rpmsRepoPattern = regexp.MustCompile(`(\S+)\.repo/(\S+)\.(\S+)\/([a-zA-Z0-9_-]+)-([\d.]+-[a-zA-Z0-9_.-]+)\.(\S+)\.rpm`) ) r.Methods("HEAD,GET,PUT,DELETE", "*", func(ctx *context.Context) { diff --git a/services/packages/alt/repository.go b/services/packages/alt/repository.go index 317862da9d..9693f4322e 100644 --- a/services/packages/alt/repository.go +++ b/services/packages/alt/repository.go @@ -714,21 +714,23 @@ func buildRelease(ctx context.Context, pv *packages_model.PackageVersion, pfs [] for architecture := range architectures.Seq() { version := time.Now().Unix() label := setting.AppName - data := fmt.Sprintf(`Archive: Alt Linux Team + origin := setting.AppName + archive := setting.AppName + + data := fmt.Sprintf(`Archive: %s Component: classic Version: %d -Origin: Alt Linux Team +Origin: %s Label: %s Architecture: %s NotAutomatic: false `, - version, label, architecture) + archive, version, origin, label, architecture) fileInfo, err := addReleaseAsFileToRepo(ctx, pv, "release.classic", data, group, architecture) if err != nil { return err } - origin := setting.AppName codename := time.Now().Unix() date := time.Now().UTC().Format(time.RFC1123) @@ -744,7 +746,7 @@ NotAutomatic: false data = fmt.Sprintf(`Origin: %s Label: %s -Suite: Sisyphus +Suite: Unknown Codename: %d Date: %s Architectures: %s diff --git a/templates/package/content/alt.tmpl b/templates/package/content/alt.tmpl index 9a5e9c7656..0a5c328e6d 100644 --- a/templates/package/content/alt.tmpl +++ b/templates/package/content/alt.tmpl @@ -24,7 +24,7 @@ apt-get install {{$.PackageDescriptor.Package.Name}}
- +
diff --git a/tests/integration/api_packages_alt_test.go b/tests/integration/api_packages_alt_test.go index c7ee2c49a5..f43759364e 100644 --- a/tests/integration/api_packages_alt_test.go +++ b/tests/integration/api_packages_alt_test.go @@ -214,7 +214,7 @@ enabled=1`, } assert.Equal(t, "classic", result.Component) - assert.Equal(t, "Alt Linux Team", result.Origin) + assert.Equal(t, "Forgejo", result.Origin) assert.Equal(t, "Forgejo", result.Label) assert.Equal(t, "x86_64", result.Architecture) assert.False(t, result.NotAutomatic) @@ -299,17 +299,17 @@ enabled=1`, assert.Equal(t, "Forgejo", result.Origin) assert.Equal(t, "Forgejo", result.Label) - assert.Equal(t, "Sisyphus", result.Suite) + assert.Equal(t, "Unknown", result.Suite) assert.Equal(t, "x86_64", result.Architectures) assert.Len(t, result.MD5Sum, 3) - assert.Equal(t, "bbf7ae6b2f540673ed1cfc0266b5f319", result.MD5Sum[0].Hash) - assert.Equal(t, 1003, result.MD5Sum[0].Size) + assert.Equal(t, "3f25f44163e8e512efb248d3b96949c3", result.MD5Sum[0].Hash) + assert.Equal(t, 1147, result.MD5Sum[0].Size) assert.Equal(t, "base/pkglist.classic", result.MD5Sum[0].File) assert.Len(t, result.BLAKE2B, 3) - assert.Equal(t, "b527bf038895ce29107ec3a6d2eebd7c365e8ce5ab767276eeddd7c549a159025225cb0ecfdbf7b71da13db7e865e77bcb0e2dae4d21335df01a4a17e0056a70", result.BLAKE2B[0].Hash) - assert.Equal(t, 1003, result.BLAKE2B[0].Size) + assert.Equal(t, "21a63e12a41f70e0697d354ae31e22ad6f024ec5ead2ea498b9a1b7db0f98a4e441f46c96d6912fba19148ff013457561fbb9bf3fca2a21d04cf15a325be7de9", result.BLAKE2B[0].Hash) + assert.Equal(t, 1147, result.BLAKE2B[0].Size) assert.Equal(t, "base/pkglist.classic", result.BLAKE2B[0].File) }) @@ -567,9 +567,9 @@ enabled=1`, assert.Equal(t, "https://gitea.io", result.URL) assert.Equal(t, "x86_64", result.Arch) assert.Equal(t, "gitea-test-1.0.2-1.src.rpm", result.SourceRpm) - assert.Equal(t, []string{"", ""}, result.ProvideNames) + assert.Equal(t, []string{"gitea-test(x86-64)", "gitea-test(x86-64)"}, result.ProvideNames) assert.Equal(t, []int{16777226, 16777226, 16777226, 16777226, 16777226, 16777226, 16777226}, result.RequireFlags) - assert.Equal(t, []string{"", "", "", "", "", "", ""}, result.RequireNames) + assert.Equal(t, []string{"rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)", "rpmlib(PayloadIsXz)"}, result.RequireNames) assert.Equal(t, []string{"5.2-1", "5.2-1", "5.2-1", "5.2-1", "5.2-1", "5.2-1", "5.2-1"}, result.RequireVersions) assert.Equal(t, []int{1678276800}, result.ChangeLogTimes) assert.Equal(t, []string{"KN4CK3R "}, result.ChangeLogNames) From fc726fae9fbcbb86ff404e356998177e0b302adf Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 12 Jul 2025 19:00:07 +0200 Subject: [PATCH 09/80] [v12.0/forgejo] fix(code-search): HighlightSearchResultCode should count the number of bytes and not the number of runes (#8498) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8492 fixes incorrect handling of unicode in the matched line Co-authored-by: Shiny Nematoda Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8498 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/indexer/code/search.go | 21 +++-- modules/indexer/code/search_test.go | 122 ++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 9 deletions(-) create mode 100644 modules/indexer/code/search_test.go diff --git a/modules/indexer/code/search.go b/modules/indexer/code/search.go index adf51a76d7..499b9117c4 100644 --- a/modules/indexer/code/search.go +++ b/modules/indexer/code/search.go @@ -97,7 +97,7 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges conv := hcd.ConvertToPlaceholders(string(hl)) convLines := strings.Split(conv, "\n") - // each highlightRange is of the form [line number, start pos, end pos] + // each highlightRange is of the form [line number, start byte offset, end byte offset] for _, highlightRange := range highlightRanges { ln, start, end := highlightRange[0], highlightRange[1], highlightRange[2] line := convLines[ln] @@ -105,15 +105,18 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges continue } + sr := strings.NewReader(line) sb := strings.Builder{} count := -1 isOpen := false - for _, r := range line { + for r, size, err := sr.ReadRune(); err == nil; r, size, err = sr.ReadRune() { if token, ok := hcd.PlaceholderTokenMap[r]; // token was not found - !ok || - // token was marked as used - token == "" || + !ok { + count += size + } else if + // token was marked as used + token == "" || // the token is not an valid html tag emitted by chroma !(len(token) > 6 && (token[0:5] == "= end: // if tag is not open, no need to close if !isOpen { break } sb.WriteRune(endTag) isOpen = false - case start: + case count >= start: // if tag is open, do not open again if isOpen { break @@ -161,7 +164,7 @@ func HighlightSearchResultCode(filename string, lineNums []int, highlightRanges highlightedLines := strings.Split(hcd.Recover(conv), "\n") // The lineNums outputted by highlight.Code might not match the original lineNums, because "highlight" removes the last `\n` lines := make([]ResultLine, min(len(highlightedLines), len(lineNums))) - for i := 0; i < len(lines); i++ { + for i := range len(lines) { lines[i].Num = lineNums[i] lines[i].FormattedContent = template.HTML(highlightedLines[i]) } diff --git a/modules/indexer/code/search_test.go b/modules/indexer/code/search_test.go new file mode 100644 index 0000000000..e542b38c24 --- /dev/null +++ b/modules/indexer/code/search_test.go @@ -0,0 +1,122 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package code + +import ( + "html/template" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestHighlightSearchResultCode(t *testing.T) { + opts := []struct { + Title string + File string + Lines []int + Range [][3]int + Code string + Result []template.HTML + }{ + { + Title: "One Match Text", + File: "test.txt", + Range: [][3]int{{1, 5, 9}}, + Code: "First Line\nMark this only\nThe End", + Result: []template.HTML{ + "First Line", + "Mark this only", + "The End", + }, + }, + { + Title: "Two Match Text", + File: "test.txt", + Range: [][3]int{ + {1, 5, 9}, + {2, 5, 9}, + }, + Code: "First Line\nMark this only\nMark this too\nThe End", + Result: []template.HTML{ + "First Line", + "Mark this only", + "Mark this too", + "The End", + }, + }, + { + Title: "Unicode Before", + File: "test.txt", + Range: [][3]int{{1, 10, 14}}, + Code: "First Line\nMark 👉 this only\nThe End", + Result: []template.HTML{ + "First Line", + "Mark 👉 this only", + "The End", + }, + }, + { + Title: "Unicode Between", + File: "test.txt", + Range: [][3]int{{1, 5, 14}}, + Code: "First Line\nMark this 😊 only\nThe End", + Result: []template.HTML{ + "First Line", + "Mark this 😊 only", + "The End", + }, + }, + { + Title: "Unicode Before And Between", + File: "test.txt", + Range: [][3]int{{1, 10, 19}}, + Code: "First Line\nMark 👉 this 😊 only\nThe End", + Result: []template.HTML{ + "First Line", + "Mark 👉 this 😊 only", + "The End", + }, + }, + { + Title: "Golang", + File: "test.go", + Range: [][3]int{{1, 14, 23}}, + Code: "func main() {\n\tfmt.Println(\"mark this\")\n}", + Result: []template.HTML{ + "func main() {", + "\tfmt.Println("mark this")", + "}", + }, + }, + { + Title: "Golang Unicode", + File: "test.go", + Range: [][3]int{{1, 14, 28}}, + Code: "func main() {\n\tfmt.Println(\"mark this 😊\")\n}", + Result: []template.HTML{ + "func main() {", + "\tfmt.Println("mark this 😊")", + "}", + }, + }, + } + for _, o := range opts { + t.Run(o.Title, func(t *testing.T) { + lines := []int{} + for i := range strings.Count(strings.TrimSuffix(o.Code, "\n"), "\n") + 1 { + lines = append(lines, i+1) + } + res := HighlightSearchResultCode(o.File, lines, o.Range, o.Code) + assert.Len(t, res, len(o.Result)) + assert.Len(t, res, len(lines)) + + for i, r := range res { + require.Equal(t, lines[i], r.Num) + require.Equal(t, o.Result[i], r.FormattedContent) + } + }) + } +} From a7c64b3a5c8234a9ba0d0fb570958a8a5b77c097 Mon Sep 17 00:00:00 2001 From: Beowulf Date: Tue, 15 Jul 2025 23:22:35 +0200 Subject: [PATCH 10/80] [v12.0/forgejo] fix(ui): multiple ComboMarkdownEditors on one page interfere (#8514) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8417 Co-authored-by: zokki Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8514 Reviewed-by: Earl Warren Reviewed-by: Gusted Co-authored-by: Beowulf Co-committed-by: Beowulf --- tests/e2e/declare_repos_test.go | 16 +++++ tests/e2e/markdown-editor.test.e2e.ts | 59 +++++++++++++++++++ .../js/features/comp/ComboMarkdownEditor.js | 33 +++++------ 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/tests/e2e/declare_repos_test.go b/tests/e2e/declare_repos_test.go index 351f7821eb..39179b50e5 100644 --- a/tests/e2e/declare_repos_test.go +++ b/tests/e2e/declare_repos_test.go @@ -57,6 +57,22 @@ func DeclareGitRepos(t *testing.T) func() { Filename: "a-file", Versions: []string{"{a}{а}"}, }}), + newRepo(t, 2, "multiple-combo-boxes", []FileChanges{{ + Filename: ".forgejo/issue_template/multi-combo-boxes.yaml", + Versions: []string{` +name: "Multiple combo-boxes" +description: "To show something" +body: +- type: textarea + id: textarea-one + attributes: + label: one +- type: textarea + id: textarea-two + attributes: + label: two +`}, + }}), // add your repo declarations here } diff --git a/tests/e2e/markdown-editor.test.e2e.ts b/tests/e2e/markdown-editor.test.e2e.ts index 2b5f0d80a0..7983907343 100644 --- a/tests/e2e/markdown-editor.test.e2e.ts +++ b/tests/e2e/markdown-editor.test.e2e.ts @@ -456,3 +456,62 @@ test('Combo Markdown: preview mode switch', async ({page}) => { await expect(previewPanel).toBeHidden(); await save_visual(page); }); + +test('Multiple combo markdown: insert table', async ({page}) => { + const response = await page.goto('/user2/multiple-combo-boxes/issues/new?template=.forgejo%2fissue_template%2fmulti-combo-boxes.yaml'); + expect(response?.status()).toBe(200); + + // check that there are two textareas + const textareaOne = page.locator('textarea[name=form-field-textarea-one]'); + const comboboxOne = page.locator('textarea#_combo_markdown_editor_0'); + await expect(textareaOne).toBeVisible(); + await expect(comboboxOne).toBeHidden(); + const textareaTwo = page.locator('textarea[name=form-field-textarea-two]'); + const comboboxTwo = page.locator('textarea#_combo_markdown_editor_1'); + await expect(textareaTwo).toBeVisible(); + await expect(comboboxTwo).toBeHidden(); + + // focus first one and add table to it + await textareaOne.click(); + await expect(comboboxOne).toBeVisible(); + await expect(comboboxTwo).toBeHidden(); + + const newTableButtonOne = page.locator('[for="_combo_markdown_editor_0"] button[data-md-action="new-table"]'); + await newTableButtonOne.click(); + + const newTableModalOne = page.locator('div[data-markdown-table-modal-id="0"]'); + await expect(newTableModalOne).toBeVisible(); + + await newTableModalOne.locator('input[name="table-rows"]').fill('3'); + await newTableModalOne.locator('input[name="table-columns"]').fill('2'); + + await newTableModalOne.locator('button[data-selector-name="ok-button"]').click(); + + await expect(newTableModalOne).toBeHidden(); + + await expect(comboboxOne).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n'); + await expect(comboboxTwo).toBeEmpty(); + await save_visual(page); + + // focus second one and add table to it + await textareaTwo.click(); + await expect(comboboxOne).toBeHidden(); + await expect(comboboxTwo).toBeVisible(); + + const newTableButtonTwo = page.locator('[for="_combo_markdown_editor_1"] button[data-md-action="new-table"]'); + await newTableButtonTwo.click(); + + const newTableModalTwo = page.locator('div[data-markdown-table-modal-id="1"]'); + await expect(newTableModalTwo).toBeVisible(); + + await newTableModalTwo.locator('input[name="table-rows"]').fill('2'); + await newTableModalTwo.locator('input[name="table-columns"]').fill('3'); + + await newTableModalTwo.locator('button[data-selector-name="ok-button"]').click(); + + await expect(newTableModalTwo).toBeHidden(); + + await expect(comboboxOne).toHaveValue('| Header | Header |\n|---------|---------|\n| Content | Content |\n| Content | Content |\n| Content | Content |\n'); + await expect(comboboxTwo).toHaveValue('| Header | Header | Header |\n|---------|---------|---------|\n| Content | Content | Content |\n| Content | Content | Content |\n'); + await save_visual(page); +}); diff --git a/web_src/js/features/comp/ComboMarkdownEditor.js b/web_src/js/features/comp/ComboMarkdownEditor.js index 3c94274175..4edd13bfc2 100644 --- a/web_src/js/features/comp/ComboMarkdownEditor.js +++ b/web_src/js/features/comp/ComboMarkdownEditor.js @@ -11,8 +11,6 @@ import {initTextExpander} from './TextExpander.js'; import {showErrorToast, showHintToast} from '../../modules/toast.js'; import {POST} from '../../modules/fetch.js'; -let elementIdCounter = 0; - /** * validate if the given textarea is non-empty. * @param {HTMLElement} textarea - The textarea element to be validated. @@ -39,10 +37,13 @@ export function validateTextareaNonEmpty(textarea) { const listPrefixRegex = /^\s*((\d+)[.)]\s|[-*+]\s{1,4}\[[ x]\]\s?|[-*+]\s|(>\s?)+)?/; class ComboMarkdownEditor { + static idSuffixCounter = 0; + constructor(container, options = {}) { container._giteaComboMarkdownEditor = this; this.options = options; this.container = container; + this.elementIdSuffix = ComboMarkdownEditor.idSuffixCounter++; } async init() { @@ -55,8 +56,6 @@ class ComboMarkdownEditor { this.setupLinkInserter(); await this.switchToUserPreference(); - - elementIdCounter++; } applyEditorHeights(el, heights) { @@ -74,7 +73,7 @@ class ComboMarkdownEditor { setupTextarea() { this.textarea = this.container.querySelector('.markdown-text-editor'); this.textarea._giteaComboMarkdownEditor = this; - this.textarea.id = `_combo_markdown_editor_${elementIdCounter}`; + this.textarea.id = `_combo_markdown_editor_${this.elementIdSuffix}`; this.textarea.addEventListener('input', (e) => this.options?.onContentChanged?.(this, e)); this.applyEditorHeights(this.textarea, this.options.editorHeights); @@ -96,8 +95,8 @@ class ComboMarkdownEditor { this.textareaMarkdownToolbar.querySelector('button[data-md-action="unindent"]')?.addEventListener('click', () => { this.indentSelection(true, false); }); - this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-table"]')?.setAttribute('data-modal', `div[data-markdown-table-modal-id="${elementIdCounter}"]`); - this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-link"]')?.setAttribute('data-modal', `div[data-markdown-link-modal-id="${elementIdCounter}"]`); + this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-table"]')?.setAttribute('data-modal', `div[data-markdown-table-modal-id="${this.elementIdSuffix}"]`); + this.textareaMarkdownToolbar.querySelector('button[data-md-action="new-link"]')?.setAttribute('data-modal', `div[data-markdown-link-modal-id="${this.elementIdSuffix}"]`); // Track whether any actual input or pointer action was made after focusing, and only intercept Tab presses after that. this.tabEnabled = false; @@ -195,7 +194,7 @@ class ComboMarkdownEditor { setupDropzone() { const dropzoneParentContainer = this.container.getAttribute('data-dropzone-parent-container'); if (dropzoneParentContainer) { - this.dropzone = this.container.closest(this.container.getAttribute('data-dropzone-parent-container'))?.querySelector('.dropzone'); + this.dropzone = this.container.closest(dropzoneParentContainer)?.querySelector('.dropzone'); } } @@ -207,13 +206,13 @@ class ComboMarkdownEditor { // So here it uses our defined "data-tab-for" and "data-tab-panel" to generate the "data-tab" attribute for Fomantic. const tabEditor = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-writer'); const tabPreviewer = Array.from(tabs).find((tab) => tab.getAttribute('data-tab-for') === 'markdown-previewer'); - tabEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`); - tabPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`); + tabEditor.setAttribute('data-tab', `markdown-writer-${this.elementIdSuffix}`); + tabPreviewer.setAttribute('data-tab', `markdown-previewer-${this.elementIdSuffix}`); const toolbar = $container[0].querySelector('markdown-toolbar'); const panelEditor = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-writer"]'); const panelPreviewer = $container[0].querySelector('.ui.tab[data-tab-panel="markdown-previewer"]'); - panelEditor.setAttribute('data-tab', `markdown-writer-${elementIdCounter}`); - panelPreviewer.setAttribute('data-tab', `markdown-previewer-${elementIdCounter}`); + panelEditor.setAttribute('data-tab', `markdown-writer-${this.elementIdSuffix}`); + panelPreviewer.setAttribute('data-tab', `markdown-previewer-${this.elementIdSuffix}`); tabEditor.addEventListener('click', () => { toolbar.classList.remove('markdown-toolbar-hidden'); @@ -276,10 +275,10 @@ class ComboMarkdownEditor { setupTableInserter() { const newTableModal = this.container.querySelector('div[data-modal-name="new-markdown-table"]'); - newTableModal.setAttribute('data-markdown-table-modal-id', elementIdCounter); + newTableModal.setAttribute('data-markdown-table-modal-id', this.elementIdSuffix); const button = newTableModal.querySelector('button[data-selector-name="ok-button"]'); - button.setAttribute('data-element-id', elementIdCounter); + button.setAttribute('data-element-id', this.elementIdSuffix); button.addEventListener('click', this.addNewTable); } @@ -311,8 +310,8 @@ class ComboMarkdownEditor { setupLinkInserter() { const newLinkModal = this.container.querySelector('div[data-modal-name="new-markdown-link"]'); - newLinkModal.setAttribute('data-markdown-link-modal-id', elementIdCounter); - const textarea = document.getElementById(`_combo_markdown_editor_${elementIdCounter}`); + newLinkModal.setAttribute('data-markdown-link-modal-id', this.elementIdSuffix); + const textarea = document.getElementById(`_combo_markdown_editor_${this.elementIdSuffix}`); $(newLinkModal).modal({ // Pre-fill the description field from the selection to create behavior similar @@ -331,7 +330,7 @@ class ComboMarkdownEditor { }); const button = newLinkModal.querySelector('button[data-selector-name="ok-button"]'); - button.setAttribute('data-element-id', elementIdCounter); + button.setAttribute('data-element-id', this.elementIdSuffix); button.addEventListener('click', this.addNewLink); } From 17f23d48d6f53ee5a510406465160b7d441a6fdc Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 16 Jul 2025 00:06:22 +0200 Subject: [PATCH 11/80] [v12.0/forgejo] fix: PR not blocked by review request for a whitelisted team (#8516) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8511 Fixes #8491. Previous behavior always updated the newly created review to set the `official` flag to false. This logic was removed. The likely reason that this logic existed was that team reviews were being marked as official based upon `doer`, rather than the target team, which seems undesirable. The expected behavior was retained by removing the check for `IsOfficialReviewer(..., doer)`, ensuring that when making a review request for a team, it doesn't matter *who* makes the request. Co-authored-by: Mathieu Fenniak Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8516 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- .../TestAddTeamReviewRequest/issue.yml | 16 ++++ .../protected_branch.yml | 28 +++++++ .../TestAddTeamReviewRequest/pull_request.yml | 12 +++ models/issues/review.go | 10 --- models/issues/review_test.go | 78 +++++++++++++++++++ 5 files changed, 134 insertions(+), 10 deletions(-) create mode 100644 models/fixtures/TestAddTeamReviewRequest/issue.yml create mode 100644 models/fixtures/TestAddTeamReviewRequest/protected_branch.yml create mode 100644 models/fixtures/TestAddTeamReviewRequest/pull_request.yml diff --git a/models/fixtures/TestAddTeamReviewRequest/issue.yml b/models/fixtures/TestAddTeamReviewRequest/issue.yml new file mode 100644 index 0000000000..a1bcf2921f --- /dev/null +++ b/models/fixtures/TestAddTeamReviewRequest/issue.yml @@ -0,0 +1,16 @@ +- + id: 23 + repo_id: 2 + index: 3 + poster_id: 2 + original_author_id: 0 + name: protected branch pull + content: pull request to a protected branch + milestone_id: 0 + priority: 0 + is_pull: true + is_closed: false + num_comments: 0 + created_unix: 1707270422 + updated_unix: 1707270422 + is_locked: false \ No newline at end of file diff --git a/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml b/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml new file mode 100644 index 0000000000..93909bd991 --- /dev/null +++ b/models/fixtures/TestAddTeamReviewRequest/protected_branch.yml @@ -0,0 +1,28 @@ +- id: 1 + repo_id: 2 + branch_name: protected-main + can_push: false + enable_whitelist: true + whitelist_user_i_ds: [1] + whitelist_team_i_ds: [] + enable_merge_whitelist: true + whitelist_deploy_keys: false + merge_whitelist_user_i_ds: [1] + merge_whitelist_team_i_ds: [] + enable_status_check: false + status_check_contexts: [] + enable_approvals_whitelist: true + approvals_whitelist_user_i_ds: [] + approvals_whitelist_team_i_ds: [1] + required_approvals: 1 + block_on_rejected_reviews: true + block_on_official_review_requests: true + block_on_outdated_branch: true + dismiss_stale_approvals: true + ignore_stale_approvals: false + require_signed_commits: false + protected_file_patterns: "" + unprotected_file_patterns: "" + apply_to_admins: true + created_unix: 1752513073 + updated_unix: 1752513073 \ No newline at end of file diff --git a/models/fixtures/TestAddTeamReviewRequest/pull_request.yml b/models/fixtures/TestAddTeamReviewRequest/pull_request.yml new file mode 100644 index 0000000000..067bb01324 --- /dev/null +++ b/models/fixtures/TestAddTeamReviewRequest/pull_request.yml @@ -0,0 +1,12 @@ +- + id: 11 + type: 0 # gitea pull request + status: 2 # mergeable + issue_id: 23 + index: 3 + head_repo_id: 2 + base_repo_id: 2 + head_branch: feature/protected-branch-pr + base_branch: protected-main + merge_base: 4a357436d925b5c974181ff12a994538ddc5a269 + has_merged: false \ No newline at end of file diff --git a/models/issues/review.go b/models/issues/review.go index 584704d3e8..5370117a81 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -781,10 +781,6 @@ func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organizat official, err := IsOfficialReviewerTeam(ctx, issue, reviewer) if err != nil { return nil, fmt.Errorf("isOfficialReviewerTeam(): %w", err) - } else if !official { - if official, err = IsOfficialReviewer(ctx, issue, doer); err != nil { - return nil, fmt.Errorf("isOfficialReviewer(): %w", err) - } } if review, err = CreateReview(ctx, CreateReviewOptions{ @@ -797,12 +793,6 @@ func AddTeamReviewRequest(ctx context.Context, issue *Issue, reviewer *organizat return nil, err } - if official { - if _, err := db.Exec(ctx, "UPDATE `review` SET official=? WHERE issue_id=? AND reviewer_team_id=?", false, issue.ID, reviewer.ID); err != nil { - return nil, err - } - } - comment, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeReviewRequest, Doer: doer, diff --git a/models/issues/review_test.go b/models/issues/review_test.go index 33d131c225..6e2f6ddfa8 100644 --- a/models/issues/review_test.go +++ b/models/issues/review_test.go @@ -8,6 +8,7 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" + organization_model "forgejo.org/models/organization" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" @@ -319,3 +320,80 @@ func TestAddReviewRequest(t *testing.T) { require.Error(t, err) assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err)) } + +func TestAddTeamReviewRequest(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/TestAddTeamReviewRequest")() + require.NoError(t, unittest.PrepareTestDatabase()) + + setupForProtectedBranch := func() (*issues_model.Issue, *user_model.User) { + // From override models/fixtures/TestAddTeamReviewRequest/issue.yml; issue #23 is a PR into a protected branch + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 23}) + require.NoError(t, issue.LoadRepo(db.DefaultContext)) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + return issue, doer + } + + t.Run("Protected branch, not official team", func(t *testing.T) { + issue, doer := setupForProtectedBranch() + // Team 2 is not part of the whitelist for this protected branch + team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 2}) + + comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) + require.NoError(t, err) + require.NotNil(t, comment) + + review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID) + require.NoError(t, err) + require.NotNil(t, review) + assert.Equal(t, issues_model.ReviewTypeRequest, review.Type) + assert.Equal(t, team.ID, review.ReviewerTeamID) + // This review request should not be marked official because it is not a request for a team in the branch + // protection rule's whitelist... + assert.False(t, review.Official) + }) + + t.Run("Protected branch, official team", func(t *testing.T) { + issue, doer := setupForProtectedBranch() + // Team 1 is part of the whitelist for this protected branch + team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 1}) + + comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) + require.NoError(t, err) + require.NotNil(t, comment) + + review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID) + require.NoError(t, err) + require.NotNil(t, review) + assert.Equal(t, issues_model.ReviewTypeRequest, review.Type) + assert.Equal(t, team.ID, review.ReviewerTeamID) + // Expected to be considered official because team 1 is in the review whitelist for this protected branch + assert.True(t, review.Official) + }) + + t.Run("Unprotected branch, official team", func(t *testing.T) { + // Working on a PR into a branch that is not protected, issue #2 + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) + require.NoError(t, issue.LoadRepo(db.DefaultContext)) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + // team is a team that has write perms against the repo + team := unittest.AssertExistsAndLoadBean(t, &organization_model.Team{ID: 1}) + + comment, err := issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) + require.NoError(t, err) + require.NotNil(t, comment) + + review, err := issues_model.GetTeamReviewerByIssueIDAndTeamID(db.DefaultContext, issue.ID, team.ID) + require.NoError(t, err) + require.NotNil(t, review) + assert.Equal(t, issues_model.ReviewTypeRequest, review.Type) + assert.Equal(t, team.ID, review.ReviewerTeamID) + // Will not be marked as official because PR #2 there's no branch protection rule that enables whitelist + // approvals (verifying logic in `IsOfficialReviewerTeam` indirectly) + assert.False(t, review.Official) + + // Adding the same team review request again should be a noop + comment, err = issues_model.AddTeamReviewRequest(db.DefaultContext, issue, team, doer) + require.NoError(t, err) + require.Nil(t, comment) + }) +} From 3de8d7ec010a62c086c4f952dbe64a29c2470803 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 16 Jul 2025 01:05:37 +0200 Subject: [PATCH 12/80] [v12.0/forgejo] chore: use eventually for mysql collation test (#8518) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8301 - Regression of removing `time.Sleep(5 * time.Second)` in forgejo/forgejo#7917. - Ref: https://codeberg.org/forgejo/forgejo/issues/8221#issuecomment-5532035 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8518 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- tests/integration/db_collation_test.go | 29 +++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tests/integration/db_collation_test.go b/tests/integration/db_collation_test.go index bf55bdd8ee..c41209f1fe 100644 --- a/tests/integration/db_collation_test.go +++ b/tests/integration/db_collation_test.go @@ -7,6 +7,7 @@ package integration import ( "net/http" "testing" + "time" "forgejo.org/models/db" "forgejo.org/modules/setting" @@ -97,9 +98,13 @@ func TestDatabaseCollation(t *testing.T) { defer test.MockVariableValue(&setting.Database.CharsetCollation, "utf8mb4_bin")() require.NoError(t, db.ConvertDatabaseTable()) - r, err := db.CheckCollations(x) - require.NoError(t, err) - assert.Equal(t, "utf8mb4_bin", r.DatabaseCollation) + var r *db.CheckCollationsResult + assert.Eventually(t, func() bool { + r, err = db.CheckCollations(x) + require.NoError(t, err) + + return r.DatabaseCollation == "utf8mb4_bin" + }, time.Second*30, time.Second) assert.True(t, r.CollationEquals(r.ExpectedCollation, r.DatabaseCollation)) assert.Empty(t, r.InconsistentCollationColumns) @@ -117,9 +122,13 @@ func TestDatabaseCollation(t *testing.T) { defer test.MockVariableValue(&setting.Database.CharsetCollation, "utf8mb4_general_ci")() require.NoError(t, db.ConvertDatabaseTable()) - r, err := db.CheckCollations(x) - require.NoError(t, err) - assert.Equal(t, "utf8mb4_general_ci", r.DatabaseCollation) + var r *db.CheckCollationsResult + assert.Eventually(t, func() bool { + r, err = db.CheckCollations(x) + require.NoError(t, err) + + return r.DatabaseCollation == "utf8mb4_general_ci" + }, time.Second*30, time.Second) assert.True(t, r.CollationEquals(r.ExpectedCollation, r.DatabaseCollation)) assert.Empty(t, r.InconsistentCollationColumns) @@ -137,9 +146,15 @@ func TestDatabaseCollation(t *testing.T) { defer test.MockVariableValue(&setting.Database.CharsetCollation, "")() require.NoError(t, db.ConvertDatabaseTable()) + var r *db.CheckCollationsResult r, err := db.CheckCollations(x) require.NoError(t, err) - assert.True(t, r.IsCollationCaseSensitive(r.DatabaseCollation)) + assert.Eventually(t, func() bool { + r, err = db.CheckCollations(x) + require.NoError(t, err) + + return r.IsCollationCaseSensitive(r.DatabaseCollation) + }, time.Second*30, time.Second) assert.True(t, r.CollationEquals(r.ExpectedCollation, r.DatabaseCollation)) assert.Empty(t, r.InconsistentCollationColumns) }) From 8a93a3e59c7394dfff7ff61124ef94f913431299 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 16 Jul 2025 08:44:44 +0200 Subject: [PATCH 13/80] [v12.0/forgejo] fix: expanding exactly 20 lines between diff sections leaves visual artifact (#8523) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8519 Fixes #8488. The issue was caused by having exactly 20 lines of text between two diff sections, which is `BlobExcerptChunkSize`. The previous diff section ends at 530, the next diff section starts at 551, leaving 20 hidden lines. This triggered an off-by-one error in determining whether a synthetic section should be added to the render to include a new expander; it was falsely being triggered when it shouldn't have been. Mostly correct logic was already present in `GetExpandDirection`, so I changed the `ExcerptBlob` web rendering to use `GetExpandDirection` on whether to include the synthetic section. Then I covered `GetExpandDirection` with unit tests covering all boundary conditions, which discovered one other minor bug -- the case where exactly `BlobExcerptChunkSize` is hidden between two diff sections should never have displayed an "Up" and "Down" expansion, but just a single expander (as below). ![Untitled-2025-07-04-1538(1)](/attachments/05573c5e-3cd4-46d5-8c1f-ecdb28302a19) Note that *technically* the `ExcerptBlob` change is not covered by any new tests... but the relevant logic has been moved to somewhere more easily testable and fully covered. If this isn't covered enough for tests, let me know. ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. Co-authored-by: Mathieu Fenniak Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8523 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/repo/compare.go | 32 ++++--- services/gitdiff/gitdiff.go | 21 +++-- services/gitdiff/gitdiff_test.go | 157 +++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 20 deletions(-) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index de2e29ab9f..e3020c92d2 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -933,25 +933,29 @@ func ExcerptBlob(ctx *context.Context) { ctx.Error(http.StatusInternalServerError, "getExcerptLines") return } - if idxRight > lastRight { + + // After the "up" or "down" expansion, check if there's any remaining content in the diff and add a line that will + // be rendered into a new expander at either the top, or bottom. + lineSection := &gitdiff.DiffLine{ + Type: gitdiff.DiffLineSection, + SectionInfo: &gitdiff.DiffLineSectionInfo{ + Path: filePath, + LastLeftIdx: lastLeft, + LastRightIdx: lastRight, + LeftIdx: idxLeft, + RightIdx: idxRight, + LeftHunkSize: leftHunkSize, + RightHunkSize: rightHunkSize, + }, + } + if lineSection.GetExpandDirection() != gitdiff.DiffLineExpandNone { lineText := " " if rightHunkSize > 0 || leftHunkSize > 0 { lineText = fmt.Sprintf("@@ -%d,%d +%d,%d @@\n", idxLeft, leftHunkSize, idxRight, rightHunkSize) } lineText = html.EscapeString(lineText) - lineSection := &gitdiff.DiffLine{ - Type: gitdiff.DiffLineSection, - Content: lineText, - SectionInfo: &gitdiff.DiffLineSectionInfo{ - Path: filePath, - LastLeftIdx: lastLeft, - LastRightIdx: lastRight, - LeftIdx: idxLeft, - RightIdx: idxRight, - LeftHunkSize: leftHunkSize, - RightHunkSize: rightHunkSize, - }, - } + lineSection.Content = lineText + switch direction { case "up": section.Lines = append([]*gitdiff.DiffLine{lineSection}, section.Lines...) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 989f69d4f4..35f8cd3821 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -85,11 +85,20 @@ type DiffLine struct { // DiffLineSectionInfo represents diff line section meta data type DiffLineSectionInfo struct { - Path string - LastLeftIdx int - LastRightIdx int - LeftIdx int - RightIdx int + Path string + + // Last(Left/Right)Idx do not directly relate to this diff section, but indicate the last line number in the + // previous diff section. Set to 0 for the first diff section of a file, and 1 for the first line of code in the + // file. + LastLeftIdx int + LastRightIdx int + + // (Left/Right)Idx are the first line number in this diff section + LeftIdx int + RightIdx int + + // Number of lines contained within each diff section. In the UI, these fields are set to 0 in cases where a + // section is being used as a placeholder at the end of a diff to allow expansion into the remainder of the file. LeftHunkSize int RightHunkSize int } @@ -157,7 +166,7 @@ func (d *DiffLine) GetExpandDirection() DiffLineExpandDirection { } if d.SectionInfo.LastLeftIdx <= 0 && d.SectionInfo.LastRightIdx <= 0 { return DiffLineExpandUp - } else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx > BlobExcerptChunkSize && d.SectionInfo.RightHunkSize > 0 { + } else if d.SectionInfo.RightIdx-d.SectionInfo.LastRightIdx-1 > BlobExcerptChunkSize && d.SectionInfo.RightHunkSize > 0 { return DiffLineExpandUpDown } else if d.SectionInfo.LeftHunkSize <= 0 && d.SectionInfo.RightHunkSize <= 0 { return DiffLineExpandDown diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index 3d3c8432c4..9354abd656 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -715,6 +715,163 @@ func TestGetDiffFull(t *testing.T) { }) } +func TestDiffLine_GetExpandDirection(t *testing.T) { + tests := []struct { + name string + diffLine *DiffLine + expectedResult DiffLineExpandDirection + }{ + { + name: "non-section line - no expansion", + diffLine: &DiffLine{ + Type: DiffLineAdd, + SectionInfo: &DiffLineSectionInfo{}, + }, + expectedResult: DiffLineExpandNone, + }, + { + name: "nil section info - no expansion", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: nil, + }, + expectedResult: DiffLineExpandNone, + }, + { + name: "no lines between", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + // Previous section of the diff displayed up to line 530... + LastRightIdx: 530, + LastLeftIdx: 530, + // This section of the diff starts at line 531... + RightIdx: 531, + LeftIdx: 531, + }, + }, + // There are zero lines between 530 and 531, so we should have nothing to expand. + expectedResult: DiffLineExpandNone, + }, + { + name: "first diff section is the start of the file", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + // Last[...]Idx is set to zero when it's the first section in the file (and not 1, which would be + // the first section -is- the first line of the file). + LastRightIdx: 0, + LastLeftIdx: 0, + // The diff section is showing line 1, the top of th efile. + RightIdx: 1, + LeftIdx: 1, + }, + }, + // We're at the top of the file; no expansion. + expectedResult: DiffLineExpandNone, + }, + { + name: "first diff section doesn't start at the top of the file", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + // Last[...]Idx is set to zero when it's the first section in the file (and not 1, which would be + // the first section -is- the first line of the file). + LastRightIdx: 0, + LastLeftIdx: 0, + RightIdx: 531, + LeftIdx: 531, + }, + }, + // We're at the top of the diff but there's content above, so can only expand up. + expectedResult: DiffLineExpandUp, + }, + { + name: "middle of the file with single expansion", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + // Previous section ended at ~500... + LastRightIdx: 500, + LastLeftIdx: 500, + // Next section starts one line away... + RightIdx: 502, + LeftIdx: 502, + // The next block has content (> 0) + RightHunkSize: 50, + LeftHunkSize: 50, + }, + }, + // Can be expanded in a single direction, displaying the missing line (501). + expectedResult: DiffLineExpandSingle, + }, + { + name: "middle of the file with multi line expansion", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + // Previous section ended at ~500... + LastRightIdx: 500, + LastLeftIdx: 500, + // Lines 501-520 are hidden, exactly 20 lines, matching BlobExcerptChunkSize (20)... + RightIdx: 521, + LeftIdx: 521, + // The next block has content (> 0) + RightHunkSize: 50, + LeftHunkSize: 50, + }, + }, + // Can be expanded in a single direction, displaying all the hidden 20 lines. + expectedResult: DiffLineExpandSingle, + }, + { + name: "middle of the file with multi direction expansion", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + // Previous section ended at ~500... + LastRightIdx: 500, + LastLeftIdx: 500, + // Lines 501-521 are hidden, exactly 21 lines, exceeding BlobExcerptChunkSize (20)... + RightIdx: 522, + LeftIdx: 522, + // The next block has content (> 0) + RightHunkSize: 50, + LeftHunkSize: 50, + }, + }, + // Now can be expanded down to display from 501-520 (521 remains hidden), or up to display 502-521 (501 + // remains hidden). + expectedResult: DiffLineExpandUpDown, + }, + { + name: "end of the diff but still file content to display", + diffLine: &DiffLine{ + Type: DiffLineSection, + SectionInfo: &DiffLineSectionInfo{ + // We had a previous diff section, of any size/location... + LastRightIdx: 200, + LastLeftIdx: 200, + RightIdx: 531, + LeftIdx: 531, + // Hunk size size 0 is a placeholder value for the end or beginning of a file... + RightHunkSize: 0, + LeftHunkSize: 0, + }, + }, + // Combination of conditions says we're at the end of the last diff section, can only expand down. + expectedResult: DiffLineExpandDown, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.diffLine.GetExpandDirection() + assert.Equal(t, tt.expectedResult, result) + }) + } +} + func TestNoCrashes(t *testing.T) { type testcase struct { gitdiff string From 48920461a210576bf30254e394b1213abadd0d61 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 16 Jul 2025 15:58:56 +0200 Subject: [PATCH 14/80] [v12.0/forgejo] chore: failed authentication attempts are not errors and are displayed at the log info level (#8526) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8524 I think those are not errors but simple info messages for admins. I saw them a lot in my logs when no auth was passed to the api calls. Co-authored-by: Michael Kriese Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8526 Reviewed-by: Michael Kriese Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/api/packages/api.go | 2 +- routers/web/web.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index c53edfbf96..721f96b768 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -117,7 +117,7 @@ func verifyAuth(r *web.Route, authMethods []auth.Method) { var err error ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session) if err != nil { - log.Error("Failed to verify user: %v", err) + log.Info("Failed to verify user: %v", err) ctx.Error(http.StatusUnauthorized, "authGroup.Verify") return } diff --git a/routers/web/web.go b/routers/web/web.go index 4b39f22f7d..6cca2a9f2e 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -117,7 +117,7 @@ func webAuth(authMethod auth_service.Method) func(*context.Context) { return func(ctx *context.Context) { ar, err := common.AuthShared(ctx.Base, ctx.Session, authMethod) if err != nil { - log.Error("Failed to verify user: %v", err) + log.Info("Failed to verify user: %v", err) ctx.Error(http.StatusUnauthorized, ctx.Locale.TrString("auth.unauthorized_credentials", "https://codeberg.org/forgejo/forgejo/issues/2809")) return } From 3dd9172fd6b1bbb668b35c1041888c422f077942 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 16 Jul 2025 16:00:56 +0200 Subject: [PATCH 15/80] [v12.0/forgejo] fix: ignore "Close" error when uploading container blob (#8528) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8527 https://github.com/go-gitea/gitea/pull/34620/files (cherry picked from commit 7a59f5a8253402d6f98234bf0047ec53156d3af9) ## Testing Ask @viceice to run the reproducer in his environment once the v12 backport lands Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8528 Reviewed-by: Earl Warren Reviewed-by: Michael Kriese Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/api/packages/container/container.go | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index 191a4aa455..9a7fd03aa8 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -399,12 +399,7 @@ func EndUploadBlob(ctx *context.Context) { } return } - doClose := true - defer func() { - if doClose { - uploader.Close() - } - }() + defer uploader.Close() if ctx.Req.Body != nil { if err := uploader.Append(ctx, ctx.Req.Body); err != nil { @@ -437,11 +432,10 @@ func EndUploadBlob(ctx *context.Context) { return } - if err := uploader.Close(); err != nil { - apiError(ctx, http.StatusInternalServerError, err) - return - } - doClose = false + // There was a strange bug: the "Close" fails with error "close .../tmp/package-upload/....: file already closed" + // AFAIK there should be no other "Close" call to the uploader between NewBlobUploader and this line. + // At least it's safe to call Close twice, so ignore the error. + _ = uploader.Close() if err := container_service.RemoveBlobUploadByID(ctx, uploader.ID); err != nil { apiError(ctx, http.StatusInternalServerError, err) From edceb0c3d93cd0086da61bf3d1a26997099af253 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Wed, 16 Jul 2025 21:52:50 +0500 Subject: [PATCH 16/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate Translation updates that were relevant to v11 branch were picked from this commit: 8cc20864029ff0ee705f0705da0b3790880c49d1 (#8295) Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Dirk Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Juno Takano Co-authored-by: Outbreak2096 Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Xinayder Co-authored-by: artnay Co-authored-by: earl-warren Co-authored-by: jedik Co-authored-by: justbispo Co-authored-by: kwoot Co-authored-by: leandro-costa Co-authored-by: xtex Co-authored-by: yeager Co-authored-by: zub --- options/locale/locale_cs-CZ.ini | 6 +- options/locale/locale_fi-FI.ini | 4 +- options/locale/locale_fr-FR.ini | 4 +- options/locale/locale_lv-LV.ini | 2 +- options/locale/locale_nb_NO.ini | 120 +++++++++++++++++++++++++- options/locale/locale_nds.ini | 2 +- options/locale/locale_nl-NL.ini | 17 +++- options/locale/locale_pt-BR.ini | 18 ++-- options/locale/locale_pt-PT.ini | 12 +-- options/locale/locale_sv-SE.ini | 12 +-- options/locale/locale_uk-UA.ini | 51 +++++++++-- options/locale/locale_zh-CN.ini | 2 +- options/locale_next/locale_cs-CZ.json | 7 +- options/locale_next/locale_lv-LV.json | 7 +- options/locale_next/locale_nb_NO.json | 74 +++++++++++++++- options/locale_next/locale_nl-NL.json | 12 ++- options/locale_next/locale_pt-BR.json | 7 +- options/locale_next/locale_pt-PT.json | 9 +- options/locale_next/locale_sv-SE.json | 9 +- 19 files changed, 326 insertions(+), 49 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 830065fb64..168965a740 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1063,7 +1063,7 @@ language.localization_project = Pomozte nám s překladem Forgejo do vašeho jaz user_block_yourself = Nemůžete zablokovat sami sebe. pronouns_custom_label = Vlastní zájmena change_username_redirect_prompt.with_cooldown.few = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnech. Do té doby budete moci své staré uživatelské jméno znovu získat. -change_username_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dni. Do té doby budete moci své staré uživatelské jméno znovu získat. +change_username_redirect_prompt.with_cooldown.one = Staré uživatelské jméno bude dostupné ostatním po %[1]d dnu. Do té doby budete moci své staré uživatelské jméno znovu získat. keep_pronouns_private = Zobrazovat zájmena pouze přihlášeným uživatelům keep_pronouns_private.description = Toto nastavení skryje vaše zájmena před návštěvníky, kteří nejsou přihlášeni. quota = Kvóta @@ -1579,7 +1579,7 @@ issues.remove_ref_at=`odstranil/a referenci %s %s` issues.add_ref_at=`přidal/a referenci %s %s` issues.delete_branch_at=`odstranil/a větev %s %s` issues.filter_label=Štítek -issues.filter_label_exclude=`Chcete-li vyloučit štítky, použijte alt + click/enter` +issues.filter_label_exclude=Chcete-li vyloučit štítky, použijte Alt + kliknutí issues.filter_label_no_select=Všechny štítky issues.filter_label_select_no_label=Bez štítku issues.filter_milestone=Milník @@ -3058,7 +3058,7 @@ teams.invite.by=Pozvání od %s teams.invite.description=Pro připojení k týmu klikněte na tlačítko níže. follow_blocked_user = Tuto organizaci nemůžete sledovat, protože jste v ní zablokováni. open_dashboard = Otevřít nástěnku -settings.change_orgname_redirect_prompt.with_cooldown.one = Starý název organizace bude dostupný ostatním po %[1]d dni. Do té doby budete moci staré jméno znovu získat. +settings.change_orgname_redirect_prompt.with_cooldown.one = Starý název organizace bude dostupný ostatním po %[1]d dnu. Do té doby budete moci staré jméno znovu získat. settings.change_orgname_redirect_prompt.with_cooldown.few = Starý název organizace bude dostupný ostatním po %[1]d dnech. Do té doby budete moci starý název znovu získat. [admin] diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index 164a60cc8d..efd8a16526 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -2255,14 +2255,14 @@ pulls.cmd_instruction_merge_warning = Varoitus: Asetusta ”Tunnista manu pulls.cmd_instruction_merge_desc = Yhdistä muutokset ja päivitä Forgejossa. pulls.cannot_auto_merge_desc = Tätä vetopyyntöä ei voida yhdistää automaattisesti ristiriitojen vuoksi. adopt_preexisting_success = Omaksuttu tiedostot ja luotu tietovarasto lähteestä %s -issues.comment_manually_pull_merged_at = manuaalisesti yhdistetty kommitti %[1]s %[2]s tietovarastoon %[3]s +issues.comment_manually_pull_merged_at = manuaalisesti yhdisti kommitin %[1]s %[2]s tietovarastoon %[3]s pulls.cmd_instruction_merge_title = Yhdistä pulls.has_merged = Epäonnistui: vetopyyntö on yhdistetty, joten et voi yhdistää uudelleen tai muuttaa kohdehaaraa. pulls.cmd_instruction_checkout_title = Uloskuittaus pulls.cmd_instruction_checkout_desc = Kuittaa ulos uusi haara projektitietovarastostasi ja testaa muutokset. pulls.clear_merge_message_hint = Yhdistämisviestin tyhjentäminen poistaa vain kommittiviestin sisällön ja säilyttää luodut git-trailerit, kuten "Co-Authored-By…". settings.protect_check_status_contexts_desc = Vaadi tilatarkistusten läpäisy ennen yhdistämistä. Kun käytössä, kommitit on ensin työnnettävä toiseen haaraan ja sitten yhdistettävä tai työnnettävä suoraan tätä sääntöä vastaavaan haaraan tilantarkistuksen jälkeen. Jos konteksteja ei löydy, viimeisen kommitin on oltava onnistunut kontekstista riippumatta. -issues.comment_pull_merged_at = yhdistetty kommitti %[1]s %[2]s tietovarastoon %[3]s +issues.comment_pull_merged_at = yhdisti kommitin %[1]s %[2]s haaraan %[3]s settings.pulls.enable_autodetect_manual_merge = Ota Tunnista manuaalinen yhdistäminen automaattisesti -asetus käyttöön (Huomaa: joissakin erityistapauksissa voi esiintyä virhearviointeja) pulls.no_merge_desc = Tätä vetopyyntöä ei voida yhdistää, koska kaikki tietovaraston yhdistämisvaihtoehdot ovat poistettu käytöstä. pulls.no_merge_not_ready = Tämä vetopyyntö ei ole valmis yhdistettäväksi. Tarkista katselmoinnin tila ja tilantarkistukset. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 1cb7103bc0..e522d5ab92 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1579,7 +1579,7 @@ issues.remove_ref_at=`a supprimé la référence %s %s.` issues.add_ref_at=`a ajouté la référence %s %s.` issues.delete_branch_at=`a supprimé la branche %s %s.` issues.filter_label=Label -issues.filter_label_exclude=`Utilisez Alt + Clic/entrée pour exclure les labels.` +issues.filter_label_exclude=`Utilisez Alt + Click pour exclure les étiquettes.` issues.filter_label_no_select=Toutes les labels issues.filter_label_select_no_label=Aucun label issues.filter_milestone=Jalon @@ -4091,4 +4091,4 @@ issues.write = Écrire : Fermer des tickets et gérer les métadonnées t pulls.read = Lire : Lire et créer des demandes de tirage. [translation_meta] -test = Ceci est une chaîne de test. Elle n'est pas affichée dans l'interface de Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) +test = Ceci est une chaîne de test. Elle n'est pas affichée dans Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 98baff217b..4a98e1aa9d 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1577,7 +1577,7 @@ issues.remove_ref_at=`noņēma atsauci no %s %s` issues.add_ref_at=`pievienoja atsauci uz %s %s` issues.delete_branch_at=`izdzēsa zaru %s %s` issues.filter_label=Iezīme -issues.filter_label_exclude=`Jāizmanto alt + klikšķis/Enter, lai neiekļautu iezīmes` +issues.filter_label_exclude=Jāizmanto Alt + klikšķis, lai neiekļautu iezīmes issues.filter_label_no_select=Visas iezīmes issues.filter_label_select_no_label=Bez iezīmes issues.filter_milestone=Atskaites punkts diff --git a/options/locale/locale_nb_NO.ini b/options/locale/locale_nb_NO.ini index 2c8b5cfc64..7dda7499a5 100644 --- a/options/locale/locale_nb_NO.ini +++ b/options/locale/locale_nb_NO.ini @@ -60,7 +60,7 @@ rerun = Kjør på nytt rerun_all = Kjør alle jobber på nytt save = Lagre cancel = Avbryt -forks = Forks +forks = Kopier milestones = Milepæler ok = OK test = Test @@ -134,14 +134,44 @@ webauthn_error_timeout = Et tidsavbrudd oppsto før nøkkelen din kunne leses. V new_fork = Ny fork av repository collaborative = Samarbeidende +tracked_time_summary = Oppsummering av sporet tid basert på problemfiltre +pull_requests = Pull requests +issues = Saker +copy_branch = Kopier branch navn +error404 = Siden du forsøker å nå eksisterer ikke, er blitt fjernet eller du har ikke tilgang til å se den. +error413 = Du har brukt opp kvoten din. +unpin = Løsne +filter.is_fork = Forks +filter.not_fork = Ikke forks + [search] -search = Søk... +search = Søk… type_tooltip = Søketype fuzzy = Fuzzy union = Union regexp = RegExp exact = Nøyaktig +fuzzy_tooltip = Inkluder resultater som også stemmer godt overens med søketermen +union_tooltip = Inkluder resultater som samsvarer med ett eller flere av nøkkelordene adskilt med mellomrom +exact_tooltip = Inkluder kun resultater som samsvarer nøyaktig med søkeordet +regexp_tooltip = Tolk søkeordet som et regulæruttrykk +repo_kind = Søk i repositorer… +user_kind = Søk i brukere… +org_kind = Søk i organisasjoner… +team_kind = Søk i teams… +code_kind = Søk i kode… +code_search_unavailable = Kodesøk er ikke tilgjengelig. Kontakt administratoren. +package_kind = Søk i pakker… +project_kind = Søk i prosjekter… +branch_kind = Søk i brancher… +commit_kind = Søk i commits… +runner_kind = Søk i runners… +no_results = Ingen treff funnet. +issue_kind = Søk i saker… +pull_kind = Søk i pulls… +keyword_search_unavailable = Søk etter nøkkelord er for øyeblikket ikke tilgjengelig. Kontakt administratoren. + [auth] verify = Bekreft sign_up_button = Opprett konto nå. @@ -152,4 +182,88 @@ oauth_signup_title = Fullfør ny konto oauth_signup_submit = Fullfør konto [home] -uname_holder = Brukernavn eller e-postadresse \ No newline at end of file +uname_holder = Brukernavn eller e-postadresse + +[aria] +navbar = Navigasjonslinje +footer = Bunntekst +footer.software = Om dette programmet +footer.links = Linker + +[heatmap] +number_of_contributions_in_the_last_12_months = %s bidrag de siste 12 månedene +contributions_zero = Ingen bidrag +contributions_format = {contributions} den {day} {month} {year} +contributions_one = bidrag +contributions_few = bidrag +less = Mindre +more = Mer + +[editor] +buttons.heading.tooltip = Legg til overskrift +buttons.bold.tooltip = Legg til uthevet tekst +buttons.italic.tooltip = Legg til kursiv text +buttons.quote.tooltip = Siter tekst +buttons.code.tooltip = Legg til kode +buttons.link.tooltip = Legg til link +buttons.list.unordered.tooltip = Legg til punktliste +buttons.list.ordered.tooltip = Legg til nummerert liste +buttons.list.task.tooltip = Legg til liste over saker +buttons.mention.tooltip = Nevn en bruker eller team +buttons.ref.tooltip = Referanse til en sak eller pull request +buttons.switch_to_legacy.tooltip = Bruk den gamle editoren istedenfor +buttons.enable_monospace_font = Aktiver monospace font +buttons.disable_monospace_font = Deaktiver monospace font +buttons.indent.tooltip = Grupper elementene med et nivå +buttons.unindent.tooltip = Pakk ut elementene med et nivå +buttons.new_table.tooltip = Legg til tabell +table_modal.header = Legg til tabell +table_modal.placeholder.header = Overskrift +table_modal.placeholder.content = Innhold +table_modal.label.rows = Rader +table_modal.label.columns = Kolonner +link_modal.header = Legg til en link +link_modal.url = Url +link_modal.description = Beskrivelse +link_modal.paste_reminder = Tips: Når du har en URL i utklippstavlen kan du lime den direkte inn i editoren for å lage en lenke. + +[filter] +string.asc = A - Z +string.desc = Z - A + +[error] +occurred = En feil oppstod +report_message = Hvis du mener dette er en feil i Forgejo kan du søke på Codeberg eller åpne en ny sak. +not_found = Kunne ikke finne målet. +network_error = Nettverks feil +server_internal = Intern server feil + +[startpage] +app_desc = En enkel Git-tjeneste du kan drifte selv +install = Enkel å installere +install_desc = Du kan enkelt kjøre programfilen for din platform, bruke Docker, eller hente den som en ferdig pakke. +platform = Plattformuavhengig +platform_desc = Forgejo fungerer på frie operativsystemer som Linux og FreeBSD, og støtter flere CPU-arkitekturer. Velg den plattformen du foretrekker! +lightweight = Lettvekt +lightweight_desc = Forgejo krever lite ressurser og kan kjøres på en rimelig Raspberry Pi. Spar strøm og miljøet! +license = Åpen kildekode +license_desc = Last ned Forgejo! Bli med ved å bidra for å gjøre prosjektet enda bedre. Ikke vær redd for å bli en bidragsyter! + +[install] +install = Installasjon +title = Førstegangsoppsett +docker_helper = Dersom du bruker Forgejo med Docker, anbefales det å lese dokumentasjonen før du gjør endringer i konfigurasjonen. +require_db_desc = Forgejo krever MySQL, PostgreSQL, SQLite3 eller TiDB (MySQL protokoll). +db_title = Database innstillinger +db_type = Database type +host = Server +user = Brukernavn +password = Passord +db_name = Database navn +db_schema = Skjema +db_schema_helper = La stå tomt for databasens standardverdi ("public"). +ssl_mode = SSL +path = Sti +sqlite_helper = Sti til SQLite3-databasen.
Bruk absolutt filsti dersom Forgejo kjøres som en tjeneste. +reinstall_error = Du prøver å installere i en eksisterende Forgejo-database +reinstall_confirm_message = Å installere på nytt med en eksisterende Forgejo-database kan føre til problemer. I de fleste tilfeller bør du bruke din eksisterende "app.ini" for å kjøre Forgejo. Hvis du vet hva du gjør, og vil fortsette, bekreft følgende: \ No newline at end of file diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index 68fe899d6e..cd91f7c35a 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -1552,7 +1552,7 @@ issues.dependency.pr_close_blocks = Deeser Haalvörslag blockeert dat Dichtmaken issues.dependency.issue_batch_close_blocked = Kann de utköört Gefallens nich all tosamen dichtmaken, denn Gefall #%d hett noch open Ofhangens issues.dependency.pr_close_blocked = Du muttst all Gefallens, wat deesen Haalvörslag blockeren, dichtmaken, ehr du dat hier tosamenföhren kannst. issues.dependency.blocks_short = Blockeert -issues.dependency.blocked_by_short = Hang of vun +issues.dependency.blocked_by_short = Hangt of vun issues.dependency.remove_header = Ofhangen wegdoon issues.dependency.setting = Ofhangens för Gefallens un Haalvörslagen anknipsen issues.dependency.add_error_same_issue = Du kannst een Gefall nich vun sik sülvst ofhangen laten. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 48442bc39f..7f121395b9 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -559,7 +559,7 @@ team_invite.text_2 = Klik alstublieft op de volgende link om aan het team deel t admin.new_user.text = Klik hier om deze gebruiker te beheren vanuit het beheerderspaneel. password_change.subject = Uw wachtwoord is gewijzigd password_change.text_1 = Het wachtwoord voor je account is zojuist gewijzigd. -reset_password.text_1 = +reset_password.text_1 = totp_disabled.subject = TOTP is uitgeschakeld primary_mail_change.subject = Uw primaire e-mail is gewijzigd totp_disabled.no_2fa = Er zijn geen andere 2FA methodes meer geconfigureerd, wat betekent dat het niet langer nodig is om in te loggen op uw account met 2FA. @@ -1495,7 +1495,7 @@ issues.remove_ref_at=`heeft referentie %s verwijderd %s` issues.add_ref_at=`heeft referentie %s toegevoegd %s` issues.delete_branch_at=`heeft %[2]s de branch %[1]s verwijderd.` issues.filter_label=Label -issues.filter_label_exclude=Gebruik alt + klik/voer in om labels uit te sluiten +issues.filter_label_exclude=Gebruik alt + klik om labels uit te sluiten issues.filter_label_no_select=Alle labels issues.filter_milestone=Mijlpaal issues.filter_project=Project @@ -1554,8 +1554,8 @@ issues.close_comment_issue=Sluit met commentaar issues.reopen_issue=Heropen issues.reopen_comment_issue=Heropen met commentaar issues.create_comment=Reageer -issues.closed_at=`heeft dit probleem gesloten %s` -issues.reopened_at=`heropende dit probleem %s` +issues.closed_at=`heeft deze issue gesloten %s` +issues.reopened_at=`heropende deze issue %s` issues.commit_ref_at=`verwees naar dit probleem vanuit commit %s` issues.ref_issue_from=`refereerde aan dit issue %[3]s %[1]s` issues.ref_pull_from=`refereerde aan deze pull request %[3]s %[1]s` @@ -2915,6 +2915,15 @@ sync_fork.branch_behind_few = Deze branch is %[1]d commits achter %[2]s +issues.filter_type.all_pull_requests = Alle pull requests +settings.event_header_action = Actie run evenementen +settings.event_action_failure = Mislukking +settings.event_action_failure_desc = Action run is mislukt. +settings.event_action_recover = Herstel +settings.event_action_recover_desc = Action run is geslaagd nadat de laatste action run in dezelfde workflow is mislukt. +settings.event_action_success = Succes +settings.event_action_success_desc = Action run is geslaagd. + [graphs] component_loading_info = Dit kan even duren… component_failed_to_load = Er is een onverwachte fout opgetreden. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 8de0374eb2..cd5cc14833 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1278,7 +1278,7 @@ star_guest_user=Entre para adicionar este repositório aos favoritos. unwatch=Deixar de observar watch=Observar unstar=Retirar dos favoritos -star=Juntar aos favoritos +star=Adicionar aos favoritos fork=Fork download_archive=Baixar repositório more_operations=Mais operações @@ -1934,7 +1934,7 @@ pulls.status_checks_success=Todas as verificações foram bem sucedidas pulls.status_checks_warning=Algumas verificações reportaram avisos pulls.status_checks_failure=Algumas verificações falharam pulls.status_checks_error=Algumas verificações reportaram erros -pulls.status_checks_requested=Obrigatário +pulls.status_checks_requested=Obrigatório pulls.status_checks_details=Detalhes pulls.update_branch=Atualizar branch por merge pulls.update_branch_rebase=Atualizar branch por rebase @@ -2803,7 +2803,7 @@ mirror_use_ssh.helper = Forgejo irá espelhar o repositório via Git através de mirror_denied_combination = Não é possível combinar o uso de chave pública e autenticação baseada em senha. mirror_public_key = Chave SSH pública mirror_use_ssh.text = Usar autenticação por SSH -mirror_use_ssh.not_available = Autenticação por SSH não está disponível. +mirror_use_ssh.not_available = A autenticação via SSH não está disponível. settings.push_mirror_sync_in_progress = Fazendo push das mudanças para o remoto %s nesse momento. settings.federation_apapiurl = URL de federação deste repositório. Copie e cole isso nas Configurações de Federação de outro repositório como uma URL de um Repositório Seguidor. pulls.agit_explanation = Criado usando o fluxo de trabalho AGit. AGit permite que contribuidores proponham mudanças usando "git push" sem criar um fork ou novo branch. @@ -2817,7 +2817,7 @@ settings.mirror_settings.pushed_repository = Repositório enviado settings.mirror_settings.docs.disabled_pull_mirror.instructions = Configure seu projeto para automaticamente fazer push de commits, tags e branches para outro repositório. Espelhos de pull foram desativados pelo administrador do seu site. settings.mirror_settings.docs.disabled_push_mirror.instructions = Configure seu projeto para automaticamente fazer pull de commits, tags e branches de outro repositório. settings.mirror_settings.docs.doc_link_pull_section = a seção "Fazendo pull de um repositório remoto" da documentação. -subscribe.pull.guest.tooltip = Entre para receber notificações deste pull request. +subscribe.pull.guest.tooltip = Inicie a sessão para receber notificações deste pull request. settings.pull_mirror_sync_quota_exceeded = Cota excedida, não será feito pull das mudanças. settings.mirror_settings.docs.more_information_if_disabled = Saiba mais sobre espelhos de push e pull aqui: settings.transfer_quota_exceeded = O novo dono (%s) excedeu a cota. O repositório não foi transferido. @@ -3900,7 +3900,7 @@ deletion=Excluir segredo deletion.description=A exclusão de um segredo é permanente e não pode ser desfeita. Continuar? deletion.success=O segredo foi excluído. deletion.failed=Falha ao excluir segredo. -management=Gerenciar segredos +management=Gerenciamento de segredos [actions] actions=Ações @@ -3909,12 +3909,12 @@ unit.desc=Gerenciar pipelines integradas de CI/CD com Forgejo Actions. status.unknown=Desconhecido status.waiting=Aguardando -status.running=Rodando +status.running=Executando status.success=Sucesso status.failure=Falha -status.cancelled=Cancelado -status.skipped=Ignorado -status.blocked=Bloqueado +status.cancelled=Cancelada +status.skipped=Ignorada +status.blocked=Bloqueada runners=Runners runners.runner_manage_panel=Gerenciar runners diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 0e8f2d485e..d82ae69fa4 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1063,8 +1063,8 @@ language.description = Este idioma vai ser guardado na sua conta e ser usado com language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Ler mais. 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. +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. Pode 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. Pode 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. @@ -1583,7 +1583,7 @@ issues.remove_ref_at=`removeu a referência %s %s` issues.add_ref_at=`adicionou a referência %s %s` issues.delete_branch_at=`eliminou o ramo %s %s` issues.filter_label=Rótulo -issues.filter_label_exclude=`Use alt + clique/enter para excluir rótulos` +issues.filter_label_exclude=Use Alt + Clique para excluir rótulos issues.filter_label_no_select=Todos os rótulos issues.filter_label_select_no_label=Sem rótulo issues.filter_milestone=Etapa @@ -2923,6 +2923,8 @@ settings.event_action_recover_desc = A execução de ação foi bem sucedida dep settings.event_action_success = Sucesso settings.event_action_success_desc = A Execução de ação foi bem sucedida. +issues.filter_type.all_pull_requests = Todos os pedidos de integração + [graphs] component_loading=A carregar %s… component_loading_failed=Não foi possível carregar %s @@ -3057,8 +3059,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 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. +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. Pode 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. Pode ainda reivindicar o nome antigo durante o período de espera. [admin] dashboard=Painel de controlo diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 8b43cb29b8..2e212c8c49 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -918,7 +918,7 @@ migrate.migrate_items_options=Åtkomsttoken krävs för att migrera ytterligare migrated_from=Migrerad från %[2]s migrated_from_fake=Migrerad från %[1]s migrate.migrate=Migrera från %s -migrate.migrating=Migrerar från %s ... +migrate.migrating=Migrerar från %s … migrate.migrating_failed=Migrering från %s misslyckades. migrate.migrating_issues=Migrerar ärenden @@ -2324,15 +2324,15 @@ exact = Exakt exact_tooltip = Inkludera bara resultat som exakt matchar söktermen repo_kind = Sök repon… user_kind = Sök användare… -code_kind = Sök kod... -package_kind = Sök paket... +code_kind = Sök kod… +package_kind = Sök paket… runner_kind = Sök exekutorer... -branch_kind = Sök grenar... +branch_kind = Sök grenar… commit_kind = Sök commiter... -project_kind = Sök projekt... +project_kind = Sök projekt… search = Sök… type_tooltip = Söktyp -team_kind = Sök lag... +team_kind = Sök team… org_kind = Sök organisationer… issue_kind = Sök ärenden... regexp_tooltip = Tolka söktermen som ett reguljärt uttryck diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index faa3f2a56e..11954dd625 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -198,7 +198,7 @@ buttons.disable_monospace_font = Вимкнути моноширинний шр buttons.indent.tooltip = Вкласти предмет на один рівень buttons.unindent.tooltip = Викласти об'єкт на один рівень buttons.mention.tooltip = Згадати користувача чи команду -buttons.ref.tooltip = Послатись на задачу чи на запит на злиття +buttons.ref.tooltip = Послатися на задачу чи на запит на злиття buttons.enable_monospace_font = Увімкнути моноширинний шрифт buttons.new_table.tooltip = Додати таблицю table_modal.label.columns = Стовпці @@ -1276,7 +1276,7 @@ file_raw=Неформатований file_history=Історія file_view_source=Переглянути вихідний код file_view_rendered=Переглянути відрендерено -file_view_raw=Перегляд Raw +file_view_raw=Переглянути неформатований file_permalink=Постійне посилання file_too_large=Цей файл завеликий щоб бути показаним. @@ -2670,6 +2670,32 @@ commitstatus.success = Успіх commitstatus.failure = Збій issues.filter_type.all_pull_requests = Усі запити на злиття +summary_card_alt = Підсумкова картка репозиторію %s +migrate_options_lfs_endpoint.placeholder = Якщо залишити порожнім, то кінцеву точку буде визначено з URL-адреси клону +migrate.invalid_local_path = Локальний шлях недійсний. Він не існує або не є каталогом. +subscribe.issue.guest.tooltip = Увійдіть, щоб підписатися на цю задачу. +subscribe.pull.guest.tooltip = Увійдіть, щоб підписатися на цей запит на злиття. +cite_this_repo = Послатися на цей репозиторій +broken_message = Неможливо прочитати дані Git, що лежать в основі цього репозиторію. Зверніться до адміністратора цього екземпляра або видаліть репозиторій. +invisible_runes_header = `Цей файл містить невидимі символи Юнікоду` +invisible_runes_description = `Цей файл містить невидимі символи Юнікоду, які людині неможливо розрізнити, але які по-різному обробляються комп'ютером. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` +ambiguous_runes_header = `Цей файл містить неоднозначні символи Юнікоду` +ambiguous_runes_description = `Цей файл містить символи Юнікоду, які легко сплутати з іншими символами. Якщо так зроблено навмисно, можете ігнорувати це попередження. Щоб показати ці символи, скористайтеся кнопкою «Escape».` +invisible_runes_line = `У цьому рядку є невидимі символи Юнікоду` +ambiguous_runes_line = `У цьому рядку є неоднозначні символи Юнікоду` +ambiguous_character = `%[1]c [U+%04[1]X] можна сплутати з %[2]c [U+%04[2]X]` +escape_control_characters = Escape +unescape_control_characters = Unescape +no_eol.text = Без EOL +editor.filename_is_invalid = Хибна назва файлу: «%s». +editor.directory_is_a_file = Назва каталогу «%s» уже використовується в цьому репозиторії як назва файлу. +editor.filename_is_a_directory = Назва файлу «%s» уже використовується в цьому репозиторії як назва каталогу. +issues.choose.invalid_config = У конфігурації задачі є помилки: +issues.summary_card_alt = Підсумкова картка задачі «%s» в репозиторії %s +settings.sourcehut_builds.secrets_helper = Надати завданню доступ до секретів збірки (потрібен дозвіл SECRETS:RO) +settings.remove_protected_branch_failed = Не вдалося видалити правило захисту гілок «%s». +release.summary_card_alt = Підсумкова картка випуску «%s» в репозиторії %s + [graphs] contributors.what = внески component_loading_info = Це може зайняти деякий час… @@ -3193,7 +3219,7 @@ config.git_gc_timeout=Тайм-аут операції збирача смітт config.log_config=Конфігурація журналу config.disabled_logger=Вимкнено -config.access_log_mode=Режим доступу до журналу +config.access_log_mode=Режим журналювання доступу config.xorm_log_sql=Журнал SQL @@ -3250,7 +3276,7 @@ packages.package_manage_panel = Керування пакунками packages.published = Опубліковано notices.operations = Дії packages.cleanup = Очистити недійсні дані -packages.cleanup.success = Успішно очищено недійсні дані +packages.cleanup.success = Недійсні дані успішно очищено users.still_own_packages = Цей користувач досі володіє одним чи більше пакунками, спочатку видаліть ці пакунки. users.purge_help = Примусово видалити користувача і будь-які репозиторії, організації та пакунки, якими він володіє. Всі коментарі та задачі, створені цим користувачем, також будуть видалені. dashboard.cleanup_packages = Очистити непридатні пакунки @@ -3308,6 +3334,13 @@ auths.invalid_openIdConnectAutoDiscoveryURL = Неправильна URL-адр settings = Налаштування адміністратора +dashboard.cancel_abandoned_jobs = Скасувати покинуті завдання дій +dashboard.start_schedule_tasks = Запустити заплановані завдання дій +config.logger_name_fmt = Журнал: %s +config.access_log_template = Шаблон журналу доступу +config.set_setting_failed = Не вдалося встановити параметр %s +monitor.download_diagnosis_report = Завантажити діагностичний звіт + [action] create_repo=створив(ла) репозиторій %s rename_repo=репозиторій перейменовано з %[1]s на %[3]s @@ -3527,6 +3560,9 @@ npm.registry = Налаштуйте цей реєстр у файлі .npm chef.registry = Налаштуйте цей реєстр у файлі ~/.chef/config.rb: owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки публічний ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. +nuget.dependency.framework = Цільовий фреймворк +owner.settings.cleanuprules.preview.overview = Заплановано видалити %d пакунків. + [secrets] deletion = Видалити секрет creation.success = Секрет «%s» додано. @@ -3633,6 +3669,11 @@ runners.status.idle = Простоює +runs.invalid_workflow_helper = Недійсний файл конфігурації робочого потоку. Будь ласка, перевірте файл конфігурації: %s +runs.no_job_without_needs = Робочий потік повинен містити принаймні одне завдання без залежностей. +runs.no_job = Робочий потік повинен містити принаймні одне завдання +workflow.dispatch.use_from = Використати робочий потік із + [projects] type-3.display_name = Проєкт організації type-2.display_name = Проєкт репозиторію @@ -3641,7 +3682,7 @@ deleted.display_name = Видалений проєкт [git.filemode] symbolic_link=Символічне посилання -directory = Тека +directory = Каталог submodule = Підмодуль normal_file = Звичайний файл executable_file = Виконуваний файл diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index c6c534df9f..d87ad53676 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -171,7 +171,7 @@ copy_path = 复制路径 [aria] navbar=导航栏 footer=页脚 -footer.software=关于软件 +footer.software=关于此软件 footer.links=链接 [heatmap] diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 97a8536d4f..3a32d3a95a 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -105,5 +105,10 @@ "settings.visibility.description": "Viditelnost profilu ovlivňuje možnost ostatních přistupovat k vašim veřejným repozitářům. Zjistit více", "avatar.constraints_hint": "Velikost vlastního avataru nesmí překročit %[1]s nebo být větší než %[2]dx%[3]d pixelů", "repo.diff.commit.next-short": "Další", - "repo.diff.commit.previous-short": "Předchozí" + "repo.diff.commit.previous-short": "Předchozí", + "profile.actions.tooltip": "Další akce", + "profile.edit.link": "Upravit profil", + "feed.atom.link": "Zdroj Atom", + "keys.ssh.link": "Klíče SSH", + "keys.gpg.link": "Klíče GPG" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index f71cfa227d..242c22bbf3 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -105,5 +105,10 @@ "settings.visibility.description": "Profila redzamība ietekmē iespēju citiem piekļūt Tavām glabātavām, kas nav privātas. Uzzināt vairāk", "avatar.constraints_hint": "Pielāgots profila attēls nevar pārsniegt %[1]s vai būt lielāks par %[2]dx%[3]d pikseļiem", "repo.diff.commit.next-short": "Nāk.", - "repo.diff.commit.previous-short": "Iepr." + "repo.diff.commit.previous-short": "Iepr.", + "profile.actions.tooltip": "Vairāk darbību", + "profile.edit.link": "Labot profilu", + "feed.atom.link": "Atom barotne", + "keys.ssh.link": "SSH atslēgas", + "keys.gpg.link": "GPG atslēgas" } diff --git a/options/locale_next/locale_nb_NO.json b/options/locale_next/locale_nb_NO.json index 0967ef424b..8f2f9b5e1c 100644 --- a/options/locale_next/locale_nb_NO.json +++ b/options/locale_next/locale_nb_NO.json @@ -1 +1,73 @@ -{} +{ + "home.welcome.no_activity": "Ingen aktivitet", + "home.welcome.activity_hint": "Det er foreløpig ingenting i feeden din. Aktivitetene dine og handlingene dine fra repositorier du følger, vil vises her etter hvert.", + "home.explore_repos": "Utforsk repositorier", + "home.explore_users": "Utforsk brukere", + "home.explore_orgs": "Utforsk organisasjoner", + "stars.list.none": "Ingen har gitt stjerner til dette repoet.", + "watch.list.none": "Ingen følger dette repoet.", + "followers.incoming.list.self.none": "Ingen følger profilen din.", + "followers.incoming.list.none": "Ingen følger denne brukeren.", + "followers.outgoing.list.self.none": "Du følger ikke noen.", + "followers.outgoing.list.none": "%s følger ikke noen.", + "relativetime.now": "nå", + "relativetime.future": "i fremtiden", + "relativetime.1day": "i går", + "relativetime.2days": "to dager siden", + "relativetime.1week": "forrige uke", + "relativetime.2weeks": "to uker siden", + "relativetime.1month": "forrige måned", + "relativetime.2months": "to måneder siden", + "relativetime.1year": "i fjor", + "relativetime.2years": "to år siden", + "repo.form.cannot_create": "Det maksimale antallet repositories er nådd i alle områdene du har tilgang til.", + "repo.issue_indexer.title": "Saksindekserer", + "search.milestone_kind": "Søker i milepæler…", + "incorrect_root_url": "Denne Forgejo instansen er konfigurert til å bruke \"%s\". Du bruker Forgejo via en annen URL, noe som kan forårsake at deler av applikasjonen ikke fungerer. Den kanoniske URL-en styres av Forgejo-administratorer via innstillingen ROOT_URL i app.ini-filen.", + "themes.names.forgejo-auto": "Forgejo (følg systemtema)", + "themes.names.forgejo-light": "Forgejo lyst", + "themes.names.forgejo-dark": "Forgejo mørk", + "error.not_found.title": "Fant ikke siden", + "alert.asset_load_failed": "Kunne ikke laste inn ressursfiler fra {path}. Sørg for at ressursfilene er tilgjengelige.", + "alert.range_error": " må være et nummer mellom %[1]s og %[2]s.", + "install.invalid_lfs_path": "Kan ikke opprette LFS-root på: %[1]s", + "profile.actions.tooltip": "Flere handlinger", + "profile.edit.link": "Rediger profil", + "feed.atom.link": "Atom feed", + "keys.ssh.link": "SSH nøkler", + "keys.gpg.link": "GPG nøkler", + "admin.config.moderation_config": "Moderasjonskonfigurasjon", + "moderation.report_abuse": "Rapporter missbruk", + "moderation.report_content": "Rapporter innhold", + "moderation.report_abuse_form.header": "Rapporter missbruk til en administrator", + "moderation.report_abuse_form.details": "Denne formen skal brukes for å rapporterer brukere som oppretter spam profiler, repositorier, saker, kommentarer eller oppfører seg upassende.", + "moderation.report_abuse_form.invalid": "Ugyldige argumenter", + "moderation.report_abuse_form.already_reported": "Du har allerede rapportert dette innholdet", + "moderation.abuse_category": "Kategori", + "moderation.abuse_category.placeholder": "Velg en kategori", + "moderation.abuse_category.spam": "Spam", + "moderation.abuse_category.malware": "Skadelig programvare", + "moderation.abuse_category.illegal_content": "Ulovlig innhold", + "moderation.abuse_category.other_violations": "Andre regel overtredelser", + "moderation.report_remarks": "Kommentar", + "moderation.report_remarks.placeholder": "Skriv noen detaljer rundt missbruket du rapporterer.", + "moderation.submit_report": "Send rapport", + "moderation.reporting_failed": "Missbruk rapporten kunne ikke sendes inn: %v", + "moderation.reported_thank_you": "Takk for meldingen. Vi har varslet administratorene.", + "mail.actions.successful_run_after_failure_subject": "Arbeidsflyten %[1]s er gjenopprettet i repository %[2]s", + "mail.actions.not_successful_run_subject": "Arbeidsflyten %[1]s feilet i repository %[2]s", + "mail.actions.successful_run_after_failure": "Arbeidsflyten %[1]s er gjenopprettet i repository %[2]s", + "mail.actions.not_successful_run": "Arbeidsflyten %[1]s feilet i repository %[2]s", + "mail.actions.run_info_cur_status": "Status for denne kjøringen: %[1]s (oppdatert fra %[2]s)", + "mail.actions.run_info_previous_status": "Status for forrige kjøring: %[1]s", + "mail.actions.run_info_trigger": "Startet på grunn av: %[1]s by: %[2]s", + "repo.diff.commit.next-short": "Neste", + "repo.diff.commit.previous-short": "Forrige", + "discussion.locked": "Denne diskusjonen er låst. Kommentarer kan kun gjøres av bidragsytere.", + "editor.textarea.tab_hint": "Linjen er allerede innrykket. Trykk Tab igjen eller trykk Escape for å gå ut av editoren.", + "editor.textarea.shift_tab_hint": "Ingen innrykk på denne linjen. Trykk Shift + Tab igjen eller Escape for å gå ut av editoren.", + "admin.dashboard.cleanup_offline_runners": "Rydd opp offline runners", + "settings.visibility.description": "Profilens synlighet påvirker andres mulighet til å få tilgang til dine ikke-private repositorier. Les mer", + "avatar.constraints_hint": "Egendefinert avatar kan ikke overstige %[1]s i størrelse eller være større enn %[2]d × %[3]d piksler", + "meta.last_line": "Vi gir oss ikke. Kongen har sagt nei!" +} diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 4f109825aa..690a522b80 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -92,5 +92,15 @@ "watch.list.none": "Niemand houdt deze repo in de gaten.", "followers.incoming.list.self.none": "Niemand volgt uw profiel.", "followers.incoming.list.none": "Deze gebruiker wordt door niemand gevolgd.", - "followers.outgoing.list.self.none": "U volgt niemand." + "followers.outgoing.list.self.none": "U volgt niemand.", + "profile.actions.tooltip": "Meer acties", + "profile.edit.link": "Profiel bewerken", + "feed.atom.link": "Atom-feed", + "keys.ssh.link": "SSH sleutels", + "keys.gpg.link": "GPG sleutels", + "repo.diff.commit.next-short": "Volgende", + "repo.diff.commit.previous-short": "Vorige", + "admin.dashboard.cleanup_offline_runners": "Offline runners opruimen", + "settings.visibility.description": "Profielzichtbaarheid beïnvloedt de mogelijkheid van anderen om toegang te krijgen tot je niet-privé repositories. Lees meer", + "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 1a5eca6d34..acff9ff364 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -105,5 +105,10 @@ "avatar.constraints_hint": "Imagem de perfil personalizada não pode exceder %[1]s em tamanho ou ser maior que %[2]dx%[3]d pixels", "settings.visibility.description": "A visibilidade do perfil afeta a habilidade de acessarem seus repositórios não-privados. Saiba mais", "repo.diff.commit.next-short": "Próximo", - "repo.diff.commit.previous-short": "Anterior" + "repo.diff.commit.previous-short": "Anterior", + "profile.actions.tooltip": "Mais Actions", + "profile.edit.link": "Editar perfil", + "feed.atom.link": "Feed Atom", + "keys.ssh.link": "Chaves SSH", + "keys.gpg.link": "Chaves GPG" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 78e6dc4493..8697bbaec0 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -103,5 +103,12 @@ "stars.list.none": "Ninguém juntou este repositório aos favoritos.", "admin.dashboard.cleanup_offline_runners": "Limpeza de executores offline", "settings.visibility.description": "A visibilidade do perfil afecta a capacidade de outros acederem aos seus repositórios não privados. Ler mais", - "avatar.constraints_hint": "O avatar personalizado não pode exceder %[1]s de tamanho ou ser maior do que %[2]dx%[3]d pixéis" + "avatar.constraints_hint": "O avatar personalizado não pode exceder %[1]s de tamanho ou ser maior do que %[2]dx%[3]d pixéis", + "profile.actions.tooltip": "Mais Actions", + "profile.edit.link": "Editar perfil", + "feed.atom.link": "Feed Atom", + "keys.ssh.link": "Chaves SSH", + "keys.gpg.link": "Chaves GPG", + "repo.diff.commit.next-short": "Seg.", + "repo.diff.commit.previous-short": "Ant." } diff --git a/options/locale_next/locale_sv-SE.json b/options/locale_next/locale_sv-SE.json index 9a8762212c..c8c50baa76 100644 --- a/options/locale_next/locale_sv-SE.json +++ b/options/locale_next/locale_sv-SE.json @@ -95,5 +95,12 @@ "moderation.abuse_category.spam": "Skräppost", "moderation.abuse_category.malware": "Skadlig kod", "settings.visibility.description": "Profilens synlighet påverkar andras möjlighet att komma åt dina icke-privata förråd. Läs mer", - "avatar.constraints_hint": "Anpassade avatarer får inte vara större än %[1] eller %[2]dx%[3] bildpunkter" + "avatar.constraints_hint": "Anpassade avatarer får inte vara större än %[1] eller %[2]dx%[3] bildpunkter", + "profile.actions.tooltip": "Fler åtgärder", + "profile.edit.link": "Redigera profil", + "feed.atom.link": "Atom-flöde", + "keys.ssh.link": "SSH-nycklar", + "keys.gpg.link": "GPG-nycklar", + "repo.diff.commit.next-short": "Nästa", + "repo.diff.commit.previous-short": "Föreg" } From 49b4965e1f8267627289b4c63bbb40d235639105 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Wed, 16 Jul 2025 21:54:14 +0500 Subject: [PATCH 17/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate Translation updates that were relevant to v11 branch were picked from this commit: dc6626453a1645bc48affd4583b439aae8b4bffd (#8410) Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Atul_Eterno Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Dirk Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Juno Takano Co-authored-by: Laxystem Co-authored-by: Miguel P.L Co-authored-by: Salif Mehmed Co-authored-by: SomeTr Co-authored-by: aivot-on Co-authored-by: earl-warren Co-authored-by: kne Co-authored-by: oster5 Co-authored-by: readevalprintloop Co-authored-by: tacaly Co-authored-by: volkan Co-authored-by: xtex Co-authored-by: yurtpage --- options/locale/locale_ar.ini | 26 +- options/locale/locale_bg.ini | 407 +++++++++++++++++++++++++- options/locale/locale_ca.ini | 24 +- options/locale/locale_cs-CZ.ini | 6 +- options/locale/locale_da.ini | 17 +- options/locale/locale_de-DE.ini | 2 +- options/locale/locale_el-GR.ini | 10 +- options/locale/locale_eo.ini | 12 +- options/locale/locale_es-ES.ini | 11 +- options/locale/locale_et.ini | 26 +- options/locale/locale_fa-IR.ini | 30 +- options/locale/locale_fi-FI.ini | 10 + options/locale/locale_fil.ini | 4 +- options/locale/locale_fr-FR.ini | 2 +- options/locale/locale_ga-IE.ini | 28 +- options/locale/locale_gl.ini | 24 +- options/locale/locale_he.ini | 36 ++- options/locale/locale_hu-HU.ini | 24 +- options/locale/locale_id-ID.ini | 38 +-- options/locale/locale_is-IS.ini | 22 +- options/locale/locale_it-IT.ini | 32 +- options/locale/locale_ja-JP.ini | 28 +- options/locale/locale_ko-KR.ini | 18 +- options/locale/locale_lt.ini | 24 +- options/locale/locale_ml-IN.ini | 6 +- options/locale/locale_nds.ini | 2 +- options/locale/locale_pl-PL.ini | 28 +- options/locale/locale_ro.ini | 15 +- options/locale/locale_ru-RU.ini | 8 +- options/locale/locale_si-LK.ini | 6 +- options/locale/locale_sk-SK.ini | 6 +- options/locale/locale_sl.ini | 8 +- options/locale/locale_sv-SE.ini | 6 +- options/locale/locale_tr-TR.ini | 133 ++++++--- options/locale/locale_uk-UA.ini | 173 ++++++++--- options/locale/locale_zh-CN.ini | 4 +- options/locale/locale_zh-TW.ini | 2 +- options/locale_next/locale_ar.json | 4 +- options/locale_next/locale_be.json | 4 +- options/locale_next/locale_bn.json | 4 +- options/locale_next/locale_ca.json | 3 +- options/locale_next/locale_cs-CZ.json | 3 +- options/locale_next/locale_da.json | 7 +- options/locale_next/locale_de-DE.json | 3 +- options/locale_next/locale_es-ES.json | 3 +- options/locale_next/locale_fi-FI.json | 3 +- options/locale_next/locale_hu-HU.json | 3 +- options/locale_next/locale_id-ID.json | 13 +- options/locale_next/locale_ja-JP.json | 3 +- options/locale_next/locale_ko-KR.json | 3 +- options/locale_next/locale_lv-LV.json | 3 +- options/locale_next/locale_nds.json | 3 +- options/locale_next/locale_nl-NL.json | 3 +- options/locale_next/locale_pt-BR.json | 5 +- options/locale_next/locale_ru-RU.json | 3 +- options/locale_next/locale_sr-SP.json | 4 +- options/locale_next/locale_tr-TR.json | 3 +- options/locale_next/locale_uk-UA.json | 3 +- options/locale_next/locale_vi.json | 4 +- options/locale_next/locale_zh-CN.json | 6 +- options/locale_next/locale_zh-HK.json | 7 +- 61 files changed, 966 insertions(+), 392 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 15d614e8bc..956f9a302a 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -297,7 +297,7 @@ twofa_disabled = عُطِّل الاستيثاق الثنائي. theme_desc = ستكون هذه السمة المبدئية لك عبر الموقع. new_password = كلمة المرور الجديدة twofa_disable_desc = تعطيل الاستيثاق الثنائي سيجعل حسابك أقل أمانًا. أتريد الاستمرار؟ -manage_themes = اختر السمة المبدئية +manage_themes = الموضوع الافتراضي delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عنها بعد ذلك. cancel = ألغ repos_none = ليس لديك أي مستودع. @@ -390,7 +390,7 @@ account = الحساب uploaded_avatar_is_too_big = حجم الملف المرفوع (%d كي‌ب) يتخطى الحجم الأقصى (%d كي‌ب). biography_placeholder = أخبرنا شيئا عن نفسك! (يمكنك استخدام ماركداون) comment_type_group_reference = الإشارات -orgs = إدارة المنظمات +orgs = المنظمات update_profile = حدِّث الملف الشخصي profile = الملف الشخصي comment_type_group_dependency = الاعتماديات @@ -421,7 +421,7 @@ keep_email_private_popup = سيؤدي هذا إلى إخفاء عنوان بري ssh_key_name_used = هناك مفتاح SSH بنفس الاسم موجود بالفعل على حسابك. authorized_oauth2_applications = تطبيقات OAuth2 المأذونة uid = المعرّف الرمزي -manage_openid = إدارة عناوين OpenID +manage_openid = عناوين OpenID webauthn = استيثاق ثنائي (مفاتيح الأمان) comment_type_group_deadline = الموعد النهائي add_key = أضف مفتاح @@ -503,6 +503,8 @@ oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع ك remove_account_link = أزل الحساب المربوط remove_account_link_success = أُزيل الحساب المربوط. +quota = كوتا + [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. settings.delete_prompt = ستزال المنظمة إلى الأبد. لا يمكن التراجع عنها بعد ذلك! @@ -1973,7 +1975,7 @@ component_failed_to_load = حدث خطأ غير متوقع. [search] -org_kind = بحث في المنظمات... +org_kind = بحث في المنظمات… code_search_unavailable = البحث في الكود غير متوفر حاليًا. يرجى الاتصال بمدير الموقع. search = ابحث... type_tooltip = نوع البحث @@ -1981,13 +1983,13 @@ fuzzy = أجعد fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق match = تتناسب match_tooltip = قم بتضمين النتائج التي تطابق مصطلح البحث المحدد فقط -repo_kind = بحث في المستودعات... -user_kind = بحث عن المستخدمين... -team_kind = بحث عن الفرق ... -code_kind = بحث في الكود... -project_kind = البحث ضمن المشاريع... -branch_kind = البحث ضمن الفروع... +repo_kind = بحث في المستودعات… +user_kind = بحث عن المستخدمين… +team_kind = بحث عن الفرق… +code_kind = بحث في الكود… +project_kind = البحث ضمن المشاريع… +branch_kind = البحث ضمن الفروع… no_results = لا توجد نتائج مطابقة. -issue_kind = البحث ضمن الأعطال... -pull_kind = البحث ضمن طلبات السحب... +issue_kind = البحث ضمن الأعطال… +pull_kind = البحث ضمن طلبات السحب… keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 1b9767f674..35e33f4430 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -142,6 +142,8 @@ webauthn_error = Неуспешно прочитане на вашия ключ webauthn_unsupported_browser = Вашият браузър в момента не поддържа WebAuthn. webauthn_error_duplicated = Ключът за сигурност не е разрешен за тази заявка. Моля, уверете се, че ключът не е вече регистриран. +tracked_time_summary = Обобщение на проследеното време въз основа на филтрите в списъка със задачи + [settings] ui = Тема delete_key = Премахване @@ -324,7 +326,7 @@ permissions_list = Разрешения: edit_oauth2_application = Редактиране на OAuth2 приложение remove_oauth2_application = Премахване на OAuth2 приложение twofa_recovery_tip = Ако загубите устройството си, ще можете да използвате ключ за еднократно възстановяване, за да си върнете достъпа до акаунта. -visibility.private_tooltip = Видим само за членове на организации, в които участвате +visibility.private_tooltip = Видим само за участници в организации, в които участвате quota.applies_to_user = Следните правила за квота се прилагат за вашия акаунт quota.rule.no_limit = Неограничена hints = Подсказки @@ -381,6 +383,20 @@ comment_type_group_lock = Състояние на заключване can_not_add_email_activations_pending = Има чакаща активация, опитайте отново след няколко минути, ако искате да добавите нова ел. поща. storage_overview = Преглед на съхранението +webauthn = Двуфакторно удостоверяване (Ключове за сигурност) +quota.sizes.all = Всички +quota.sizes.repos.all = Хранилища +quota.sizes.repos.public = Публични хранилища +quota.sizes.repos.private = Частни хранилища +quota.sizes.git.all = Git съдържание +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.attachments.all = Прикачени файлове +quota.sizes.assets.attachments.issues = Прикачени файлове към задачи +quota.sizes.assets.attachments.releases = Прикачени файлове към издания +quota.sizes.assets.artifacts = Артефакти +quota.sizes.assets.packages.all = Пакети +quota.sizes.wiki = Уики + [packages] container.labels.value = Стойност alpine.repository.repositories = Хранилища @@ -439,6 +455,113 @@ arch.version.conflicts = В конфликт alpine.repository.branches = Клонове arch.pacman.repo.multi.item = Конфигурация за %s +desc = Управление на пакетите на хранилището. +alpine.registry = Настройте този регистър, като добавите URL адреса във вашия файл /etc/apk/repositories: +alpine.registry.key = Изтеглете публичния RSA ключ на регистъра в папката /etc/apk/keys/, за да проверите подписа на индекса: +alpine.registry.info = Изберете $branch и $repository от списъка по-долу. +alpine.install = За да инсталирате пакета, изпълнете следната команда: +arch.version.properties = Свойства на версията +arch.version.makedepends = Зависимости за изграждането +arch.version.checkdepends = Зависимости за проверката +chef.registry = Настройте този регистър във вашия файл ~/.chef/config.rb: +chef.install = За да инсталирате пакета, изпълнете следната команда: +composer.registry = Настройте този регистър във вашия файл ~/.composer/config.json: +composer.install = За да инсталирате пакета с Composer, изпълнете следната команда: +composer.dependencies = Зависимости +conan.details.repository = Хранилище +conan.registry = Настройте този регистър от командния ред: +conan.install = За да инсталирате пакета с Conan, изпълнете следната команда: +conda.registry = Настройте този регистър като Conda хранилище във вашия файл .condarc: +conda.install = За да инсталирате пакета с Conda, изпълнете следната команда: +container.pull = Издърпайте образа от командния ред: +container.multi_arch = ОС / Архитектура +container.layers = Слоеве на образа +cran.registry = Настройте този регистър във вашия файл Rprofile.site: +cran.install = За да инсталирате пакета, изпълнете следната команда: +debian.registry = Настройте този регистър от командния ред: +debian.registry.info = Изберете $distribution и $component от списъка по-долу. +debian.install = За да инсталирате пакета, изпълнете следната команда: +debian.repository = Информация за хранилището +debian.repository.distributions = Дистрибуции +debian.repository.components = Компоненти +debian.repository.architectures = Архитектури +helm.registry = Настройте този регистър от командния ред: +helm.install = За да инсталирате пакета, изпълнете следната команда: +maven.registry = Настройте този регистър във файла на вашия проект pom.xml: +maven.install = За да използвате пакета, включете следното в блока dependencies във файла pom.xml: +maven.install2 = Изпълнете през командния ред: +maven.download = За да изтеглите зависимостта, изпълнете през командния ред: +nuget.registry = Настройте този регистър от командния ред: +nuget.install = За да инсталирате пакета с NuGet, изпълнете следната команда: +nuget.dependency.framework = Целева платформа +npm.registry = Настройте този регистър във файла на вашия проект .npmrc: +npm.install = За да инсталирате пакета с npm, изпълнете следната команда: +npm.install2 = или го добавете във файла package.json: +npm.dependencies.optional = Опционални зависимости +npm.details.tag = Маркер +pub.install = За да инсталирате пакета с Dart, изпълнете следната команда: +pypi.requires = Изисква Python +pypi.install = За да инсталирате пакета с pip, изпълнете следната команда: +rpm.registry = Настройте този регистър от командния ред: +rpm.distros.redhat = на дистрибуции, базирани на RedHat +rpm.distros.suse = на дистрибуции, базирани на SUSE +rpm.install = За да инсталирате пакета, изпълнете следната команда: +rpm.repository = Информация за хранилището +rpm.repository.architectures = Архитектури +rpm.repository.multiple_groups = Този пакет е наличен в няколко групи. +alt.registry = Настройте този регистър от командния ред: +alt.registry.install = За да инсталирате пакета, изпълнете следната команда: +alt.install = Инсталиране на пакет +alt.setup = Добавете хранилище към списъка със свързани хранилища (изберете необходимата архитектура вместо „_arch_“): +alt.repository = Информация за хранилището +alt.repository.architectures = Архитектури +alt.repository.multiple_groups = Този пакет е наличен в няколко групи. +swift.registry = Настройте този регистър от командния ред: +swift.install = Добавете пакета във вашия файл Package.swift: +swift.install2 = и изпълнете следната команда: +vagrant.install = За да добавите Vagrant box, изпълнете следната команда: +settings.link = Свързване на този пакет с хранилище +settings.link.description = Ако свържете пакет с хранилище, пакетът се изброява в списъка с пакети на хранилището. +settings.link.select = Изберете хранилище +settings.link.button = Обновяване на връзката на хранилището +settings.link.success = Връзката на хранилището беше успешно обновена. +settings.link.error = Неуспешно обновяване на връзката на хранилището. +settings.delete.description = Изтриването на пакет е трайно и не може да бъде отменено. +settings.delete.notice = На път сте да изтриете %s (%s). Тази операция е необратима, сигурни ли сте? +owner.settings.cargo.title = Индекс на регистъра на Cargo +owner.settings.cargo.initialize = Инициализиране на индекс +owner.settings.cargo.initialize.description = Необходимо е специално Git хранилище за индекс, за да се използва регистърът на Cargo. Използването на тази опция ще (пре)създаде хранилището и ще го конфигурира автоматично. +owner.settings.cargo.initialize.error = Неуспешно инициализиране на индекса на Cargo: %v +owner.settings.cargo.initialize.success = Индексът на Cargo беше успешно създаден. +owner.settings.cargo.rebuild = Преизграждане на индекс +owner.settings.cargo.rebuild.description = Преизграждането може да бъде полезно, ако индексът не е синхронизиран със съхранените Cargo пакети. +owner.settings.cargo.rebuild.error = Неуспешно преизграждане на индекса на Cargo: %v +owner.settings.cargo.rebuild.success = Индексът на Cargo беше успешно преизграден. +owner.settings.cargo.rebuild.no_index = Не може да се преизгради, няма инициализиран индекс. +owner.settings.cleanuprules.title = Правила за почистване +owner.settings.cleanuprules.add = Добавяне на правило за почистване +owner.settings.cleanuprules.edit = Редактиране на правилото за почистване +owner.settings.cleanuprules.none = Все още няма правила за почистване. +owner.settings.cleanuprules.preview = Преглед на правило за почистване +owner.settings.cleanuprules.preview.overview = %d пакета са насрочени за премахване. +owner.settings.cleanuprules.preview.none = Правилото за почистване не съвпада с нито един пакет. +owner.settings.cleanuprules.enabled = Включено +owner.settings.cleanuprules.pattern_full_match = Прилагане на шаблона към пълното име на пакета +owner.settings.cleanuprules.keep.title = Версиите, които съответстват на тези правила, се запазват, дори ако съответстват на правило за премахване по-долу. +owner.settings.cleanuprules.keep.count = Запазване на най-новите +owner.settings.cleanuprules.keep.count.1 = 1 версия на пакет +owner.settings.cleanuprules.keep.count.n = %d версии на пакет +owner.settings.cleanuprules.keep.pattern = Запазване на версии, съответстващи на +owner.settings.cleanuprules.keep.pattern.container = Версията latest винаги се запазва за Container пакети. +owner.settings.cleanuprules.remove.title = Версиите, които съответстват на тези правила, се премахват, освен ако правило по-горе не казва да се запазят. +owner.settings.cleanuprules.remove.days = Премахване на версии, по-стари от +owner.settings.cleanuprules.remove.pattern = Премахване на версии, съответстващи на +owner.settings.cleanuprules.success.update = Правилото за почистване е обновено. +owner.settings.cleanuprules.success.delete = Правилото за почистване е изтрито. +owner.settings.chef.title = Регистър на Chef +owner.settings.chef.keypair = Генериране на двойка ключове +owner.settings.chef.keypair.description = Заявките, изпратени до регистъра на Chef, трябва да бъдат криптографски подписани като средство за удостоверяване. При генериране на двойка ключове, само публичният ключ се съхранява във Forgejo. Частният ключ ви се предоставя, за да се използва с knife. Генерирането на нова двойка ключове ще презапише предишната. + [tool] hours = %d часа now = сега @@ -940,9 +1063,9 @@ editor.no_changes_to_show = Няма промени за показване. issues.choose.get_started = Първи стъпки issues.change_milestone_at = `промени етапа от %s на %s %s` issues.change_project_at = `промени проекта от %s на %s %s` -issues.self_assign_at = `си само-възложи това %s` +issues.self_assign_at = `си самовъзложи това %s` issues.remove_assignee_at = `е премахнат като изпълнител от %s %s` -issues.remove_self_assignment = `се само-премахна като изпълнител %s` +issues.remove_self_assignment = `се самопремахна като изпълнител %s` issues.add_assignee_at = `му бе възложено това от %s %s` pulls.merged_by = от %[3]s бе слята %[1]s pulls.merged_by_fake = от %[2]s бе слята %[1]s @@ -1272,7 +1395,7 @@ issues.review.show_resolved = Показване на решено issues.review.hide_resolved = Скриване на решено issues.review.resolve_conversation = Решаване на обсъждането diff.comment.markdown_info = Поддържа се стилизиране с Маркдаун. -diff.file_suppressed = Разликите не са показани, защото са твърде много +diff.file_suppressed = Разликите във файла са потиснати, защото са твърде много pulls.reject_count_n = %d поискани промени settings.pulls.default_allow_edits_from_maintainers = Позволяване на редакции от поддържащите по подразбиране fork_branch = Клон за клониране в разклонението @@ -1347,7 +1470,7 @@ settings.default_branch_desc = Изберете стандартен клон з settings.transfer.button = Прехвърляне на притежанието settings.transfer.modal.title = Прехвърляне на притежанието ambiguous_runes_line = `Този ред съдържа двусмислени Уникод знаци` -ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан с %[2]c [U+%04[2]X]` +ambiguous_character = `%[1]c [U+%04[1]X] може да бъде объркан със %[2]c [U+%04[2]X]` invisible_runes_header = `Този файл съдържа невидими Уникод знаци` issues.all_title = Общо issues.new.assign_to_me = Възлагане на мен @@ -1446,7 +1569,7 @@ generated_from = генерирано от clear_ref = `Изчистване на текущата препратка` file_follow = Последване на символната връзка commitstatus.failure = Неуспех -issues.filter_label_exclude = `Използвайте alt + click/enter, за да изключите етикети` +issues.filter_label_exclude = Използвайте Alt + Click, за да изключите етикети migrate.migrating_failed = Мигрирането от %s е неуспешно. migrate.migrating_issues = Мигриране на задачи mirror_from = огледално на @@ -1576,6 +1699,178 @@ issues.force_push_compare = Сравняване pulls.status_checking = Някои проверки са в очакване pulls.nothing_to_compare = Тези клонове са равни. Не е нужно да създавате заявка за сливане. +rss.must_be_on_branch = Трябва да сте на клон, за да имате RSS емисия. +admin.manage_flags = Управление на флаговете +admin.enabled_flags = Флагове, включени за хранилището: +admin.update_flags = Обновяване на флаговете +admin.failed_to_replace_flags = Неуспешна замяна на флаговете на хранилището +admin.flags_replaced = Флаговете на хранилището са заменени +fork_to_different_account = Разклоняване в друг акаунт +mirror_interval = Интервал на огледалото (валидни единици за време са „h“, „m“, „s“). 0 за изключване на периодичната синхронизация. (Минимален интервал: %s) +mirror_interval_invalid = Интервалът на огледалото не е валиден. +mirror_use_ssh.text = Използване на SSH удостоверяване +mirror_use_ssh.helper = Forgejo ще създаде огледало на хранилището чрез Git през SSH и ще генерира двойка ключове за вас, когато изберете тази опция. Трябва да се уверите, че генерираният публичен ключ е упълномощен да изтласква към целевото хранилище. Не можете да използвате удостоверяване, базирано на парола, когато избирате това. +mirror_use_ssh.not_available = SSH удостоверяването не е налично. +mirror_denied_combination = Не може да се използва удостоверяване с публичен ключ и парола едновременно. +mirror_sync_on_commit = Синхронизиране при изтласкване на подавания +mirror_address_desc = Поставете всички необходими данни за удостоверяване в секцията „Упълномощаване“. +mirror_address_url_invalid = Предоставеният URL е невалиден. Трябва да екранирате правилно всички компоненти на URL адреса. +mirror_address_protocol_invalid = Предоставеният URL е невалиден. Само http(s):// или git:// адреси могат да се използват за огледални хранилища. +mirror_lfs = Съхранение на големи файлове (LFS) +mirror_password_help = Променете потребителското име, за да изтриете запазена парола. +unit_disabled = Администраторът на сайта е изключил тази секция на хранилището. +summary_card_alt = Карта с обобщение на хранилище %s +template.items = Елементи на шаблона +template.git_content = Git съдържание (стандартен клон) +template.git_hooks = Git куки +template.git_hooks_tooltip = В момента не можете да променяте или премахвате Git куки, след като са добавени. Изберете това само ако се доверявате на шаблонното хранилище. +template.one_item = Трябва да изберете поне един елемент от шаблона +template.invalid = Трябва да изберете шаблонно хранилище +migrate.cancel_migrating_title = Отказ от миграцията +migrate.cancel_migrating_confirm = Искате ли да откажете тази миграция? +invisible_runes_description = `Този файл съдържа невидими Уникод знаци, които са неразличими за хората, но могат да бъдат обработени по различен начин от компютър. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +ambiguous_runes_header = `Този файл съдържа двусмислени Уникод знаци` +ambiguous_runes_description = `Този файл съдържа Уникод знаци, които могат да бъдат объркани с други знаци. Ако смятате, че това е умишлено, можете спокойно да пренебрегнете това предупреждение. Използвайте бутона „Екраниране“, за да ги разкриете.` +file_copy_permalink = Копиране на постоянна връзка +view_git_blame = Преглед на git blame +video_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „video“. +audio_not_supported_in_browser = Вашият браузър не поддържа HTML5 тага „audio“. +stored_lfs = Съхранено с Git LFS +commit_graph.select = Изберете клонове +editor.cannot_edit_lfs_files = LFS файлове не могат да се редактират в уеб интерфейса. +editor.filename_help = Добавете директория, като въведете името ѝ, последвано от наклонена черта („/“). Премахнете директория, като натиснете backspace в началото на полето за въвеждане. +editor.commit_signed_changes = Подаване на подписани промени +editor.require_signed_commit = Клонът изисква подписано подаване +editor.commit_email = Ел. поща на подаването +commits.desc = Разглеждане на историята на промените в програмния код. +commits.search.tooltip = Можете да добавите префикс към ключовите думи с „author:“, „committer:“, „after:“ или „before:“, напр. „revert author:Alice before:2019-01-13“. +commits.signed_by = Подписано от +commits.signed_by_untrusted_user = Подписано от недоверен потребител +commits.signed_by_untrusted_user_unmatched = Подписано от недоверен потребител, който не съвпада с подаващия +commits.ssh_key_fingerprint = Отпечатък на SSH ключ +commits.view_single_diff = Преглед на промените в този файл, въведени в това подаване +commit.revert = Връщане +commit.revert-header = Връщане: %s +commit.revert-content = Изберете клон, върху който да се върне: +issues.desc = Организирайте доклади за грешки, задачи и етапи. +issues.choose.ignore_invalid_templates = Невалидните шаблони са игнорирани +issues.choose.invalid_config = Конфигурацията на задачите съдържа грешки: +issues.filter_type.all_pull_requests = Всички заявки за сливане +issues.role.member_helper = Този потребител е участник в организацията, притежаваща това хранилище. +issues.lock.unknown_reason = Не може да се заключи задача с неизвестна причина. +issues.lock_duplicate = Задача не може да бъде заключена два пъти. +issues.unlock_error = Не може да се отключи задача, която не е заключена. +issues.lock.notice_1 = - Други потребители не могат да добавят нови коментари към тази задача. +issues.lock.notice_2 = - Вие и други сътрудници с достъп до това хранилище все още можете да оставяте коментари, които другите да виждат. +issues.lock.notice_3 = - Винаги можете да отключите тази задача отново в бъдеще. +issues.unlock.notice_1 = - Всеки ще може отново да коментира тази задача. +issues.unlock.notice_2 = - Винаги можете да заключите тази задача отново в бъдеще. +issues.lock.title = Заключване на обсъждането по тази задача. +issues.unlock.title = Отключване на обсъждането по тази задача. +issues.comment_on_locked = Не можете да коментирате заключена задача. +issues.delete.text = Наистина ли искате да изтриете тази задача? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +issues.cancel_tracking_history = `отмени проследяването на времето %s` +issues.add_time_sum_to_small = Не е въведено време. +issues.due_date_form = гггг-мм-дд +issues.due_date_invalid = Крайният срок е невалиден или извън обхвата. Моля, използвайте формата „гггг-мм-дд“. +issues.dependency.no_permission_1 = Нямате разрешение да прочетете %d зависимост +issues.dependency.no_permission_n = Нямате разрешение да прочетете %d зависимости +issues.dependency.no_permission.can_remove = Нямате разрешение да прочетете тази зависимост, но можете да я премахнете +issues.dependency.issue_batch_close_blocked = Не могат да бъдат затворени групово избраните задачи, защото задача #%d все още има отворени зависимости +issues.dependency.blocked_by_short = Зависи от +issues.dependency.setting = Включване на зависимости за задачи и заявки за сливане +issues.dependency.add_error_same_issue = Не можете да направите задача зависима от самата нея. +issues.dependency.add_error_dep_issue_not_exist = Зависимата задача не съществува. +issues.dependency.add_error_cannot_create_circular = Не можете да създадете зависимост с две задачи, които се блокират взаимно. +issues.dependency.add_error_dep_not_same_repo = И двете задачи трябва да са в едно и също хранилище. +issues.review.self.rejection = Не можете да поискате промени в собствената си заявка за сливане. +issues.review.dismissed = отхвърли рецензията на %s %s +issues.review.content.empty = Трябва да оставите коментар, посочващ исканите промени. +issues.review.add_review_requests = поиска рецензии от %[1]s %[2]s +issues.review.remove_review_request = премахна заявката за рецензия за %[1]s %[2]s +issues.review.remove_review_requests = премахна заявките за рецензия за %[1]s %[2]s +issues.review.remove_review_request_self = отказа да рецензира %s +issues.review.pending.tooltip = Този коментар в момента не е видим за други потребители. За да изпратите изчакващите си коментари, изберете „%s“ -> „%s/%s/%s“ в горната част на страницата. +issues.review.outdated = Остарял +issues.review.outdated_description = Съдържанието е променено, след като е направен този коментар +issues.review.show_outdated = Показване на остарели +issues.review.hide_outdated = Скриване на остарели +issues.content_history.options = Опции +issues.blocked_by_user = Не можете да създавате задачи в това хранилище, защото сте блокирани от притежателя на хранилището. +comment.blocked_by_user = Коментирането не е възможно, защото сте блокирани от притежателя на хранилището или от автора. +issues.reopen.blocked_by_user = Не можете да отворите наново тази задача, защото сте блокирани от притежателя на хранилището или от автора на тази задача. +compare.compare_base = основа +compare.compare_head = сравняване +pulls.desc = Включване на заявки за сливане и рецензии на код. +pulls.view = Преглед на заявката за сливане +pulls.allow_edits_from_maintainers_desc = Потребители с право на запис в основния клон могат също да изтласкват към този клон +pulls.allow_edits_from_maintainers_err = Обновяването е неуспешно +pulls.has_changed_since_last_review = Променено след последната ви рецензия +pulls.switch_comparison_type = Превключване на типа сравнение +pulls.filter_branch = Филтриране на клон +pulls.review_only_possible_for_full_diff = Рецензирането е възможно само при преглед на пълните разлики +pulls.wrong_commit_id = ID на подаването трябва да бъде ID на подаване в целевия клон +pulls.blocked_by_user = Не можете да създадете заявка за сливане в това хранилище, защото сте блокирани от притежателя на хранилището. +pulls.no_merge_desc = Тази заявка за сливане не може да бъде слята, защото всички опции за сливане в хранилището са изключени. +pulls.no_merge_helper = Включете опциите за сливане в настройките на хранилището или слейте заявката за сливане ръчно. +pulls.no_merge_wip = Тази заявка за сливане не може да бъде слята, защото е отбелязана като в процес на работа. +pulls.squash_merge_pull_request = Създаване на сплескано подаване +pulls.merge_manually = Ръчно слята +pulls.merge_commit_id = ID на подаването със сливане +pulls.require_signed_wont_sign = Клонът изисква подписани подавания, но това сливане няма да бъде подписано +pulls.merge_conflict = Сливането е неуспешно: Възникна конфликт по време на сливането. Подсказка: Опитайте различна стратегия +pulls.merge_conflict_summary = Съобщение за грешка +pulls.rebase_conflict_summary = Съобщение за грешка +pulls.has_merged = Неуспешно: Заявката за сливане е слята, не можете да слеете отново или да промените целевия клон. +pulls.push_rejected = Изтласкването е неуспешно: Изтласкването е отхвърлено. Прегледайте Git куките за това хранилище. +pulls.push_rejected_no_message = Изтласкването е неуспешно: Изтласкването е отхвърлено, но няма отдалечено съобщение. Прегледайте Git куките за това хранилище +pulls.update_not_allowed = Нямате разрешение да обновявате клона +pulls.outdated_with_base_branch = Този клон е остарял спрямо основния клон +pulls.cmd_instruction_merge_warning = Предупреждение: Настройката „Автоматично откриване на ръчно сливане“ не е включена за това хранилище, ще трябва да отбележите тази заявка за сливане като ръчно слята след това. +pulls.editable_explanation = Тази заявка за сливане позволява редакции от поддържащите. Можете да допринесете директно към нея. +pulls.auto_merge_button_when_succeed = (Когато проверките са успешни) +pulls.auto_merge_when_succeed = Автоматично сливане, когато всички проверки са успешни +pulls.auto_merge_newly_scheduled = Заявката за сливане е насрочена за сливане, когато всички проверки са успешни. +pulls.auto_merge_has_pending_schedule = %[1]s насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[2]s. +pulls.auto_merge_cancel_schedule = Отмяна на автоматичното сливане +pulls.auto_merge_not_scheduled = Тази заявка за сливане не е насрочена за автоматично сливане. +pulls.auto_merge_canceled_schedule = Автоматичното сливане е отменено за тази заявка за сливане. +pulls.auto_merge_newly_scheduled_comment = `насрочи тази заявка за сливане за автоматично сливане, когато всички проверки са успешни %[1]s` +pulls.auto_merge_canceled_schedule_comment = `отмени автоматичното сливане на тази заявка за сливане, когато всички проверки са успешни %[1]s` +pulls.delete.title = Да се изтрие ли тази заявка за сливане? +pulls.delete.text = Наистина ли искате да изтриете тази заявка за сливане? (Това ще премахне трайно цялото съдържание. Помислете дали вместо това да не я затворите, ако възнамерявате да я запазите архивирана) +diff.data_not_available = Съдържанието на разликите не е налично +diff.bin = ДВОИЧЕН +diff.file_suppressed_line_too_long = Разликите във файла са потиснати, защото един или повече редове са твърде дълги +diff.too_many_files = Някои файлове не бяха показани, защото твърде много файлове имат промени в тези разлики +diff.show_more = Показване на още +diff.generated = генериран +diff.comment.add_line_comment = Добавяне на коментар към ред +diff.comment.add_review_comment = Добавяне на коментар +diff.review.self_reject = Авторите на заявки за сливане не могат да поискват промени в собствените си заявки +diff.review.self_approve = Авторите на заявки за сливане не могат да одобряват собствените си заявки +diff.image.side_by_side = Едно до друго +diff.image.swipe = Плъзгане +diff.image.overlay = Наслагване +diff.has_escaped = Този ред има скрити Уникод знаци +release.tag_name_protected = Името на маркера е защитено. +release.add_tag_msg = Използване на заглавието и съдържанието на изданието като съобщение на маркера. +release.hide_archive_links = Скриване на автоматично генерираните архиви +release.hide_archive_links_helper = Скрийте автоматично генерираните архиви с програмен код за това издание. Например, ако качвате свои собствени. +release.asset_external_url = Външен URL адрес +release.summary_card_alt = Карта с обобщение на издание със заглавие „%s“ в хранилище %s +branch.protected_deletion_failed = Клонът „%s“ е защитен. Не може да бъде изтрит. +branch.default_deletion_failed = Клонът „%s“ е стандартният клон. Не може да бъде изтрит. +branch.included_desc = Този клон е част от стандартния клон +branch.included = Включен +branch.warning_rename_default_branch = Преименувате стандартния клон. +topic.count_prompt = Не можете да изберете повече от 25 теми +find_file.no_matching = Не е намерен съвпадащ файл +error.csv.too_large = Не може да се визуализира този файл, защото е твърде голям. +error.csv.unexpected = Не може да се визуализира този файл, защото съдържа неочакван знак на ред %d и колона %d. +error.csv.invalid_field_count = Не може да се визуализира този файл, защото има грешен брой полета на ред %d. +error.broken_git_hook = Git куките на това хранилище изглеждат повредени. Моля, последвайте документацията, за да ги поправите, след което изтласкайте подавания, за да обновите статуса. + [modal] confirm = Потвърждаване no = Не @@ -1663,18 +1958,18 @@ follow_blocked_user = Не можете да следвате тази орга settings.delete_prompt = Организацията ще бъде премахната завинаги. Това НЕ МОЖЕ да бъде отменено! settings.labels_desc = Добавете етикети, които могат да се използват за задачи за всички хранилища в тази организация. teams.none_access = Без достъп -teams.members.none = Няма членове в този екип. +teams.members.none = Няма участници в този екип. repo_updated = Обновено %s teams.delete_team_success = Екипът е изтрит. teams.search_repo_placeholder = Потърсете хранилище… teams.delete_team_title = Изтриване на екипа -teams.add_team_member = Добавяне на член на екипа +teams.add_team_member = Добавяне на участник в екипа teams.read_access_helper = Членовете могат да преглеждат и клонират хранилищата на екипа. teams.invite.description = Моля, щракнете върху бутона по-долу, за да се присъедините към екипа. teams.invite.title = Поканени сте да се присъедините към екип %s в организация %s. team_permission_desc = Разрешение members.public_helper = Да е скрит -teams.members = Членове на екипа +teams.members = Участници в екипа teams.delete_team = Изтриване на екипа members.owner = Притежател members.member_role = Роля на участника: @@ -1685,6 +1980,38 @@ settings.delete_org_desc = Тази организация ще бъде изт open_dashboard = Отваряне на таблото settings.change_orgname_prompt = Бележка: Промяната на името на организацията ще промени и URL адреса на вашата организация и ще освободи старото име. +team_access_desc = Достъп до хранилище +team_unit_desc = Разрешаване на достъп до секции на хранилището +team_unit_disabled = (Изключено) +form.name_reserved = Името на организацията „%s“ е резервирано. +form.name_pattern_not_allowed = Шаблонът „%s“ не е разрешен в име на организация. +form.create_org_not_allowed = Нямате разрешение да създавате организация. +settings.update_setting_success = Настройките на организацията са обновени. +settings.change_orgname_redirect_prompt = Старото име ще се пренасочва, докато не бъде взето. +settings.change_orgname_redirect_prompt.with_cooldown.one = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d ден. Все още можете да си върнете старото име по време на периода на изчакване. +settings.change_orgname_redirect_prompt.with_cooldown.few = Старото име на организацията ще бъде достъпно за всички след период на изчакване от %[1]d дни. Все още можете да си върнете старото име по време на периода на изчакване. +settings.update_avatar_success = Профилната снимка на организацията е обновена. +settings.hooks_desc = Добавете уеб-куки, които ще се задействат за всички хранилища в тази организация. +members.membership_visibility = Видимост на участничеството: +members.public = Видим +members.private = Скрит +members.invite_desc = Добавяне на нов участник към %s: +members.invite_now = Поканване сега +teams.admin_access = Администраторски достъп +teams.invite_team_member = Поканване в %s +teams.invite_team_member.list = Чакащи покани +teams.delete_team_desc = Изтриването на екип отнема достъпа до хранилището от неговите участници. Продължаване? +teams.remove_all_repos_desc = Това ще премахне всички хранилища от екипа. +teams.add_all_repos_title = Добавяне на всички хранилища +teams.add_all_repos_desc = Това ще добави всички хранилища на организацията към екипа. +teams.add_nonexistent_repo = Хранилището, което се опитвате да добавите, не съществува, моля, първо го създайте. +teams.add_duplicate_users = Потребителят вече е участник в екипа. +teams.repos.none = Няма хранилища, до които този екип да има достъп. +teams.specific_repositories = Конкретни хранилища +teams.specific_repositories_helper = Участниците ще имат достъп само до хранилища, изрично добавени към екипа. Избирането на това няма автоматично да премахне хранилища, вече добавени с Всички хранилища. +teams.all_repositories_helper = Екипът има достъп до всички хранилища. Избирането на това ще добави всички съществуващи хранилища към екипа. +teams.invite.by = Поканен от %s + [install] admin_password = Парола user = Потребителско име @@ -1978,14 +2305,14 @@ Pronouns = Местоимения Biography = Биография Website = Уебсайт Location = Местоположение -cannot_add_org_to_team = Организация не може да бъде добавена като член на екип. +cannot_add_org_to_team = Организация не може да бъде добавена като участник в екип. auth_failed = Неуспешно удостоверяване: %v team_no_units_error = Разрешете достъп до поне една секция на хранилището. password_uppercase_one = Поне един голям знак CommitSummary = Обобщение на подаването username_error = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“), долна черта („_“) и точка („.“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` username_error_no_dots = ` може да съдържа само буквено-цифрови знаци („0-9“, „a-z“, „A-Z“), тире („-“) и долна черта („_“). Не може да започва или завършва с не-буквено-цифрови знаци, като също така са забранени и последователни не-буквено-цифрови знаци.` -duplicate_invite_to_team = Потребителят вече е поканен като член на екипа. +duplicate_invite_to_team = Потребителят вече е поканен като участник в екипа. must_use_public_key = Ключът, който предоставихте, е частен ключ. Моля, не качвайте частния си ключ никъде. Вместо това използвайте публичния си ключ. org_still_own_packages = Тази организация все още притежава един или повече пакети, първо ги изтрийте. admin_cannot_delete_self = Не можете да изтриете себе си, когато сте администратор. Моля, първо премахнете администраторските си привилегии. @@ -2006,7 +2333,7 @@ enterred_invalid_repo_name = Името на хранилището, което enterred_invalid_org_name = Името на организацията, което въведохте, е неправилно. enterred_invalid_password = Паролата, която въведохте, е неправилна. organization_leave_success = Успешно напуснахте организацията %s. -still_has_org = Вашият акаунт е член на една или повече организации, първо ги напуснете. +still_has_org = Вашият акаунт е участник в една или повече организации, първо ги напуснете. org_still_own_repo = Тази организация все още притежава едно или повече хранилища, първо ги изтрийте или прехвърлете. target_branch_not_exist = Целевият клон не съществува. glob_pattern_error = ` glob шаблонът е невалиден: %s.` @@ -2017,6 +2344,18 @@ AdminEmail = Администраторски адрес за ел. поща email_domain_is_not_allowed = Домейнът на адреса за ел. поща на потребителя %s е в конфликт с EMAIL_DOMAIN_ALLOWLIST или EMAIL_DOMAIN_BLOCKLIST. Уверете се, че сте въвели правилно адреса за ел. поща. email_been_used = Адресът за ел. поща вече се използва. +NewBranchName = Име на новия клон +git_ref_name_error = ` трябва да е правилно форматирано име на Git препратка.` +regex_pattern_error = ` шаблонът на регулярния израз е невалиден: %s.` +repository_files_already_exist = Вече съществуват файлове за това хранилище. Свържете се със системния администратор. +repository_files_already_exist.delete = Вече съществуват файлове за това хранилище. Трябва да ги изтриете. +enterred_invalid_owner_name = Името на новия притежател не е валидно. +last_org_owner = Не можете да премахнете последния потребител от екипа на „притежателите“. Трябва да има поне един притежател за организация. +invalid_ssh_key = Не може да се потвърди вашият SSH ключ: %s +invalid_gpg_key = Не може да се потвърди вашият GPG ключ: %s +unable_verify_ssh_key = Не може да се потвърди SSH ключът, проверете го отново за грешки. +required_prefix = Въведеният текст трябва да започва с „%s“ + [action] close_issue = `затвори задача %[3]s#%[2]s` rename_repo = преименува хранилище от %[1]s на %[3]s @@ -2045,6 +2384,12 @@ compare_branch = Сравняване compare_commits_general = Сравняване на подавания compare_commits = Сравнете %d подавания +transfer_repo = прехвърли хранилище %s към %s +mirror_sync_push = синхронизира подавания към %[3]s на %[4]s от огледало +mirror_sync_create = синхронизира нова препратка %[3]s към %[4]s от огледало +mirror_sync_delete = синхронизира и изтри препратка %[2]s на %[3]s от огледало +review_dismissed = `отхвърли рецензия от %[4]s за %[3]s#%[2]s` + [auth] tab_openid = OpenID openid_connect_submit = Свързване @@ -2178,6 +2523,15 @@ variables.not_found = Променливата не е открита. variables.id_not_exist = Променлива с идентификатор %d не съществува. runners.owner_type = Тип +unit.desc = Управление на интегрирани CI/CD pipelines с Forgejo Actions. +status.unknown = Неизвестно +status.waiting = Изчаква се +status.running = Изпълнява се +status.success = Успешно +status.failure = Неуспешно +status.cancelled = Отменено +status.skipped = Пропуснато + [heatmap] less = По-малко number_of_contributions_in_the_last_12_months = %s приноса през последните 12 месеца @@ -2258,3 +2612,32 @@ eib = ЕиБ [translation_meta] test = окей + +[repo.permissions] +code.read = Четене: Достъп и клониране на кода на хранилището. +code.write = Писане: Изтласкване към хранилището, създаване на клонове и маркери. +issues.read = Четене: Четене и създаване на задачи и коментари. +issues.write = Писане: Затваряне на задачи и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +pulls.read = Четене: Четене и създаване на заявки за сливане. +pulls.write = Писане: Затваряне на заявки за сливане и управление на метаданни като етикети, етапи, изпълнители, крайни срокове и зависимости. +releases.read = Четене: Преглед и изтегляне на издания. +wiki.read = Четене: Четене на интегрираното уики и неговата история. +wiki.write = Писане: Създаване, обновяване и изтриване на страници в интегрираното уики. +projects.read = Четене: Достъп до проектните табла на хранилището. +projects.write = Писане: Създаване и редактиране на проекти и колони. + +[gpg] +default_key = Подписано с ключ по подразбиране +error.extract_sign = Неуспешно извличане на подпис +error.generate_hash = Неуспешно генериране на хеш на подаването +error.no_committer_account = Няма акаунт, свързан с адреса за ел. поща на подаващия +error.no_gpg_keys_found = Не е намерен известен ключ за този подпис в базата данни +error.not_signed_commit = Не е подписано подаване +error.failed_retrieval_gpg_keys = Неуспешно извличане на ключ, свързан с акаунта на подаващия +error.probable_bad_signature = ВНИМАНИЕ! Въпреки че има ключ с това ID в базата данни, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. +error.probable_bad_default_signature = ВНИМАНИЕ! Въпреки че ключът по подразбиране има това ID, той не потвърждава това подаване! Това подаване е ПОДОЗРИТЕЛНО. + +[units] +unit = Елемент +error.no_unit_allowed_repo = Нямате разрешение за достъп до никоя секция на това хранилище. +error.unit_not_allowed = Нямате разрешение за достъп до тази секция на хранилището. diff --git a/options/locale/locale_ca.ini b/options/locale/locale_ca.ini index 9cb7d5e50c..ea2af3b645 100644 --- a/options/locale/locale_ca.ini +++ b/options/locale/locale_ca.ini @@ -153,26 +153,26 @@ fuzzy = Difusa search = Cerca... type_tooltip = Tipus de cerca fuzzy_tooltip = Inclou resultats que s'assemblen al terme de la cerca -repo_kind = Cerca repos... -user_kind = Cerca usuaris... +repo_kind = Cerca repos… +user_kind = Cerca usuaris… code_search_unavailable = La cerca de codi no està disponible actualment. Si us plau concteu amb l'administrador del lloc. code_search_by_git_grep = Els resultats actuals de la cerca de codi són proporcionats per "git grep". Podríen haver-hi millors resultats si l'administrador del lloc habilita l'indexador de codi. -package_kind = Cerca paquets... -project_kind = Cerca projectes... -branch_kind = Cerca branques... -commit_kind = Cerca commits... -runner_kind = Cerca executors... +package_kind = Cerca paquets… +project_kind = Cerca projectes… +branch_kind = Cerca branques… +commit_kind = Cerca commits… +runner_kind = Cerca executors… no_results = Cap resultat coincident trobat. keyword_search_unavailable = La cerca per paraula clau no està disponible ara mateix. Si us plau contacteu amb l'administrador del lloc. union = Paraules clau union_tooltip = Inclou resultats que encaixen amb qualsevol paraula clau separada per espais -org_kind = Cerca organitzacions... -team_kind = Cerca teams... -code_kind = Cerca codi... -pull_kind = Cerca "pulls"... +org_kind = Cerca organitzacions… +team_kind = Cerca teams… +code_kind = Cerca codi… +pull_kind = Cerca "pulls"… exact = Exacte exact_tooltip = Inclou només resultats que són exactament el terme de cerca -issue_kind = Cerca problemes... +issue_kind = Cerca problemes… regexp = RegExp regexp_tooltip = Interpreta el terme de cerca com una expressió regular diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 168965a740..30fa95e6be 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1556,7 +1556,7 @@ issues.label_templates.info=Zatím nebyly vytvořeny žádné štítky. Vytvořt issues.label_templates.helper=Vyberte přednastavené značky issues.label_templates.use=Použít přednastavené štítky issues.label_templates.fail_to_load_file=Nepodařilo se načíst soubor šablony popisku „%s“: %v -issues.add_label=přidal/a %s štítek %s +issues.add_label=přidal/a štítek %s %s issues.add_labels=přidal/a %s štítky %s issues.remove_label=odstranil/a %s štítek %s issues.remove_labels=odstranil/a %s štítky %s @@ -1763,7 +1763,7 @@ issues.error_modifying_due_date=Změna termínu dokončení selhala. issues.error_removing_due_date=Odstranění termínu dokončení selhalo. issues.push_commit_1=přidal/a %d revizi %s issues.push_commits_n=přidal/a %d revize %s -issues.force_push_codes=`vynucené nahrání %[1]s od %[2]s do %[4]s %[6]s` +issues.force_push_codes=`vynutil/a nahrání %[1]s od %[2]s do %[4]s %[6]s` issues.force_push_compare=Porovnat issues.due_date_form=rrrr-mm-dd issues.due_date_form_add=Přidat termín dokončení @@ -1813,7 +1813,7 @@ issues.review.approve=schválil/a tyto změny %s issues.review.comment=posoudil/a %s issues.review.dismissed=zamítl/a posouzení uživatele %s %s issues.review.dismissed_label=Zamítnuto -issues.review.left_comment=zanechal komentář +issues.review.left_comment=zanechal/a komentář issues.review.content.empty=Je potřeba zanechat poznámku s uvedením požadované změny (požadovaných změn). issues.review.reject=požádal/a o změny %s issues.review.wait=byl/a požádán/a o posouzení %s diff --git a/options/locale/locale_da.ini b/options/locale/locale_da.ini index c82779ab60..99b5789de3 100644 --- a/options/locale/locale_da.ini +++ b/options/locale/locale_da.ini @@ -1,6 +1,3 @@ - - - [common] home = Hjem dashboard = Instrumentpanel @@ -978,8 +975,8 @@ delete_with_all_comments = Din konto er yngre end %s. For at undgå spøgelsesko delete_account_title = Slet brugerkonto user_block_yourself = Du kan ikke blokere dig selv. pronouns_custom_label = Brugerdefinerede stedord -change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. -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. +change_username_redirect_prompt.with_cooldown.one = Det gamle brugernavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, år. Du kan stadig kræve det gamle brugernavn tilbage i nedkølingsperioden. +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, år. 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 @@ -1520,13 +1517,13 @@ issues.add_labels = tilføjede %s etiketterne %s issues.add_remove_labels = tilføjede %s og fjernede %s etiketter %s issues.add_milestone_at = `føjede dette til %s milepælen %s` issues.add_project_at = `føjede dette til %s- projektet %s` -issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne den, %[1]s` +issues.ref_reopening_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil genåbne det, %[1]s` issues.ref_closed_from = `lukkede dette problem %[4]s %[2 ]s` issues.ref_reopened_from = `genåbnede dette problem %[4]s %[2 ]s` issues.ref_from = `fra %[1]s` issues.author = Forfatter issues.commit_ref_at = `henviste til dette problem fra en commit %s` -issues.ref_issue_from = `henviste til dette problem %[3]s %[2 ]s` +issues.ref_issue_from = `henviste til dette problem %[3]s %[1]s` issues.ref_pull_from = `henviste til denne pull-anmodning %[3]s %[1]s` issues.ref_closing_from = `henviste til dette problem fra en pull-anmodning %[3]s, der vil lukke det, %[1]s` issues.author.tooltip.issue = Denne bruger er forfatteren til dette problem. @@ -1582,7 +1579,7 @@ issues.change_ref_at = `ændret reference fra %s til issues.remove_ref_at = `fjernet reference %s %s` issues.add_ref_at = `tilføjet reference %s %s` issues.delete_branch_at = `slettet gren %s %s` -issues.filter_label_exclude = `Brug alt + klik/enter for at ekskludere etiketter` +issues.filter_label_exclude = Brug Alt + klik for at ekskludere etiketter issues.filter_milestone = Milepæl issues.filter_milestone_all = Alle milepæle issues.filter_milestone_none = Ingen milepæle @@ -2833,8 +2830,8 @@ team_permission_desc = Tilladelse members.member = Medlem settings.change_orgname_prompt = Bemærk: Ændring af organisationens navn vil også ændre din organisations URL og frigøre det gamle navn. settings.change_orgname_redirect_prompt = Det gamle navn vil omdirigere, indtil det gøres krav på. -settings.change_orgname_redirect_prompt.with_cooldown.one = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, du kan stadig kræve det gamle navn tilbage i nedkølingsperioden. -settings.change_orgname_redirect_prompt.with_cooldown.few = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, du kan stadig kræve det gamle navn tilbage i . +settings.change_orgname_redirect_prompt.with_cooldown.one = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dag, år. Du kan stadig kræve det gamle navn tilbage i nedkølingsperioden. +settings.change_orgname_redirect_prompt.with_cooldown.few = Det gamle organisationsnavn vil være tilgængeligt for alle efter en nedkølingsperiode på %[1]d dage, år. Du kan stadig kræve det gamle navn tilbage i. settings.update_avatar_success = Organisationens avatar er blevet opdateret. members.public_helper = Gør skjult members.private = Skjult diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f8bfc9258a..35d1dfddaf 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -752,7 +752,7 @@ organization=Organisationen uid=UID webauthn=Hardware-Sicherheitsschlüssel -public_profile=Öffentliches Profil +public_profile=Öffentliches profil biography_placeholder=Erzähle anderen ein wenig über dich selbst! (Markdown wird unterstützt) location_placeholder=Teile deinen ungefähren Standort mit anderen profile_desc=Über dich diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 398a0d9ce4..41c55beb11 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -3939,12 +3939,12 @@ submodule=Υπομονάδα [search] code_search_unavailable = Η αναζήτηση κώδικα δεν είναι επί του παρόντος διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή σας. keyword_search_unavailable = Η αναζήτηση με την χρήση λέξεων-κλειδιών δεν είναι επί του παρόντος διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή σας. -runner_kind = Αναζήτηση runner... +runner_kind = Αναζήτηση runner… code_search_by_git_grep = Για την αναζήτηση κώδικα, χρησιμοποιείται η εντολή «git grep». Ίσως να παρουσιαστούν καλύτερα αποτελέσματα, αν ο διαχειριστής σας ενεργοποιήσει ένα ευρετήριο για αποθετήρια («Repository Indexer»). package_kind = Αναζήτηση πακέτων… -project_kind = Αναζήτηση έργων... +project_kind = Αναζήτηση έργων… branch_kind = Αναζήτηση κλάδων… -commit_kind = Αναζήτηση commit... +commit_kind = Αναζήτηση commit… no_results = Δεν βρέθηκαν κατάλληλα αποτελέσματα. search = Αναζήτηση… type_tooltip = Είδος αναζήτησης @@ -3958,8 +3958,8 @@ org_kind = Αναζήτηση οργανισμών… team_kind = Αναζήτηση ομαδών… code_kind = Αναζήτηση κώδικα… exact_tooltip = Να συμπεριληφθούν μόνο αποτελέσματα που ταιριάζουν με τον όρο αναζήτησης -issue_kind = Αναζήτηση ζητημάτων... -pull_kind = Αναζήτηση pull... +issue_kind = Αναζήτηση ζητημάτων… +pull_kind = Αναζήτηση pull… exact = Ακριβής milestone_kind = Αναζήτηση ορόσημων... union = Ένωση diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index 6393765d63..5a06120e9e 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -630,7 +630,7 @@ account = Konto ssh_gpg_keys = SSH / GPG-ŝlosiloj twofa_disable = Malaktivigi duoblan aŭtentikigon twofa_enroll = Ŝalti duoblan aŭtentikigon -orgs = Mastrumi organizaĵojn +orgs = Organizaĵoj blocked_users = Blokitaj uzantoj profile = Profilo ui = Haŭto @@ -686,7 +686,7 @@ verify_ssh_key_success = SSH-ŝlosilo «%s» jam konfirmiĝis. edit_oauth2_application = Redakti OAuth2-programon gpg_key_deletion = Forigi GPG-ŝlosilon gpg_key_matched_identities = Akordaj identecoj: -manage_themes = Elekti implicitan haŭton +manage_themes = Defaŭlta temo ssh_key_deletion = Forigi SSH-ŝlosilon key_state_desc = Ĉi tiu ŝlosilo uziĝis dum la pasintaj 7 tagoj valid_forever = Validos dumĉiame @@ -700,7 +700,7 @@ primary = Ĉefa ssh_disabled = SSH malaktivigita update_avatar_success = Via profilbildo konserviĝis. keep_email_private = Kaŝi retpoŝtadreson -manage_openid = Mastrumi OpenID-adresojn +manage_openid = OpenID-adresoj delete_current_avatar = Forigi nunan profilbildon email_preference_set_success = Retpoŝta prefero konserviĝis sukcese. permissions_access_all = Ĉiu (publika, privata, kaj limigita) @@ -884,9 +884,9 @@ commit_kind = Serĉi enmetojn… no_results = Ne trovis kongruantajn rezultojn. exact = Ĝusta exact_tooltip = Inkluzivas nur rezultojn kongruantajn kun la ĝustaj serĉoterminoj -issue_kind = Serĉi erarojn... +issue_kind = Serĉi erarojn… regexp_tooltip = Interpretas la serĉoterminoj kiel regulesprimo fuzzy = Svaga branch_kind = Serĉi disbranĉigojn… -runner_kind = Serĉi rulantojn... -pull_kind = Serĉi tirpetojn... \ No newline at end of file +runner_kind = Serĉi rulantojn… +pull_kind = Serĉi tirpetojn… \ No newline at end of file diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index bdafba93b4..c783b1605b 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -740,7 +740,7 @@ avatar=Avatar ssh_gpg_keys=Claves SSH / GPG social=Redes sociales applications=Aplicaciones -orgs=Administrar organizaciones +orgs=Organizaciones repos=Repositorios delete=Eliminar cuenta twofa=Autenticación de dos factores (TOTP) @@ -1075,8 +1075,8 @@ keep_pronouns_private = Mostrar pronombres solo a personas autenticadas storage_overview = Resumen del almacenamiento quota.sizes.assets.artifacts = Artefactos quota.sizes.assets.attachments.releases = Archivos adjuntos del lanzamiento -change_username_redirect_prompt.with_cooldown.few = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d días, aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. -change_username_redirect_prompt.with_cooldown.one = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d día, aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. +change_username_redirect_prompt.with_cooldown.few = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d días. Aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. +change_username_redirect_prompt.with_cooldown.one = El antiguo nombre de usuario estará disponible para todos después un periodo de tiempo de espera de %[1]d día. Aún puedes reclamar el antiguo nombre de usuario durante el periodo de tiempo de espera. quota.rule.exceeded = Excedido quota.rule.no_limit = Ilimitado quota.sizes.assets.all = Activos @@ -1574,7 +1574,7 @@ issues.remove_ref_at=`eliminó la referencia %s %s` issues.add_ref_at=`añadió la referencia %s %s` issues.delete_branch_at=`eliminó la rama %s %s` issues.filter_label=Etiqueta -issues.filter_label_exclude=`Usa alt + clic/enter para excluir etiquetas` +issues.filter_label_exclude=Usa Alt + Click para excluir etiquetas issues.filter_label_no_select=Todas las etiquetas issues.filter_label_select_no_label=Sin etiqueta issues.filter_milestone=Hito @@ -2888,6 +2888,9 @@ summary_card_alt = Tarjeta de resumen del repositorio %s settings.pull_mirror_sync_quota_exceeded = Cuota excedida, no se empujan los cambios. archive.nocomment = No es posible hacer comentarios porque el repositorio está archivado. +sync_fork.branch_behind_one = Esta rama esta %[1]d cambios detrás de %[2]s +sync_fork.branch_behind_few = Esta rama está %[1]d confirmaciones detrás de %[2]s + [graphs] component_loading = Cargando %s… component_loading_failed = No se pudo cargar %s diff --git a/options/locale/locale_et.ini b/options/locale/locale_et.ini index e54ceadbb5..441a7d8e07 100644 --- a/options/locale/locale_et.ini +++ b/options/locale/locale_et.ini @@ -108,7 +108,7 @@ never = Mitte kunagi unknown = Teadmata rss_feed = RSS infovoog confirm_delete_artifact = Kas oled kindel et soovite artefakti "%s" kustutada? -pin = +pin = artifacts = Artefaktid archived = Arhiveeritud concept_system_global = Ülemaailmne @@ -145,25 +145,25 @@ fuzzy_tooltip = Lisage tulemused mis vastavad ka otsingu terminile union = Märksõnad exact = Täpne exact_tooltip = Sisaldab ainult tulemusi mis vastavad täpsele otsingusõnale -repo_kind = Otsi hoidlad... -user_kind = Otsi kasutajaid... -org_kind = Otsi organisatsioone... -team_kind = Otsi meeskonnad... -code_kind = Otsi koodi... +repo_kind = Otsi hoidlad… +user_kind = Otsi kasutajaid… +org_kind = Otsi organisatsioone… +team_kind = Otsi meeskonnad… +code_kind = Otsi koodi… code_search_by_git_grep = Praeguse koodi otsingu tulemused annab "git grep". Paremaid tulemusi võib saada, kui saidi administraator lubab koodi indekseerija. -package_kind = Otsi pakette... -project_kind = Otsi projekte... -branch_kind = Otsi harusid... -commit_kind = Otsi kommiteid... -runner_kind = Otsi jooksjaid... +package_kind = Otsi pakette… +project_kind = Otsi projekte… +branch_kind = Otsi harusid… +commit_kind = Otsi kommiteid… +runner_kind = Otsi jooksjaid… no_results = Sobivaid tulemusi ei leitud. -issue_kind = Otsi probleeme... +issue_kind = Otsi probleeme… milestone_kind = Otsi verstapostid... type_tooltip = Otsingu tüüp code_search_unavailable = Koodide otsing ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. union_tooltip = Sisaldab tulemused mis vastavad mis tahes tühikutega eraldatud võtmesõnadele keyword_search_unavailable = Otsing märksõna järgi ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. -pull_kind = Otsi tõmbepäringuid... +pull_kind = Otsi tõmbepäringuid… [aria] navbar = Navigatsiooniriba diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index dae0695495..23cb74f814 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -608,7 +608,7 @@ avatar=آواتار ssh_gpg_keys=کلید‌های SSH / GPG social=حساب های اجتماعی applications=برنامه‌ها -orgs=مدیریت سازمان‌ها +orgs=سازمان‌ها repos=مخازن delete=حذف حساب کاربری twofa=احراز هویت دوگانه @@ -653,8 +653,8 @@ password_change_disabled=کاربران غیر محلی نمیتوانند گذ emails=نشانی‌های ایمیل manage_emails=مدیریت نشانی‌های ایمیل -manage_themes=تم پیش فرض را انتخاب کنید -manage_openid=مدیریت نشانی‌های OpenID +manage_themes=تم پیش فرض +manage_openid=آدرس‌های OpenID theme_desc=این پوشته پیش فرض شما در سراسر سایت می باشد. primary=اصلی activated=فعال شده @@ -2766,23 +2766,23 @@ search = جستجو... fuzzy = درهم fuzzy_tooltip = پیامدهایی را درج کنید که دقیقا با عبارت جستجو همخوانی داشته باشند regexp = عبارات باقاعده -pull_kind = جستجو واکشی‌ها... +pull_kind = جستجو واکشی‌ها… no_results = نتیجه درخوری یافت نشد. -runner_kind = جستجو دونده‌ها... +runner_kind = جستجو دونده‌ها… keyword_search_unavailable = جستجو کلیدواژه اکنون در درسترس نیست. لطفا با مدیر سایت در میان بگذارید. -repo_kind = جستجو مخازن... +repo_kind = جستجو مخازن… regexp_tooltip = اصطلاح جستجو شده را با عبارات باقاعده تفسیر کن -user_kind = جستجو کاربران... -org_kind = جستجو سازمان‌ها... -team_kind = جستجو گروه‌ها... -package_kind = جستجو بسته‌ها... -project_kind = جستجو پروژه‌ها... +user_kind = جستجو کاربران… +org_kind = جستجو سازمان‌ها… +team_kind = جستجو گروه‌ها… +package_kind = جستجو بسته‌ها… +project_kind = جستجو پروژه‌ها… code_search_unavailable = جستجوی کد اکنون در دسترس نیست. لطفا با مدیر سایت درمیان بگذارید. -code_kind = جستجو کدها... +code_kind = جستجو کدها… union = بهم پیوستگی union_tooltip = نتایجی را در بر بگیر که با هر یک از کلیدواژه‌های جدا شده از فضای‌خالی مطابقت دارد -branch_kind = جستجو شاخه‌ها... -commit_kind = جستجو سپرده‌ها... -issue_kind = جستجو مشکلات... +branch_kind = جستجو شاخه‌ها… +commit_kind = جستجو سپرده‌ها… +issue_kind = جستجو مشکلات… exact = مو به مو exact_tooltip = نتایجی را در بر بگیر که مو به مو با اصطلاح جستجو شده یکی باشد diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index efd8a16526..7eea87f959 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -669,6 +669,13 @@ username_claiming_cooldown = Käyttäjänimeä ei voi ottaa käyttöön, koska s email_domain_is_not_allowed = Käyttäjän sähköpostiosoitteen %s verkkotunnus on ristiriidassa EMAIL_DOMAIN_ALLOWLIST:in tai EMAIL_DOMAIN_BLOCKLIST:in kanssa. Varmista, että olen asettanut sähköpostiosoitteen oikein. +invalid_group_team_map_error = ` kuvaus ei ole kelvollinen: %s` +visit_rate_limit = Etävierailujen pyyntörajoitukset. +2fa_auth_required = Etävierailu vaati kaksivaiheisen todennuksen. +unset_password = Kirjautuneen käyttäjän salasanaa ei ole asetettu. +unsupported_login_type = Tällä kirjautumistavalla ei voi poistaa tunnusta. +invalid_ssh_principal = Väärä toimija: %s + [user] change_avatar=Vaihda profiilikuvasi… repositories=Tietovarastot @@ -1038,6 +1045,9 @@ then_enter_passcode = Kirjoita sovelluksessa näkyvä pääsykoodi: gpg_key_matched_identities_long = Tähän avaimeen upotetut identiteetit vastaavat tämän käyttäjän seuraavia aktivoituja sähköpostiosoitteita. Kommitit, jotka vastaavat näitä sähköpostiosoitteita, voidaan vahvistaa tällä avaimella. twofa_failed_get_secret = Salaisuuden saaminen epäonnistui. +uid = UID +hidden_comment_types.ref_tooltip = Kommentit missä tähän ongelmaan viitattiin toisesta ongelmasta/kommitista/… + [repo] owner=Omistaja owner_helper=Jotkin organisaatiot eivät välttämättä näy pudotusvalikossa, koska tietovarastojen enimmäismäärää on rajoitettu. diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 8c9badb04b..487768ea22 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -699,7 +699,7 @@ security = Seguridad avatar = Avatar ssh_gpg_keys = Mga SSH / GPG key applications = Mga Aplikasyon -orgs = Ipamahala ang mga organisasyon +orgs = Mga organisasyon repos = Mga Repositoryo delete = Burahin ang account twofa = Authentikasyong two-factor (TOTP) @@ -707,7 +707,7 @@ account_link = Mga naka-link na account uid = UID webauthn = Authentikasyong two-factor (Mga security key) blocked_users = Mga hinarang na user -public_profile = Pampublikong Profile +public_profile = Pampublikong profile location_placeholder = Ibahagi ang iyong tinatayang lokasyon sa iba password_username_disabled = Ang mga di-lokal na gumagamit ay hindi pinapayagan na baguhin ang kanilang username. Mangyaring makipag-ugnayan sa iyong tagapangasiwa ng site para sa higit pang mga detalye. full_name = Buong pangalan diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index e522d5ab92..db9872236b 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -4091,4 +4091,4 @@ issues.write = Écrire : Fermer des tickets et gérer les métadonnées t pulls.read = Lire : Lire et créer des demandes de tirage. [translation_meta] -test = Ceci est une chaîne de test. Elle n'est pas affichée dans Forgejo mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) +test = Ceci est une chaîne de test. Elle n'est pas affichée dans Forgejo même mais est utilisée à des fins de test. N'hésitez pas à entrer 'ok' pour gagner du temps (ou un fait amusant de votre choix) pour atteindre ce difficile 100 % de complétion. :-) diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index 3bb06e8c21..c8f4d2a72c 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -132,20 +132,20 @@ fuzzy = Doiléir fuzzy_tooltip = Cuir san áireamh torthaí a mheaitseálann an téarma cuardaigh go dlúth freisin exact = Beacht exact_tooltip = Ní chuir san áireamh ach torthaí a mheaitseálann leis an téarma -repo_kind = Cuardaigh stórtha... -user_kind = Cuardaigh úsáideoirí... -org_kind = Cuardaigh eagraíochtaí... -team_kind = Cuardaigh foirne... -code_kind = Cód cuardaigh... +repo_kind = Cuardaigh stórtha… +user_kind = Cuardaigh úsáideoirí… +org_kind = Cuardaigh eagraíochtaí… +team_kind = Cuardaigh foirne… +code_kind = Cód cuardaigh… code_search_unavailable = Níl cuardach cód ar fáil faoi láthair. Déan teagmháil le riarthóir an láithreáin. -package_kind = Cuardaigh pacáistí... -project_kind = Cuardaigh tionscadail... -branch_kind = Cuardaigh brainsí... -commit_kind = Cuardaigh tiomáintí... -runner_kind = Cuardaigh reathaithe... +package_kind = Cuardaigh pacáistí… +project_kind = Cuardaigh tionscadail… +branch_kind = Cuardaigh brainsí… +commit_kind = Cuardaigh tiomáintí… +runner_kind = Cuardaigh reathaithe… no_results = Níl aon torthaí meaitseála le fáil. -issue_kind = Saincheisteanna cuardaigh... -pull_kind = Cuardaigh iarratais tarraingthe... +issue_kind = Saincheisteanna cuardaigh… +pull_kind = Cuardaigh iarratais tarraingthe… keyword_search_unavailable = Níl cuardach de réir eochairfhocal ar fáil faoi láthair. Déan teagmháil le riarthóir an láithreáin. [aria] @@ -507,12 +507,12 @@ avatar = Abhatár ssh_gpg_keys = Eochracha SSH/GPG applications = Iarratais repos = Stórais -delete = Scrios Cuntas +delete = Scrios cuntas twofa = Fíordheimhniú Dhá Fachtóir (TOTP) organization = Eagraíochtaí uid = UID webauthn = Fíordheimhniú Dhá-Fachtóir (Eochracha Slándála) -public_profile = Próifíl Phoiblí +public_profile = Próifíl phoiblí location_placeholder = Comhroinn do shuíomh thart le daoine eile full_name = Ainm Iomlán website = Láithreán Gréasáin diff --git a/options/locale/locale_gl.ini b/options/locale/locale_gl.ini index 3854b375af..c380e9b2ff 100644 --- a/options/locale/locale_gl.ini +++ b/options/locale/locale_gl.ini @@ -196,29 +196,29 @@ link_modal.paste_reminder = Consello: Coa URL no portapapeis, podes pegala direc [search] search = Buscar... type_tooltip = Tipo de procura -repo_kind = Buscar repositorios... -user_kind = Buscar usuarios... +repo_kind = Buscar repositorios… +user_kind = Buscar usuarios… regexp = RegExp regexp_tooltip = Interpretar o termo da procura como expresión regular -org_kind = Procurar organizacións... -team_kind = Procurar equipos... -code_kind = Procurar código... +org_kind = Procurar organizacións… +team_kind = Procurar equipos… +code_kind = Procurar código… code_search_unavailable = A procura de código non está dispoñible neste momento. Por favor contacte coa persoa responsable da administración da páxina. -package_kind = Buscar paquetes... +package_kind = Buscar paquetes… fuzzy = Difusa fuzzy_tooltip = Incluír resultados que tamén coincidan estreitamente co termo da procura union = Palabras clave union_tooltip = Incluír resultados correspondentes a calquera dal palabras clave separadas por espazos en branco exact = Exacta exact_tooltip = Incluír só resultados correspondentes ao termo exacto da procura -issue_kind = Procurar incidencias... -project_kind = Buscar proxectos... -branch_kind = Buscar ramas... +issue_kind = Procurar incidencias… +project_kind = Buscar proxectos… +branch_kind = Buscar ramas… no_results = Non se atoparon resultados coincidentes. keyword_search_unavailable = A busca por palabra clave non está dispoñible actualmente. Póñase en contacto co administrador do sitio. -commit_kind = Buscar achegas... -runner_kind = Buscar executores... -pull_kind = Buscar pulls... +commit_kind = Buscar achegas… +runner_kind = Buscar executores… +pull_kind = Buscar pulls… [startpage] platform = Multiplataforma diff --git a/options/locale/locale_he.ini b/options/locale/locale_he.ini index 19c4815277..26e3084809 100644 --- a/options/locale/locale_he.ini +++ b/options/locale/locale_he.ini @@ -1,6 +1,3 @@ - - - [common] webauthn_error_unable_to_process = שרת זה נכשל בעיבוד בקשתך. help = עזרה @@ -150,24 +147,24 @@ union = מילות מפתח exact = מדויק exact_tooltip = תוצאות יתאימו במדויק לתוכן תיבת החיפוש regexp = רג'קס -user_kind = חיפוש אנשים... -code_kind = חיפוש קוד... -team_kind = חיפוש צוותים... +user_kind = חיפוש אנשים… +code_kind = חיפוש קוד… +team_kind = חיפוש צוותים… no_results = לא נמצאו תוצאות. union_tooltip = תוצאות יכללו לפחות מילת מפתח אחת; אפשר להפריד מילות מפתח עם רווחים -org_kind = חיפוש ארגונים... -package_kind = חיפוש חבילות... -project_kind = חיפוש פרוייקטים... -branch_kind = חיפוש ענפים... -commit_kind = חיפוש קומיטים... -issue_kind = חיפוש סוגיות... +org_kind = חיפוש ארגונים… +package_kind = חיפוש חבילות… +project_kind = חיפוש פרוייקטים… +branch_kind = חיפוש ענפים… +commit_kind = חיפוש קומיטים… +issue_kind = חיפוש סוגיות… fuzzy_tooltip = תוצאות יתאימו לתוכן תיבת החיפוש בקירוב; מומלץ כנגד שגיאות כתיב -repo_kind = חיפוש קרפיפים... +repo_kind = חיפוש קרפיפים… code_search_by_git_grep = תוצאות החיפוש יוצרו על ידי "git grep"; יכול להיות שיתקבלו תוצאות טובות יותר אם מנהלי המערכת יפעילו את המפתחן. -runner_kind = חיפוש מריצים... +runner_kind = חיפוש מריצים… keyword_search_unavailable = חיפוש מילות מפתח לא זמין. נא לדווח למנהלי המערכת. code_search_unavailable = חיפוש קוד לא זמין. נא לדווח למנהלי המערכת. -pull_kind = חיפוש בקשות מיזוג... +pull_kind = חיפוש בקשות מיזוג… [heatmap] number_of_contributions_in_the_last_12_months = % תרומות ב־12 החודשים האחרונים @@ -385,6 +382,9 @@ account_activated = חשבונך הופעל resent_limit_prompt = כבר ביקשת מייל אימות בשלושת הדקות האחרונות. נא לחכות ולנסות שוב. has_unconfirmed_mail = שלום %s, חשבונך משויך לכתובת אימייל לא מאומתת (%s). אם לא קיבלת הודעת אימות באימייל, או שאתה צריך חדשה, נא ללחוץ על הכפתור למטה. +confirmation_mail_sent_prompt = אימייל אימות חדש נשלח ל־%s. יש לבדוק את תיבת הדואר וללחוץ על הלינק תוך %s על מנת להשלים את רישום החשבון. אם כתובת המייל שגוייה, אפשר להיכנס לחשבון ולבקש דוא"ל אימות לכתובת אחרת. +reset_password_mail_sent_prompt = אימייל אימות חדש נשלח ל־%s. יש לבדוק את תיבת הדואר וללחוץ על הלינק תוך %s על מנת להשלים את רישום החשבון. + [settings] key_content = תוכן principal_content = תוכן @@ -469,7 +469,7 @@ uploaded_avatar_not_a_image = הקובץ שהועלה לא תמונה. [repo] new_advanced = הגדרות מתקדמות -new_advanced_expand = +new_advanced_expand = owner = בעלים repo_name = שם הקרפיף repo_name_helper = שמות קרפיפים טובים הם זכירים, קצרים וייחודיים. @@ -662,6 +662,10 @@ issues.label_archive = לארכיון issues.label_archived_filter = הצגת תוויות מהארכיון issues.label_archive_tooltip = תוויות בארכיון לא מוצעות בחיפוש על־בסיס תווית כברירת מחדל. +issues.deleted_milestone = נמחק +issues.deleted_project = נמחק +issues.self_assign_at = `שייךה עצמית %s` + [translation_meta] test = ואהבת לרעך כמוך diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 3e93ee8ba9..c6eca84ac7 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -330,7 +330,7 @@ code_no_results=Nincs találat a keresési kifejezésedre. code_last_indexed_at=Utoljára indexelve: %s [auth] -create_new_account=Regisztráció +create_new_account=Fiók regisztrálása register_helper_msg=Van már felhasználói fiókja? Jelentkezzen be! social_register_helper_msg=Van már felhasználói fiókja? Csatlakoztassa most! disable_register_prompt=Regisztráció le van tiltva. Kérjük, lépjen kapcsolatba az oldal adminisztrátorával. @@ -532,8 +532,8 @@ password_change_disabled=A nem helyi felhasználók nem frissíthetik jelszavuka emails=E-mail címek manage_emails=E-mail címek kezelése -manage_themes=Válassza ki az alapértelmezett témát -manage_openid=OpenID címek kezelése +manage_themes=Alapértelmezett téma +manage_openid=OpenID címek theme_desc=Ez lesz az alapértelmezett téma az oldalon. primary=Elsődleges activated=Aktivált @@ -1779,14 +1779,14 @@ directory = Könyvtár [search] search = Keresés... type_tooltip = Keresés típusa -code_kind = Kód keresése... +code_kind = Kód keresése… code_search_unavailable = A kódban való keresés jelenleg nem elérhető. Kérem vegye fel a kapcsolatot az oldal adminisztrátorával. -package_kind = Csomagok keresése... -project_kind = Projektek keresése... -user_kind = Felhasználók keresése... -repo_kind = Tárak keresése... -org_kind = Szervezetek keresése... -team_kind = Csapatok keresése... +package_kind = Csomagok keresése… +project_kind = Projektek keresése… +user_kind = Felhasználók keresése… +repo_kind = Tárak keresése… +org_kind = Szervezetek keresése… +team_kind = Csapatok keresése… exact = Pontos code_search_by_git_grep = A kódkeresés jelenleg a "git grep" parancsot használja. Lehet, hogy jobb találatok is lennének, ha a webhely adminisztrátora bekapcsolja a forráskód indexelését. milestone_kind = Mérföldkövek keresése... @@ -1794,8 +1794,8 @@ fuzzy_tooltip = A keresési kifejezéshez hasonló találatok mutatása fuzzy = Hasonlók union = Kulcsszavakra union_tooltip = A szóközzel elválasztott kulcsszavak bármelyikét tartalmazó találatok mutatása -branch_kind = Ágak keresése... +branch_kind = Ágak keresése… no_results = Nincsenek megfelelő találatok. -issue_kind = Hibajegyek keresése... +issue_kind = Hibajegyek keresése… exact_tooltip = Csak a keresési kifejezést pontosan tartalmazó találatok mutatása keyword_search_unavailable = A kulcsszó alapú keresés jelenleg nem elérhető. Kérlek értesítsd az oldal rendszergazdáját. diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index f1a392105e..56993bec0d 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -206,9 +206,9 @@ email_title = Pengaturan email smtp_from = Kirim Email Sebagai [home] -uname_holder=Nama Pengguna atau Alamat Surel +uname_holder=Nama pengguna atau alamat surel password_holder=Kata Sandi -switch_dashboard_context=Alihkan Dasbor Konteks +switch_dashboard_context=Alihkan dasbor konteks my_repos=Repositori show_more_repos=Tampilkan repositori lainnya… collaborative_repos=Repositori Kolaboratif @@ -236,7 +236,7 @@ org_no_results=Tidak ada organisasi yang cocok ditemukan. code_no_results=Tidak ada kode sumber yang cocok dengan istilah yang anda cari. [auth] -create_new_account=Daftar Akun +create_new_account=Daftar akun register_helper_msg=Sudah memiliki akun? Masuk sekarang! social_register_helper_msg=Sudah memiliki akun? Hubungkan sekarang! disable_register_prompt=Maaf, pendaftaran telah dinonaktifkan. Silakan hubungi administrator situs. @@ -269,11 +269,11 @@ twofa_passcode_incorrect=Kata sandi Anda salah. Jika Anda salah tempatkan perang twofa_scratch_token_incorrect=Kode coretan anda tidak tepat. login_userpass=Masuk tab_openid=OpenID -oauth_signup_tab=Daftar Akun Baru -oauth_signup_submit=Akun Lengkap -oauth_signin_tab=Tautkan ke Akun yang Tersedia -oauth_signin_title=Masuk untuk Izinkan Akun Tertaut -oauth_signin_submit=Taut Akun +oauth_signup_tab=Daftar akun baru +oauth_signup_submit=Akun lengkap +oauth_signin_tab=Tautkan ke akun yang tersedia +oauth_signin_title=Masuk untuk izinkan akun tertaut +oauth_signin_submit=Taut akun openid_connect_submit=Sambungkan openid_connect_title=Sambungkan ke akun yang sudah ada openid_connect_desc=OpenID URI yang dipilih tak dikenal. Asosiasikan dengan akun baru disini. @@ -398,14 +398,14 @@ avatar=Avatar ssh_gpg_keys=Kunci SSH / GPG social=Akun Sosial applications=Aplikasi -orgs=Kelola organisasi +orgs=Organisasi repos=Repositori -delete=Hapus Akun +delete=Hapus akun twofa=Otentikasi Dua-Faktor account_link=Akun Tertaut organization=Organisasi -public_profile=Profil Publik +public_profile=Profil publik password_username_disabled=Pengguna non-lokal tidak diizinkan untuk mengubah nama pengguna mereka. Silakan hubungi administrator sistem anda untuk lebih lanjut. full_name=Nama Lengkap website=Situs Web @@ -438,8 +438,8 @@ password_change_disabled=Pengguna non-lokal tidak dapat mengganti kata sandi mer emails=Alamat Surel manage_emails=Kelola Alamat Surel -manage_themes=Pilih tema default -manage_openid=Kelola alamat OpenID +manage_themes=Tema default +manage_openid=Alamat OpenID theme_desc=Ini akan menjadi tema asal Anda pada keseluruhan situs. primary=Utama activated=Diaktifkan @@ -1484,10 +1484,10 @@ search = Cari... type_tooltip = Tipe pencarian fuzzy_tooltip = Termasuk juga hasil yang mendekati kata pencarian exact_tooltip = Hanya menampilkan hasil yang cocok dengan istilah pencarian -repo_kind = Cari repo... -user_kind = Telusuri pengguna... -org_kind = Cari organisasi... -team_kind = Cari tim... -code_kind = Cari kode... +repo_kind = Cari repo… +user_kind = Telusuri pengguna… +org_kind = Cari organisasi… +team_kind = Cari tim… +code_kind = Cari kode… code_search_unavailable = Pencarian kode saat ini tidak tersedia. Silahkan hubungi administrator. -branch_kind = Cari cabang... +branch_kind = Cari cabang… diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index baf8286923..f3333dbea2 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -223,7 +223,7 @@ default_keep_email_private.description=Fela sjálfgefið netföng nýrra notenda no_reply_address_helper=Lén fyrir notendur með falið netfang. Til dæmis notandanafnið „joe“ verður skráð í Git sem „joe@noreply.example.org“ ef falið tölvupóstlén er stillt á „noreply.example.org“. [home] -uname_holder=Notandanafn eða Netfang +uname_holder=Notandanafn eða netfang password_holder=Lykilorð my_repos=Hugbúnaðarsöfn show_more_repos=Sýna fleiri hugbúnaðarsöfn… @@ -255,7 +255,7 @@ org_no_results=Engar samsvarandi stofnanir fundust. code_no_results=Enginn samsvarandi frumkóði fannst eftur þínum leitarorðum. [auth] -create_new_account=Skrá Notanda +create_new_account=Skrá notanda register_helper_msg=Ertu nú þegar með notanda? Skráðu þig inn núna! social_register_helper_msg=Ertu nú þegar með reikning? Tengdu hann núna! manual_activation_only=Hafðu samband við stjórnanda vefsvæðisins til að ljúka virkjun. @@ -276,13 +276,13 @@ verify=Staðfesta scratch_code=Skrapkóði use_scratch_code=Nota skrapkóða twofa_scratch_token_incorrect=Skrapkóði þinn er rangur. -login_userpass=Skrá Inn +login_userpass=Skrá inn tab_openid=OpenID -oauth_signup_tab=Skrá Nýjan Notanda -oauth_signup_title=Klára Nýjum Notanda -oauth_signup_submit=Klára Notanda -oauth_signin_tab=Tengja Núverandi Reikning -oauth_signin_submit=Tengja Notanda +oauth_signup_tab=Skrá nýjan notanda +oauth_signup_title=Klára nýjum notanda +oauth_signup_submit=Klára notanda +oauth_signin_tab=Tengja núverandi reikning +oauth_signin_submit=Tengja notanda openid_connect_submit=Tengjast openid_register_title=Skrá nýjan notanda disable_forgot_password_mail=Endurheimting reiknings er óvirk vegna þess að enginn tölvupóstur er uppsettur. Vinsamlegast hafðu samband við síðustjórann þinn. @@ -434,15 +434,15 @@ avatar=Notandamynd ssh_gpg_keys=SSH og GPG Lyklar social=Félagsreikningar applications=Forrit -orgs=Stjórna Stofnunum +orgs=Stofnanir repos=Hugbúnaðarsöfn -delete=Eyða Reikningi +delete=Eyða reikningi twofa=Tvíþætt Auðkenning account_link=Tengdir Reikningar organization=Stofnanir webauthn=Öryggislyklar -public_profile=Opinber Notandasíða +public_profile=Opinber notandasíða password_username_disabled=Notendum utan staðarins er ekki heimilt að breyta notendanafni sínu. Vinsamlegast hafðu samband við síðustjórann þinn til að fá frekari upplýsingar. full_name=Fullt Nafn website=Vefsíða diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index d46f709cde..50405ed756 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -423,7 +423,7 @@ allow_password_change=Richiede all'utente di cambiare la password (scelta consig reset_password_mail_sent_prompt=Un'e-mail di conferma è stata inviata a %s. Per completare il processo di recupero dell'account, controlla la tua posta in arrivo e clicca sul link entro i prossimi %s secondi. active_your_account=Attiva il tuo account account_activated=L'account è stato attivato -prohibit_login=L'accesso è proibito +prohibit_login=L'account è sospeso resent_limit_prompt=Hai già richiesto un'e-mail d'attivazione recentemente. Si prega di attenere 3 minuti e poi riprovare. has_unconfirmed_mail=Ciao %s, hai un indirizzo di posta elettronica non confermato (%s). Se non hai ricevuto una e-mail di conferma o vuoi riceverla nuovamente, fare clic sul pulsante qui sotto. resend_mail=Clicca qui per inviare nuovamente l'e-mail di attivazione @@ -742,7 +742,7 @@ avatar=Avatar ssh_gpg_keys=Chiavi SSH / GPG social=Account Sociali applications=Applicazioni -orgs=Gestisci le organizzazioni +orgs=Organizzazioni repos=Repositori delete=Elimina account twofa=Verifica in due passaggi @@ -803,8 +803,8 @@ password_change_disabled=Gli utenti non locali non possono cambiare la loro pass emails=Indirizzi email manage_emails=Gestisci indirizzi email -manage_themes=Seleziona il tema predefinito -manage_openid=Gestisci gli indirizzi OpenID +manage_themes=Tema predefinito +manage_openid=Indirizzi OpenID theme_desc=Questo sarà il tuo tema di predefinito in tutto il sito. primary=Primario activated=Attivato @@ -4014,25 +4014,25 @@ type_tooltip = Tipo ricerca search = Cerca… fuzzy = Approssimativa match = Precisa -org_kind = Cerca organizzazioni... -package_kind = Ricerca pacchetti... +org_kind = Cerca organizzazioni… +package_kind = Ricerca pacchetti… code_search_unavailable = La ricerca del codice non è attualmente disponibile. Contatta l'amministratorə del sito. -code_kind = Cerca nel codice... -team_kind = Cerca team... +code_kind = Cerca nel codice… +team_kind = Cerca team… code_search_by_git_grep = I risultati della ricerca del codice sono forniti da "git grep". Potrebbero esserci risultati migliori se l'amministratore del sito avesse abilitato l'indicizzatore del codice. -project_kind = Ricerca progetti... -commit_kind = Ricerca commit... -branch_kind = Ricerca rami... +project_kind = Ricerca progetti… +commit_kind = Ricerca commit… +branch_kind = Ricerca rami… no_results = Non è stato trovato alcun risultato. keyword_search_unavailable = La ricerca per parole chiave non è attualmente disponibile. Contatta l'amministratore del sito. -runner_kind = Ricerca esecutori... +runner_kind = Ricerca esecutori… match_tooltip = Includi solo risultati che corrispondono precisamente al termine di ricerca fuzzy_tooltip = Includi anche risultati che corrispondono approssimativamente al termine di ricerca -user_kind = Cerca utenti... -repo_kind = Cerca repo... +user_kind = Cerca utenti… +repo_kind = Cerca repo… exact_tooltip = Includi solo i risultati che corrispondono esattamente al termine di ricerca -issue_kind = Cerca segnalazioni... -pull_kind = Cerca richieste... +issue_kind = Cerca segnalazioni… +pull_kind = Cerca richieste… exact = Esatto milestone_kind = Ricerca tappe... regexp_tooltip = Interpreta i termini di ricerca come un'espressione regolare diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 555f5c6a75..f3a8922f88 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -250,7 +250,7 @@ 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=管理者のユーザー名が不正です @@ -1048,6 +1048,8 @@ keep_activity_private.description = 公開アクティビティ language.description = この言語はアカウントに保存され、ログイン後にデフォルトとして使用されます。 language.localization_project = Forgejo をあなたの言語に翻訳するのを手伝ってください。詳細はこちら。 +quota = クオータ + [repo] new_repo_helper=リポジトリには、プロジェクトのすべてのファイルとリビジョン履歴が入ります。 すでにほかの場所でホストしていますか? リポジトリを移行 もどうぞ。 owner=オーナー @@ -3903,20 +3905,20 @@ submodule=サブモジュール [search] search = 検索... type_tooltip = 検索タイプ -org_kind = 組織の検索... -code_kind = コードの検索... +org_kind = 組織を検索… +code_kind = コードを検索… fuzzy = あいまい -repo_kind = リポジトリの検索... +repo_kind = リポジトリを検索… code_search_unavailable = コード検索は現在利用できません。サイト管理者にお問い合わせください。 -branch_kind = ブランチの検索... -commit_kind = コミットの検索... -user_kind = ユーザーの検索... -team_kind = チームの検索... +branch_kind = ブランチを検索… +commit_kind = コミットを検索… +user_kind = ユーザーを検索… +team_kind = チームを検索… code_search_by_git_grep = 現在のコード検索結果は「git grep」によって提供されます。サイト管理者がコード インデクサーを有効にすると、より良い結果が得られる可能性があります。 -package_kind = パッケージの検索... -project_kind = プロジェクトの検索... +package_kind = パッケージを検索… +project_kind = プロジェクトを検索… keyword_search_unavailable = キーワードによる検索は現在ご利用いただけません。サイト管理者にお問い合わせください。 -runner_kind = ランナーの検索... +runner_kind = ランナーを検索… no_results = 一致する結果が見つかりませんでした。 fuzzy_tooltip = 入力された語句に近いものも結果に含める match = 一致 @@ -3924,8 +3926,8 @@ match_tooltip = 検索語句に厳密に一致するもののみ結果に含め milestone_kind = マイルストーンを検索... union_tooltip = 空白で区切られたキーワードのいずれかに一致する結果を含める exact_tooltip = 検索語句と完全に一致する結果のみを含める -issue_kind = イシューを検索... -pull_kind = プルを検索... +issue_kind = イシューを検索… +pull_kind = プルを検索… exact = 完全一致 regexp_tooltip = 検索語句を正規表現として解釈する regexp = 正規表現 diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index be0400bea4..5e2354f3a8 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -384,7 +384,7 @@ allow_password_change=사용자에게 비밀번호 변경을 요청 (권장됨) reset_password_mail_sent_prompt=확인 메일이 %s로 전송되었습니다. 받은 편지함으로 도착한 메일을 %s 안에 확인해서 비밀번호 찾기 절차를 완료하십시오. active_your_account=계정 활성화 account_activated=계정이 활성화 되었습니다 -prohibit_login = +prohibit_login = resent_limit_prompt=활성화를 위한 이메일을 이미 전송했습니다. 3분 내로 이메일을 받지 못한 경우 재시도해주세요. has_unconfirmed_mail=안녕하세요 %s, 이메일 주소(%s)가 확인되지 않았습니다. 확인 메일을 받으시지 못하겼거나 새로운 확인 메일이 필요하다면, 아래 버튼을 클릭해 재발송하실 수 있습니다. resend_mail=여기를 눌러 확인 메일 재전송 @@ -588,8 +588,8 @@ password_change_disabled=로컬 유저가 아닌 경우 Forgejo 웹 인터페이 emails=이메일 주소 manage_emails=이메일 주소 관리 -manage_themes=기본 테마 선택 -manage_openid=OpenID 주소 관리 +manage_themes=기본 테마 +manage_openid=OpenID 주소 theme_desc=이 테마가 사이트 전체 기본 테마가 됩니다. primary=대표 activated=활성화됨 @@ -1900,9 +1900,9 @@ runs.commit=커밋 [search] code_search_by_git_grep = 현재 코드 검색 결과는 "git grep"에 의해 제공됩니다.관리자가 코드 인덱서를 활성화하면 더 나은 결과가 제공될 수 있습니다. -branch_kind = 브랜치 검색... +branch_kind = 브랜치 검색… keyword_search_unavailable = 지금은 키워드로 검색이 지원되지 않습니다. 사이트 관리자에게 문의하십시오. -commit_kind = 커밋 검색... +commit_kind = 커밋 검색… no_results = 일치하는 결과를 찾을 수 없습니다. search = 검색… type_tooltip = 검색 타입 @@ -1910,11 +1910,11 @@ fuzzy_tooltip = 검색어와 밀접하게 일치하는 결과도 포함 repo_kind = 저장소 검색… user_kind = 사용자 검색… org_kind = 조직 검색… -team_kind = 팀 검색... -code_kind = 코드 검색... +team_kind = 팀 검색… +code_kind = 코드 검색… code_search_unavailable = 코드 검색은 현재 허용되지 않았습니다. 사이트 관리자와 연락하세요. -package_kind = 패키지 검색... -project_kind = 프로젝트 검색... +package_kind = 패키지 검색… +project_kind = 프로젝트 검색… exact_tooltip = 검색어와 정확하게 일치하는 결과만 포함 issue_kind = 이슈 검색… pull_kind = 풀 검색… diff --git a/options/locale/locale_lt.ini b/options/locale/locale_lt.ini index 868e5bff6e..9d1c938379 100644 --- a/options/locale/locale_lt.ini +++ b/options/locale/locale_lt.ini @@ -149,24 +149,24 @@ fuzzy = Tikslintinas union_tooltip = Įtraukti rezultatus, atitinkančius bet kurį iš matomą tarpą atskirtų raktažodžių exact = Tiksliai exact_tooltip = Įtraukti tik tuos rezultatus, kurie atitinka tikslią paieškos frazę -user_kind = Ieškoti naudotojų... -team_kind = Ieškoti komandų... -code_kind = Ieškoti kodo... +user_kind = Ieškoti naudotojų… +team_kind = Ieškoti komandų… +code_kind = Ieškoti kodo… fuzzy_tooltip = Įtraukti rezultatus, kurie taip pat labai atitinka paieškos terminą -repo_kind = Ieškoti saugyklų... +repo_kind = Ieškoti saugyklų… code_search_unavailable = Kodų paieška šiuo metu nepasiekiama. Kreipkis į svetainės administratorių. -org_kind = Ieškoti organizacijų... +org_kind = Ieškoti organizacijų… union = Bendrinis code_search_by_git_grep = Dabartiniai kodo paieškos rezultatai pateikiami atliekant „git grep“. Rezultatai gali būti geresni, jei svetainės administratorius įjungs kodo indeksuotoją. -package_kind = Ieškoti paketų... -project_kind = Ieškoti projektų... -commit_kind = Ieškoti įsipareigojimų... -runner_kind = Ieškoti vykdyklių... +package_kind = Ieškoti paketų… +project_kind = Ieškoti projektų… +commit_kind = Ieškoti įsipareigojimų… +runner_kind = Ieškoti vykdyklių… no_results = Nerasta atitinkamų rezultatų. -issue_kind = Ieškoti problemų... -branch_kind = Ieškoti šakų... +issue_kind = Ieškoti problemų… +branch_kind = Ieškoti šakų… milestone_kind = Ieškoti gairių... -pull_kind = Ieškoti sujungimų... +pull_kind = Ieškoti sujungimų… keyword_search_unavailable = Ieškoti pagal raktažodį šiuo metu nepasiekiamas. Susisiekite su svetainės administratoriumi. regexp = Reguliarusis reiškinys regexp_tooltip = Interpretuoti paieškos terminą kaip reguliariąją reiškinį diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini index fcc9888d8e..c4c266ad86 100644 --- a/options/locale/locale_ml-IN.ini +++ b/options/locale/locale_ml-IN.ini @@ -356,7 +356,7 @@ avatar=അവതാര്‍ ssh_gpg_keys=SSH / GPG കീകള്‍ social=സോഷ്യൽ അക്കൗണ്ടുകൾ applications=അപ്ലിക്കേഷനുകൾ -orgs=സംഘടനകളെ നിയന്ത്രിക്കുക +orgs=സംഘടനകൾ repos=കലവറകള്‍ delete=അക്കൗണ്ട് ഇല്ലാതാക്കുക twofa=ഇരട്ട ഘടക പ്രാമാണീകരണം @@ -400,8 +400,8 @@ password_change_disabled=പ്രാദേശിക ഇതര ഉപയോക emails=ഇ-മെയില്‍ വിലാസങ്ങള്‍ manage_emails=ഇമെയിൽ വിലാസങ്ങൾ നിയന്ത്രിക്കുക -manage_themes=സ്ഥിരസ്ഥിതി പ്രമേയം തിരഞ്ഞെടുക്കുക -manage_openid=ഓപ്പൺഐഡി വിലാസങ്ങൾ നിയന്ത്രിക്കുക +manage_themes=ഡിഫോൾട്ട് തീം +manage_openid=OpenID വിലാസങ്ങൾ email_desc=അറിയിപ്പുകൾക്കും മറ്റ് പ്രവർത്തനങ്ങൾക്കുമായി നിങ്ങളുടെ പ്രാഥമിക ഇമെയിൽ വിലാസം ഉപയോഗിക്കും. theme_desc=സൈറ്റിലുടനീളം ഇത് നിങ്ങളുടെ സ്ഥിരസ്ഥിതി പ്രമേയം ആയിരിക്കും. primary=പ്രാഥമികം diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index cd91f7c35a..fe504b2a04 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -568,7 +568,7 @@ organization = Vereenigungen uid = UID webauthn = Twee-Faktooren-Anmellen (Sekerheids-Slötels) blocked_users = Blockeert Brukers -public_profile = Publikes Profil +public_profile = Publikes profil location_placeholder = Deel waar du umslags büst mit Annerns pronouns = Pronomens pronouns_custom = Eegene diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 189e663618..a788badb47 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -1043,8 +1043,8 @@ language.title = Domyślny język language.localization_project = Pomóż nam przetłumaczyć Forgejo na twój język! Dowiedz się więcej. update_hints = Zaktualizuj wskazówki update_hints_success = Wskazówki zostały zaktualizowane. -change_username_redirect_prompt.with_cooldown.one = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dzień, nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. -change_username_redirect_prompt.with_cooldown.few = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dni, nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. +change_username_redirect_prompt.with_cooldown.one = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dzień. Nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. +change_username_redirect_prompt.with_cooldown.few = Stara nazwa użytkownika będzie dostępna dla każdego po okresie ochronnym wynoszącym %[1]d dni. Nadal możesz uzyskać z powrotem starą nazwę użytkownika podczas okresu ochronnego. language.description = Ten język zostanie zapisany na twoim koncie i będzie używany jako domyślny po zalogowaniu. hidden_comment_types_description = Rodzaje komentarzy zaznaczone tutaj nie będą wyświetlały się na stronach zgłoszeń. Zaznaczenie "Etykieta" na przykład usunie wszystkie komentarze " dodał/usunął ". principal_desc = Te podmioty certyfikatu SSH będą powiązane z twoim kontem i pozwolą na pełen dostęp do twoich repozytoriów. @@ -3951,29 +3951,29 @@ normal_file = Zwykły plik search = Wyszukaj... type_tooltip = Typ wyszukiwania fuzzy = Przybliżone -package_kind = Wyszukaj pakiety... +package_kind = Wyszukaj pakiety… fuzzy_tooltip = Uwzględnij wyniki, które są bliskie wyszukiwanemu hasłu match = Dopasuj match_tooltip = Uwzględniaj tylko wyniki pasujące do wyszukiwanego hasła -repo_kind = Wyszukaj repozytoria... -user_kind = Wyszukaj użytkownilków... +repo_kind = Wyszukaj repozytoria… +user_kind = Wyszukaj użytkownilków… code_search_unavailable = Wyszukiwanie kodu jest obecnie niedostępne. Skontakuj sie z administratorem strony. no_results = Nie znaleziono pasujących wyników. -org_kind = Wyszukaj organizacje... -team_kind = Wyszukaj zespoły... -code_kind = Wyszukaj kod... +org_kind = Wyszukaj organizacje… +team_kind = Wyszukaj zespoły… +code_kind = Wyszukaj kod… code_search_by_git_grep = Obecne wyniki wyszukiwania kodu są dostarczane przez "git grep". Wyniki mogą być lepsze, jeśli administrator witryny włączy indeksator kodu. -project_kind = Wyszukaj projekty... -branch_kind = Wyszukaj gałęzie... -commit_kind = Wyszukaj commity... -runner_kind = Wyszukaj runnery... +project_kind = Wyszukaj projekty… +branch_kind = Wyszukaj gałęzie… +commit_kind = Wyszukaj commity… +runner_kind = Wyszukaj runnery… keyword_search_unavailable = Wyszukiwanie według słów kluczowych jest obecnie niedostępne. Skontaktuj się z administratorem strony. milestone_kind = Wyszukaj kamienie milowe... union_tooltip = Uwzględnia wyniki pasujące do dowolnego słowa kluczowego rozdzielonego białymi znakami exact = Dokładne exact_tooltip = Uwzględniaj tylko wyniki pasujące do wyszukiwanego hasła -issue_kind = Wyszukaj zgłoszenia... -pull_kind = Wyszukaj pull requesty... +issue_kind = Wyszukaj zgłoszenia… +pull_kind = Wyszukaj pull requesty… union = Unia regexp = RegExp regexp_tooltip = Interpretuj wyszukiwane hasło jako wyrażenie regularne diff --git a/options/locale/locale_ro.ini b/options/locale/locale_ro.ini index 305c34d013..c52788a05e 100644 --- a/options/locale/locale_ro.ini +++ b/options/locale/locale_ro.ini @@ -1,6 +1,3 @@ - - - [common] return_to_forgejo = Înapoi la Forgejo explore = Explorează @@ -223,12 +220,12 @@ invalid_db_setting = Setările pentru bază de date sunt invalide: %v no_reply_address = Domeniu pentru adrese de email ascunse [search] -user_kind = Caută utilizatori... -team_kind = Caută echipe... -code_kind = Caută cod... -project_kind = Caută proiecte... -package_kind = Caută pachete... -org_kind = Caută organizații... +user_kind = Caută utilizatori… +team_kind = Caută echipe… +code_kind = Caută cod… +project_kind = Caută proiecte… +package_kind = Caută pachete… +org_kind = Caută organizații… code_search_unavailable = Căutarea de cod nu este disponibilă momentan. Te rog contactează administratorul site-ului. keyword_search_unavailable = Căutarea după cuvânt cheie nu este disponibilă momentan. Te rog contactează administratorul site-ului. no_results = Nu a fost găsit niciun rezultat corespunzător. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 2ef1b868d4..304b07b7ee 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1063,7 +1063,7 @@ language.description = Выбранный язык будет сохранён language.localization_project = Помогите с переводом Forgejo на свой язык! Подробнее. user_block_yourself = Нельзя заблокировать себя. pronouns_custom_label = Другие местоимения -change_username_redirect_prompt.with_cooldown.one = Прежнее имя будет доступно для использования другим пользователям после истечения защиты в %[1]d день. Вы сможете вернуть его себе во время срока защиты. +change_username_redirect_prompt.with_cooldown.one = Прежнее имя будет доступно для использования другим пользователям после истечения простоя в %[1]d день. Вы сможете вернуть его себе во время срока простоя. change_username_redirect_prompt.with_cooldown.few = Прежнее имя будет доступно для использования другим пользователям после истечения защиты в %[1]d дней. Вы сможете вернуть его себе во время срока защиты. keep_pronouns_private = Показывать местоимения только зарегистрированным пользователям keep_pronouns_private.description = Местоимения будут скрыты от пользователей, не имеющих учётных записей на сервере. @@ -1641,9 +1641,9 @@ issues.closed_at=`задача была закрыта %s` issues.reopened_at=`задача была открыта снова %s` issues.commit_ref_at=`упоминание этой задачи в коммите %s` issues.ref_issue_from=`упоминание этой задачи %[3]s %[1]s` -issues.ref_pull_from=`упоминание этого запроса слияния %[3]s %[1]s` -issues.ref_closing_from=`упоминание из запроса на слияние %[3]s, который закроет эту задачу %[1]s` -issues.ref_reopening_from=`упоминание из запроса на слияние %[3]s, который повторно откроет эту задачу %[1]s` +issues.ref_pull_from=`упомянул этот запрос на слияние %[3]s %[1]s` +issues.ref_closing_from=`упомянул эту задачу в запросе на слияние %[3]s, который закроет её %[1]s` +issues.ref_reopening_from=`упомянул эту задачу в запросе на слияние %[3]s, который переоткроет эту задачу %[1]s` issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s` issues.ref_reopened_from=`задача была открыта снова %[4]s %[2]s` issues.ref_from=`из %[1]s` diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 54b0b246db..e1e19fa78e 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -482,7 +482,7 @@ avatar=අවතාර් ssh_gpg_keys=SSH/ජීපීජී යතුරු social=සමාජ ගිණුම් applications=යෙදුම් -orgs=සංවිධාන කළමනාකරණය +orgs=සංවිධාන repos=කෝෂ්ඨ delete=ගිණුම මකන්න twofa=ද්වි-සාධක සත්යාපනය @@ -527,8 +527,8 @@ password_change_disabled=දේශීය නොවන පරිශීලකය emails=වි-තැපැල් ලිපින manage_emails=වි-තැපැල් ලිපින කළමනාකරණය -manage_themes=පෙරනිමි තේමාව තෝරන්න -manage_openid=OpenID ලිපිනයන් කළමනාකරණය කරන්න +manage_themes=පෙරනිමි තේමාව +manage_openid=OpenID ලිපින theme_desc=මෙම වෙබ් අඩවිය හරහා ඔබගේ පෙරනිමි තේමාව වනු ඇත. primary=ප්රාථමික activated=සක්රිය diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 0c44df326a..aa2f863b14 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -597,7 +597,7 @@ avatar=Avatar ssh_gpg_keys=SSH / GPG kľúče social=Sociálne účty applications=Aplikácie -orgs=Spravovať organizácie +orgs=Organizácie repos=Repozitáre delete=Zmazať účet twofa=Dvojfaktorové overenie @@ -656,8 +656,8 @@ password_change_disabled=Externe overovaní používatelia nemôžu aktualizova emails=E-mailové adresy manage_emails=Správa e-mailových adries -manage_themes=Nastavenie predvolenej témy -manage_openid=Správa OpenID adries +manage_themes=Predvolená téma +manage_openid=Adresy OpenID theme_desc=Toto bude vaša predvolená téma vzhľadu naprieč stránkou. primary=Primárny activated=Aktivovaný diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index 608e05afa4..07ccaed259 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -133,7 +133,7 @@ reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta p require_db_desc = Forgejo zahteva MySQL, PostgreSQL, SQLite3 ali TiDB (protokol MySQL). password_algorithm_helper = Nastavite algoritem za stiskanje gesla. Algoritmi imajo različne zahteve in moč. Algoritem argon2 je precej varen, vendar porabi veliko pomnilnika in je lahko neprimeren za majhne sisteme. reinstall_confirm_message = Ponovna namestitev z obstoječo zbirko podatkov Forgejo lahko povzroči več težav. V večini primerov morate za zagon programa Forgejo uporabiti obstoječi "app.ini". Če veste, kaj počnete, potrdite naslednje: -err_admin_name_is_reserved = Administrator Uporabniško ime je neveljavno, uporabniško ime je rezervirano +err_admin_name_is_reserved = Administrator uporabniško ime je neveljavno, uporabniško ime je rezervirano disable_gravatar.description = Onemogočite vire avatarjev Gravatar in avatarje tretjih oseb. Uporabi se privzeti avatar, razen če uporabnik lokalno naloži avatar. install = Namestitev title = Začetna nastavitev @@ -367,7 +367,7 @@ delete = Brisanje računa uploaded_avatar_is_too_big = Velikost naložene datoteke (%d KiB) presega največjo velikost (%d KiB). webauthn = Dvofaktorsko preverjanje pristnosti (varnostni ključi) change_username_redirect_prompt = Staro uporabniško ime bo preusmerjeno, dokler ga nekdo ne prevzame. -orgs = Upravljanje organizacij +orgs = Organizacije public_profile = Javni profil gpg_key_verified_long = Ključ je bil preverjen z žetonom in ga je mogoče uporabiti za preverjanje zavez, ki ustrezajo vsem aktiviranim e-poštnim naslovom tega uporabnika, poleg vseh ujemajočih se identitet za ta ključ. @@ -495,14 +495,14 @@ tab_openid = Odprta identiteta [home] show_both_archived_unarchived = Prikazovanje arhiviranih in nearhiviranih -switch_dashboard_context = Kontekst stikala Nadzorna plošča +switch_dashboard_context = Kontekst stikala nadzorna plošča search_repos = Poiščite skladišče… filter_by_team_repositories = Filtriranje po skupinskih skladiščih show_archived = Arhivirano collaborative_repos = Sodelovalni repozitoriji my_mirrors = Moja ogledala show_only_public = Prikazovanje samo javnih -uname_holder = Uporabniško ime ali E-poštovni naslov +uname_holder = Uporabniško ime ali e-poštni naslov password_holder = Geslo my_repos = Repozitoriji show_more_repos = Prikaži več skladišč… diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 2e212c8c49..ec57f025cd 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -2326,15 +2326,15 @@ repo_kind = Sök repon… user_kind = Sök användare… code_kind = Sök kod… package_kind = Sök paket… -runner_kind = Sök exekutorer... +runner_kind = Sök exekutorer… branch_kind = Sök grenar… -commit_kind = Sök commiter... +commit_kind = Sök commiter… project_kind = Sök projekt… search = Sök… type_tooltip = Söktyp team_kind = Sök team… org_kind = Sök organisationer… -issue_kind = Sök ärenden... +issue_kind = Sök ärenden… regexp_tooltip = Tolka söktermen som ett reguljärt uttryck code_search_unavailable = Kodsökning är för närvarande inte tillgänglig. Vänligen kontakta webbplatsadministratören. fuzzy_tooltip = Inkludera resultat som är närliggande till söktermen diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index c07cefdab9..0bb49a2181 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -163,8 +163,8 @@ new_repo.link = Yeni depo new_org.link = Yeni organizasyon error413 = Kotanızı doldurdunuz. toggle_menu = Menüyü aç-kapa -new_migrate.title = Yeni geçiş -new_migrate.link = Yeni geçiş +new_migrate.title = Yeni göç +new_migrate.link = Yeni göç copy_path = Dizini kopyala confirm_delete_artifact = "%s" adlı öğeyi silmek istediğinizden emin misiniz? @@ -179,8 +179,10 @@ number_of_contributions_in_the_last_12_months=son 12 ayda %s katkı contributions_zero=Katkı yapılmamış less=Daha az more=Daha Fazla -contributions_one = katılım -contributions_few = katılımlar +contributions_one = katkı +contributions_few = katkı + +contributions_format = {day} {month} {year} tarihinde {contributions} katkı [editor] buttons.heading.tooltip=Başlık ekle @@ -204,6 +206,13 @@ table_modal.placeholder.content = İçerik table_modal.label.rows = Satırlar table_modal.label.columns = Sütunlar +buttons.indent.tooltip = Bir seviye girinti artır +buttons.unindent.tooltip = Bir seviye girinti azalt +link_modal.header = Bağlantı ekle +link_modal.url = Url +link_modal.description = Açıklama +link_modal.paste_reminder = İpucu: Panonuzdaki bir URL'yi doğrudan düzenleyiciye yapıştırarak bir bağlantı oluşturabilirsiniz. + [filter] string.asc=A - Z string.desc=Z - A @@ -227,6 +236,8 @@ lightweight_desc=Forgejo'nın minimal gereksinimleri çok düşüktür ve ucuz b license=Açık Kaynak license_desc=Gidin ve Forgejo'yı edinin! Bu projeyi daha da iyi yapmak için katkıda bulunarak bize katılın. Katkıda bulunmaktan çekinmeyin! +platform_desc = Forgejo'nun Linux ve FreeBSD gibi özgür işletim sistemlerinde ve farklı CPU mimarilerinde çalıştığı doğrulandı. Sevdiğinizi seçin! + [install] install=Kurulum title=Başlangıç Yapılandırması @@ -252,9 +263,9 @@ err_empty_db_path=SQLite3 veritabanı dosya yolu boş olamaz. no_admin_and_disable_registration=Bir yönetici hesabı oluşturmadan kullanıcı kaydını kapatamazsınız. err_empty_admin_password=Yönetici parolası boş olamaz. err_empty_admin_email=Yönetici e-postası boş olamaz. -err_admin_name_is_reserved=Yönetici Kullanıcı Adı geçersiz, bu kullanıcı adı rezerv edilen bir kelimedir +err_admin_name_is_reserved=Yönetici kullanıcı adı geçersiz, bu kullanıcı adı rezerv edilen bir kelimedir err_admin_name_pattern_not_allowed=Yönetici kullanıcı adı geçersiz, kullanıcı adı ayrılmış bir desenle eşleşiyor -err_admin_name_is_invalid=Yönetici Kullanıcı Adı geçersiz +err_admin_name_is_invalid=Yönetici kullanıcı adı geçersiz general_title=Genel ayarlar app_name=Site Başlığı @@ -345,6 +356,8 @@ allow_dots_in_usernames = Kullanıcı isimlerinde noktaya izin ver. Var olan kul smtp_from_invalid = `"E-posta Olarak Gönder" adresi geçersiz` +config_location_hint = Bu yapılandırma seçenekleri şuraya kaydedilecek: + [home] uname_holder=Kullanıcı adı veya e-posta adresi password_holder=Parola @@ -478,6 +491,8 @@ sign_in_openid = OpenID ile giriş yap hint_login = Mevcut hesabın var mı? Hemen giriş yap! use_onetime_code = Tek kullanımlık kod kullan +change_unconfirmed_email = Kayıt sırasında yanlış e-posta adresi verdiyseniz, aşağıdan değiştirebilirsiniz; yeni adresinize bir onay mesajı gönderilecektir. + [mail] view_it_on=%s üzerinde görüntüle reply=veya doğrudan bu e-postayı yanıtlayın @@ -722,16 +737,16 @@ avatar=Avatar ssh_gpg_keys=SSH / GPG Anahtarları social=Sosyal Medya Hesapları applications=Uygulamalar -orgs=Organizasyonları Yönet +orgs=Kuruluşlar repos=Depolar -delete=Hesabı Sil -twofa=İki Aşamalı Doğrulama +delete=Hesabı sil +twofa=İki aşamalı doğrulama account_link=Bağlı Hesaplar organization=Organizasyonlar uid=UID webauthn=Güvenlik Anahtarları -public_profile=Herkese Açık Profil +public_profile=Herkese açık profil biography_placeholder=Bize kendiniz hakkında birşeyler söyleyin! (Markdown kullanabilirsiniz) location_placeholder=Yaklaşık konumunuzu başkalarıyla paylaşın profile_desc=Profilinizin başkalarına nasıl gösterildiğini yönetin. Ana e-posta adresiniz bildirimler, parola kurtarma ve web tabanlı Git işlemleri için kullanılacaktır. @@ -796,8 +811,8 @@ password_change_disabled=Yerel olmayan kullanıcılar parolalarını Forgejo web emails=E-Posta Adresleri manage_emails=E-posta Adreslerini Yönet -manage_themes=Varsayılan temayı seç -manage_openid=OpenID Adreslerini Yönet +manage_themes=Varsayılan tema +manage_openid=OpenID adresleri email_desc=Ana e-posta adresiniz bildirimler, parola kurtarma ve gizlenmemişse eğer web tabanlı Git işlemleri için kullanılacaktır. theme_desc=Bu, sitedeki varsayılan temanız olacak. primary=Birincil @@ -1166,7 +1181,7 @@ form.name_reserved=Depo adı "%s" rezerve edilmiş. form.name_pattern_not_allowed=Depo adında "%s" deseni kullanılamaz. need_auth=Yetkilendirme -migrate_options=Göç Seçenekleri +migrate_options=Göç seçenekleri migrate_service=Göç Hizmeti migrate_options_mirror_helper=Bu depo bir yansı olacaktır migrate_options_lfs=LFS dosyalarını taşı @@ -1174,7 +1189,7 @@ migrate_options_lfs_endpoint.label=LFS Uç Noktası migrate_options_lfs_endpoint.description=Taşıma, LFS sunucusunu belirlemek için Git uzak sunucusunu kullanmaya çalışacak. Eğer LFS veri deposu başka yerdeyse özel bir uç nokta da belirtebilirsiniz. migrate_options_lfs_endpoint.description.local=Yerel bir sunucu yolu da destekleniyor. migrate_options_lfs_endpoint.placeholder=Boş bırakılırsa, uç nokta klon URL'sinden türetilecektir -migrate_items=Göç Öğeleri +migrate_items=Göç öğeleri migrate_items_wiki=Wiki migrate_items_milestones=Kilometre Taşları migrate_items_labels=Etiketler @@ -1215,7 +1230,7 @@ migrate.migrating_labels=Etiketleri Taşıma migrate.migrating_releases=Sürümleri Taşıma migrate.migrating_issues=Konuları Taşıma migrate.migrating_pulls=Değişiklik İsteklerini Taşıma -migrate.cancel_migrating_title=Göçü İptal Et +migrate.cancel_migrating_title=Göçü iptal et migrate.cancel_migrating_confirm=Bu göçü iptal etmek istiyor musunuz? mirror_from=şunun yansıması @@ -1252,7 +1267,7 @@ find_tag=Etiketi bul branches=Dal tags=Etiket issues=Konular -pulls=Değişiklik İstekleri +pulls=Değişiklik istekleri project_board=Projeler packages=Paketler actions=İşlemler @@ -1443,7 +1458,7 @@ projects.column.set_default=Varsayılanı Ayarla projects.column.set_default_desc=Bu sütunu kategorize edilmemiş konular ve değişiklik istekleri için varsayılan olarak ayarlayın projects.column.unset_default=Varsayılanları Geri Al projects.column.unset_default_desc=Bu sütunu varsayılan olarak geri al -projects.column.delete=Sutün Sil +projects.column.delete=Sütunu sil projects.column.deletion_desc=Bir proje sütununun silinmesi, ilgili tüm konuları 'Kategorize edilmemiş'e taşır. Devam edilsin mi? projects.column.color=Renk projects.open=Aç @@ -1595,7 +1610,7 @@ issues.reopen_issue=Yeniden aç issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç issues.create_comment=Yorum yap issues.closed_at=`%s konusunu kapattı` -issues.reopened_at=`%s konusunu yeniden açtı` +issues.reopened_at=%s sorununu yeniden açtı issues.commit_ref_at=`%s işlemesinde bu konuyu işaret etti` issues.ref_issue_from=`bu konuya referansta bulundu %[3]s %[1]s` issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[3]s %[1]s` @@ -1647,7 +1662,7 @@ issues.label.filter_sort.alphabetically=Alfabetik issues.label.filter_sort.reverse_alphabetically=Ters alfabetik issues.label.filter_sort.by_size=En küçük boyut issues.label.filter_sort.reverse_by_size=En büyük boyut -issues.num_participants_few=%d Katılımcı +issues.num_participants_few=%d katılımcı issues.attachment.open_tab=`Yeni bir sekmede "%s" görmek için tıkla` issues.attachment.download=`"%s" indirmek için tıkla` issues.subscribe=Abone Ol @@ -1984,7 +1999,7 @@ ext_wiki=Harici Vikiye Erişim ext_wiki.desc=Harici bir wiki'ye bağlantı. wiki=Wiki -wiki.welcome=Wiki'ye Hoşgeldiniz. +wiki.welcome=Viki'ye Hoş geldiniz. wiki.welcome_desc=Wiki, katkıcılarla belge yazmanıza ve paylaşmanıza olanak tanır. wiki.desc=Katkıcılarla belgeler yazın ve paylaşın. wiki.create_first_page=İlk sayfayı oluştur @@ -2140,15 +2155,15 @@ settings.use_external_wiki=Harici Wiki Kullan settings.external_wiki_url=Harici Wiki bağlantısı settings.external_wiki_url_error=Harici wiki URL'si geçerli bir URL değil. settings.external_wiki_url_desc=Ziyaretçiler, wiki sekmesine tıklandığında harici wiki URL'sine yönlendirilir. -settings.issues_desc=Depo Konu İzleyicisini Etkinleştir -settings.use_internal_issue_tracker=Yerleşik Konu İzleyici Kullan -settings.use_external_issue_tracker=Harici Konu İzleyici Kullan -settings.external_tracker_url=Harici Konu İzleyici URLsi +settings.issues_desc=Depo sorun izleyicisini etkinleştir +settings.use_internal_issue_tracker=Yerleşik sorun izleyici kullan +settings.use_external_issue_tracker=Harici sorun izleyici kullan +settings.external_tracker_url=Harici sorun izleyici URL'si settings.external_tracker_url_error=Harici konu izleyici URL'si geçerli bir URL değil. settings.external_tracker_url_desc=Ziyaretçiler, konular sekmesine tıkladığında harici konu izleyici URL'sine yönlendirilir. -settings.tracker_url_format=Harici Konu İzleyici URL Biçimi +settings.tracker_url_format=Harici sorun izleyici URL Biçimi settings.tracker_url_format_error=Harici konu izleyici URL biçimi geçerli bir URL değil. -settings.tracker_issue_style=Harici Konu İzleyici Numara Biçimi +settings.tracker_issue_style=Harici sorun izleyici Numara Biçimi settings.tracker_issue_style.numeric=Sayısal settings.tracker_issue_style.alphanumeric=Alfanumerik settings.tracker_issue_style.regexp=Düzenli ifade @@ -2158,13 +2173,13 @@ settings.tracker_url_format_desc=Kullanıcı adı, depo adı ve yayın dizini i settings.enable_timetracker=Zaman Takibini Etkinleştir settings.allow_only_contributors_to_track_time=Sadece Katkıcılar İçin Zaman Takibine İzin Ver settings.pulls_desc=Değişiklik İsteklerini Etkinleştir -settings.pulls.ignore_whitespace=Çakışmalar için Boşlukları Gözardı Et +settings.pulls.ignore_whitespace=Çakışmalar için boşlukları gözardı et settings.pulls.enable_autodetect_manual_merge=Kendiliğinden algılamalı elle birleştirmeyi etkinleştir (Not: Bazı özel durumlarda yanlış kararlar olabilir) settings.pulls.allow_rebase_update=Değişiklik isteği dalının yeniden yapılandırmayla güncellenmesine izin ver settings.pulls.default_delete_branch_after_merge=Varsayılan olarak birleştirmeden sonra değişiklik isteği dalını sil settings.pulls.default_allow_edits_from_maintainers=Bakımcıların düzenlemelerine izin ver settings.releases_desc=Depo Sürümlerini Etkinleştir -settings.packages_desc=Depo paket kütüğünü etkinleştir +settings.packages_desc=Depo paket kayıt defterini etkinleştir settings.projects_desc=Depo Projelerini Etkinleştir settings.actions_desc=Depo İşlemlerini Etkinleştir settings.admin_settings=Yönetici Ayarları @@ -2188,7 +2203,7 @@ settings.convert_fork_desc=Bu çatalı normal bir depoya dönüştürebilirsiniz settings.convert_fork_notices_1=Bu işlem çatalı normal bir depoya dönüştürür ve geri alınamaz. settings.convert_fork_confirm=Depoyu Dönüştür settings.convert_fork_succeed=Çatal normal bir depoya dönüştürüldü. -settings.transfer.title=Sahipliği Aktar +settings.transfer.title=Sahipliği aktar settings.transfer.rejected=Depo aktarımı reddedildi. settings.transfer.success=Depo aktarımı başarıyla tamamlandı. settings.transfer_abort=Aktarımı iptal et @@ -2448,7 +2463,7 @@ settings.block_on_official_review_requests_desc=Yeterli onay olsa bile, resmi in settings.block_outdated_branch=Değişiklik isteği güncel değilse birleştirmeyi engelle settings.block_outdated_branch_desc=Baş dal taban dalın arkasındayken birleştirme mümkün olmayacaktır. settings.default_branch_desc=Değişiklik istekleri ve kod işlemeleri için varsayılan bir depo dalı seçin: -settings.merge_style_desc=Biçimleri Birleştir +settings.merge_style_desc=Birleştirme biçemleri settings.default_merge_style_desc=Değişiklik istekleri için varsayılan birleştirme tarzı: settings.choose_branch=Bir dal seç… settings.no_protected_branch=Korumalı dal yok. @@ -2701,7 +2716,7 @@ settings.new_owner_blocked_doer = Yeni sahip sizi engelledi. open_with_editor = %s ile aç object_format = Nesne Biçimi -mirror_sync = eşitlendi +mirror_sync = eşitlenme: stars = Yıldızlar desc.sha256 = SHA256 vendored = Sağlanmış @@ -2722,6 +2737,26 @@ settings.ignore_stale_approvals = Eskimiş onayları yoksay settings.ignore_stale_approvals_desc = Daha eski işlemelere (eski incelemelere) yapılmış olan onayları, Dİ'nin kaç onayı olduğunu belirlerken sayma. Eskimiş incelemeler atıldıysa bu ilgisizdir. error.broken_git_hook = Bu deponun Git İstemcileri bozuk gibi gözüküyor. Onarmak için lütfen belgelere bakın, daha sonra durumu yenilemek için bazı işlemeler itin. +mirror_public_key = Ortak SSH anahtarı +mirror_use_ssh.text = SSH yetkilendirme kullan +mirror_use_ssh.helper = Forgejo, bu seçeneği seçtiğinizde deponuzu SSH üzerinden Git üzerinden yansıtacak ve sizin için bir anahtar çifti oluşturacaktır. Oluşturulan ortak anahtarın hedef depoya gönderilmek üzere yetkilendirildiğinden emin olmalısınız. Bunu seçerken parola tabanlı yetkilendirme kullanamazsınız. +mirror_use_ssh.not_available = SSH yetkilendirme kullanılamıyor. +mirror_denied_combination = Ortak anahtar ve parola tabanlı kimlik doğrulama birlikte kullanılamaz. +n_commit_few = %s gönderi +n_branch_one = %s dal +n_branch_few = %s dal +n_tag_few = %s etiket +commits.browse_further = Daha fazlasına göz at +commits.renamed_from = %s adından yeniden adlandırıldı +issues.filter_no_results = Sonuç yok +issues.filter_no_results_placeholder = Arama filtrelerini değiştirmeyi deneyin. +issues.num_participants_one = %d katılımcı +settings.units.units = Birimler +settings.units.add_more = Daha fazlasını etkinleştir +settings.wiki_globally_editable = Vikiyi herkesin düzenlemesine izin ver +settings.default_update_style_desc = Temel dalın ardındaki çekme isteklerini güncellemek için kullanılan varsayılan güncelleme stili. +settings.wiki_rename_branch_main = Viki dal adını normalleştir + [graphs] component_loading = %s yükleniyor... component_loading_failed = %s yüklenemedi @@ -2736,7 +2771,7 @@ org_name_holder=Organizasyon Adı org_full_name_holder=Organizasyon Tam Adı org_name_helper=Organizasyon adları kısa ve hatırlanabilir olmalıdır. create_org=Organizasyon Oluştur -repo_updated=Güncellendi %s +repo_updated=%s güncellendi members=Üyeler teams=Takımlar code=Kod @@ -2792,7 +2827,7 @@ members.membership_visibility=Üyelik Görünürlüğü: members.public=Görünür members.public_helper=gizle members.private=Gizlenmiş -members.private_helper=görünür yap +members.private_helper=Görünür yap members.member_role=Üye Rolü: members.owner=Sahibi members.member=Üye @@ -3311,7 +3346,7 @@ config.git_max_diff_lines=Maksimum Değişiklik Satırı (tek bir dosya için) config.git_max_diff_line_characters=Maksimum Değişiklik Karakteri (tek bir satır için) config.git_max_diff_files=Maksimum Değişiklik Dosyaları (gösterilecek) config.git_gc_args=GC Argümanları -config.git_migrate_timeout=Göç İşlemi Zaman Aşımı +config.git_migrate_timeout=Göç işlemi zaman aşımı config.git_mirror_timeout=Yansı Güncelleme Zaman Aşımı config.git_clone_timeout=Klonlama İşlemi Zaman Aşımı config.git_pull_timeout=Çekme İşlemi Zaman Aşımı @@ -3363,7 +3398,7 @@ monitor.queue.settings.maxnumberworkers=En fazla çalışan Sayısı monitor.queue.settings.maxnumberworkers.placeholder=Şu anda %[1]d monitor.queue.settings.maxnumberworkers.error=En fazla çalışan sayısı bir sayı olmalıdır monitor.queue.settings.submit=Ayarları Güncelle -monitor.queue.settings.changed=Ayarlar Güncellendi +monitor.queue.settings.changed=Ayarlar güncellendi monitor.queue.settings.remove_all_items=Tümünü kaldır monitor.queue.settings.remove_all_items_done=Kuyruktaki tüm öğeler kaldırıldı. @@ -3771,23 +3806,23 @@ submodule=Alt modül [search] -project_kind = Projeleri ara... +project_kind = Projeleri ara… org_kind = Organizasyonları ara… team_kind = Takımları ara… search = Ara… code_kind = Kod ara… type_tooltip = Arama türü -repo_kind = Depoları ara... +repo_kind = Depoları ara… user_kind = Kullanıcıları ara… milestone_kind = Kilometre taşlarını ara... -branch_kind = Dalları ara... -package_kind = Paketleri ara... -commit_kind = Katkıları ara... -runner_kind = Çalıştırıcıları ara... +branch_kind = Dalları ara… +package_kind = Paketleri ara… +commit_kind = Katkıları ara… +runner_kind = Çalıştırıcıları ara… no_results = Eşleşen sonuç bulunamadı. code_search_unavailable = Kod araması şu anda kullanıma açık değildir. Lütfen site yöneticisi ile iletişime geçin. -issue_kind = Sorunları ara... -pull_kind = Birleştirme isteklerini ara... +issue_kind = Sorunları ara… +pull_kind = Birleştirme isteklerini ara… code_search_by_git_grep = Anlık kod araması sonuçları "git grep" komutu tarafından sağlanmaktadır. Site yöneticisinin kod endekslemesini açması durumunda daha iyi sonuçlar verilmesi mümkün olabilir. keyword_search_unavailable = Anahtar kelime ile arama şu anda kullanıma açık değildir. Lütfen site yöneticisi ile iletişime geçin. fuzzy_tooltip = Arama terimine yakın olan eşleşmeleri dahil et @@ -3795,3 +3830,15 @@ union_tooltip = Boşlukla ayrılmış anahtar kelime eşleşmelerini dahil et exact_tooltip = Sadece arama terimiyle tam uyuşan sonuçları dahit et. fuzzy = Bulanık exact = Tam +union = Anahtar sözcük +regexp = Düzenliİfade +regexp_tooltip = Arama terimini düzenli ifade olarak yorumla + +[munits.data] +b = B +kib = KiB +mib = MiB +gib = GiB +tib = TiB +pib = PiB +eib = EiB diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 11954dd625..d85a3b13e7 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -149,7 +149,7 @@ filter.not_archived = Не архівовано filter.public = Загальнодоступні filter.private = Приватні more_items = Більше пунктів -remove_label_str = Видалити об'єкт «%s» +remove_label_str = Видалити елемент «%s» new_repo.title = Новий репозиторій new_migrate.title = Нова міграція new_org.title = Нова організація @@ -195,8 +195,8 @@ buttons.list.task.tooltip = Додати список завдань buttons.heading.tooltip = Додати заголовок buttons.switch_to_legacy.tooltip = Використовувати застарілий редактор замість поточного buttons.disable_monospace_font = Вимкнути моноширинний шрифт -buttons.indent.tooltip = Вкласти предмет на один рівень -buttons.unindent.tooltip = Викласти об'єкт на один рівень +buttons.indent.tooltip = Вкласти елемент на один рівень +buttons.unindent.tooltip = Викласти елемент на один рівень buttons.mention.tooltip = Згадати користувача чи команду buttons.ref.tooltip = Послатися на задачу чи на запит на злиття buttons.enable_monospace_font = Увімкнути моноширинний шрифт @@ -348,7 +348,7 @@ app_slogan = Гасло екземпляра app_slogan_helper = Уведіть гасло вашого екземпляра тут. Залиште порожнім, аби вимкнути. run_user_helper = Імʼя користувача операційної системи, від якого запущено Forgejo. Зауважте, що цей користувач повинен мати доступ до кореневої теки репозиторію. smtp_from_invalid = Адреса з «Відправляти email від імені» недійсна -allow_dots_in_usernames = Дозволити користувачам використовувати крапки у своїх іменах. Не впливає на облікові записи, що вже існують. +allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на облікові записи, що вже існують. invalid_password_algorithm = Недійсний варіант алгоритму хешування паролів enable_update_checker_helper_forgejo = Наявність нових версій Forgejo періодично перевірятиметься через перевірку запису TXT DNS на release.forgejo.org. @@ -392,7 +392,7 @@ user_no_results=Відповідних користувачів не знайд org_no_results=Відповідних організацій не знайдено. code_no_results=Відповідний пошуковому запитанню код не знайдено. code_last_indexed_at=Останні індексовані %s -relevant_repositories = Відображаються лише релевантні репозиторії, переглянути результати без фільтру. +relevant_repositories = Показано лише релевантні репозиторії, переглянути результати без фільтру. relevant_repositories_tooltip = Приховано форки, а також репозиторії без теми, значка й опису. go_to = Перейти до stars_one = %d зірка @@ -469,7 +469,7 @@ invalid_code_forgot_password = Ваш код підтвердження неді reset_password_wrong_user = Ви ввійшли як %s, але посилання на відновлення було передбачене для %s back_to_sign_in = Назад до входу sign_in_openid = Продовжити з OpenID -openid_signin_desc = Введіть ваше посилання OpenID. Наприклад: alice.openid.example.org чи https://openid.example.org/alice. +openid_signin_desc = Уведіть свій OpenID URI. Наприклад: alice.openid.example.org чи https://openid.example.org/alice. invalid_password = Ваш пароль не відповідає тому, що був заданий при створенні облікового запису. hint_login = Вже маєте обліковий запис? Увійдіть зараз! hint_register = Потрібен обліковий запис? Зареєструйтеся зараз. @@ -813,9 +813,9 @@ manage_ssh_keys=Керування ключами SSH manage_ssh_principals=Управління SSH сертифікатами користувачів manage_gpg_keys=Керування ключами GPG add_key=Додати ключ -ssh_desc=Ці відкриті ключі SSH повʼязані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. Підтверджені ключі можна використати для підтвердження комітів Git, підписані з SSH. +ssh_desc=Ці відкриті ключі SSH повʼязані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. Підтверджені ключі можна використати для підтвердження комітів Git, підписаних із SSH. principal_desc=Ці настройки SSH сертифікатів вказані у вашому обліковому записі та надають повний доступ до ваших репозиторіїв. -gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють здійснювати перевірку комітів. +gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. ssh_helper=Потрібна допомога? Дивіться гід на GitHub з генерації ключів SSH або виправлення типових неполадок SSH. gpg_helper= Потрібна допомога? Перегляньте посібник GitHub про GPG . add_new_key=Додати SSH ключ @@ -893,7 +893,7 @@ manage_oauth2_applications=Керування програмами OAuth2 edit_oauth2_application=Редагувати програму OAuth2 oauth2_applications_desc=Програми OAuth2 дають можливість вашим стороннім програмам надійно аутентифікувати користувачів у цьому екземплярі Forgejo. remove_oauth2_application=Видалити програму OAuth2 -remove_oauth2_application_desc=Видалення програми OAuth2 скасовує доступ до всіх підписаних маркерів доступу. Продовжити? +remove_oauth2_application_desc=Видалення програми OAuth2 скасує доступ до всіх підписаних токенів доступу. Продовжити? remove_oauth2_application_success=Програму видалено. create_oauth2_application=Створити новий додаток OAuth2 create_oauth2_application_button=Створити програму @@ -1077,6 +1077,9 @@ regenerate_token = Згенерувати знову access_token_regeneration = Згенерувати новий токен доступу quota.sizes.assets.all = Ресурси +access_token_regeneration_desc = Регенерація токена скасує доступ програм, які використовують цей токен, до вашого облікового запису. Це незворотна дія. Продовжити? +regenerate_token_success = Токен згенеровано наново. Програми, які його використовують, більше не мають доступу до вашого облікового запису; ви повинні відновити доступ за допомогою нового токена. + [repo] owner=Власник owner_helper=Деякі організації можуть не відображатися у випадаючому списку через максимальну кількість репозиторііїв. @@ -1143,7 +1146,7 @@ forks=Форки reactions_more=додати %d більше unit_disabled=Адміністратор сайту вимкнув цей розділ репозиторію. language_other=Інші -adopt_search=Введіть ім'я користувач_ки для пошуку неприйнятих репозиторіїв… (залиште порожнім, щоб знайти всі) +adopt_search=Уведіть ім'я користувач_ки для пошуку неприйнятих репозиторіїв… (залиште порожнім, щоб знайти всі) adopt_preexisting_label=Прийняті файли adopt_preexisting=Прийняти вже існуючі файли adopt_preexisting_content=Створити репозиторій з %s @@ -1168,7 +1171,7 @@ desc.archived=Архівний template.items=Елементи шаблону template.git_content=Вміст Git (типова гілка) -template.git_hooks=Перехоплювачі Git +template.git_hooks=Git-хуки template.webhooks=Webhook'и template.topics=Теми template.avatar=Аватар @@ -1285,7 +1288,7 @@ video_not_supported_in_browser=Ваш браузер не підтримує т audio_not_supported_in_browser=Ваш браузер не підтримує тег HTML5 «audio». stored_lfs=Збережено з Git LFS symbolic_link=Символічне посилання -commit_graph=Графік комітів +commit_graph=Граф комітів commit_graph.select=Виберіть гілки commit_graph.hide_pr_refs=Приховати запити на злиття commit_graph.monochrome=Монохром @@ -1402,9 +1405,9 @@ issues.new.open_projects=Відкриті проєкти issues.new.closed_projects=Закриті проєкти issues.new.no_items=Немає елементів issues.new.milestone=Етап -issues.new.no_milestone=Етап відсутній +issues.new.no_milestone=Немає етапу issues.new.clear_milestone=Очистити етап -issues.new.open_milestone=Активні етапи +issues.new.open_milestone=Відкриті етапи issues.new.closed_milestone=Закриті етапи issues.new.assignees=Виконавці issues.new.clear_assignees=Прибрати виконавців @@ -1504,7 +1507,7 @@ issues.closed_at=`закриває цю задачу %s` issues.reopened_at=`повторно відкриває цю задачу %s` issues.commit_ref_at=`посилається на цю задачу в коміті %s` issues.ref_issue_from=`посилається на цю задачу %[3]s %[1]s` -issues.ref_pull_from=`посилається на цей запит злиття %[3]s %[1]s` +issues.ref_pull_from=`посилається на цей запит на злиття %[3]s %[1]s` issues.ref_closing_from=`посилається в запиті на злиття %[3]s, який закриє цю задачу, %[1]s` issues.ref_reopening_from=`посилається в запиті на злиття %[3]s, який повторно відкриє цю задачу, %[1]s` issues.ref_closed_from=`закрив цю задачу %[4]s %[2]s` @@ -1799,7 +1802,7 @@ wiki.delete_page_button=Видалити сторінку wiki.page_already_exists=Вікі-сторінка з таким самим ім'ям вже існує. wiki.pages=Сторінки wiki.last_updated=Останні оновлення %s -wiki.page_name_desc=Введіть назву вікі-сторінки. Деякі зі спеціальних імен: «Home», «_Sidebar» та «_Footer». +wiki.page_name_desc=Уведіть назву вікі-сторінки. Деякі зі спеціальних імен: «Home», «_Sidebar» та «_Footer». activity=Активність activity.period.filter_label=Період: @@ -1844,7 +1847,7 @@ activity.unresolved_conv_label=Відкрити activity.title.releases_1=%d випуск activity.title.releases_n=%d випусків activity.title.releases_published_by=%s опубліковано %s -activity.published_release_label=Опубліковано +activity.published_release_label=Випуск activity.no_git_activity=У цей період не було здійснено жодних дій. activity.git_stats_exclude_merges=Не враховуючи злиття, activity.git_stats_author_1=%d автор @@ -1886,7 +1889,7 @@ settings.collaboration.read=Читати settings.collaboration.owner=Власник settings.collaboration.undefined=Не визначено settings.hooks=Веб-хуки -settings.githooks=Git хуки +settings.githooks=Git-хуки settings.basic_settings=Основні налаштування settings.mirror_settings=Налаштування дзеркала settings.mirror_settings.mirrored_repository=Віддзеркалений репозиторій @@ -1896,7 +1899,7 @@ settings.mirror_settings.direction.push=Push settings.mirror_settings.last_update=Останнє оновлення settings.mirror_settings.push_mirror.none=Не налаштовано дзеркало push settings.mirror_settings.push_mirror.remote_url=URL віддаленого репозиторію Git -settings.mirror_settings.push_mirror.add=Додати Push дзеркало +settings.mirror_settings.push_mirror.add=Додати push-дзеркало settings.sync_mirror=Синхронізувати зараз settings.site=Веб-сайт @@ -1927,9 +1930,9 @@ settings.pulls_desc=Увімкнути запити на злиття в реп settings.pulls.ignore_whitespace=Ігнорувати пробіл у конфліктах settings.pulls.enable_autodetect_manual_merge=Увімкнути автовизначення ручного злиття (Примітка: у деяких особливий випадках можуть виникнуть помилки) settings.pulls.default_delete_branch_after_merge=Видаляти гілку запиту злиття, коли його прийнято -settings.projects_desc=Увімкнути проєкти у репозиторії +settings.projects_desc=Увімкнути проєкти в репозиторії settings.admin_settings=Налаштування адміністратора -settings.admin_enable_health_check=Включити перевірки працездатності репозиторію (git fsck) +settings.admin_enable_health_check=Увімкнути перевірки стану репозиторію (git fsck) settings.admin_enable_close_issues_via_commit_in_any_branch=Закрити задачу за допомогою коміта, зробленого не в головній гілці settings.danger_zone=Небезпечна зона settings.new_owner_has_same_repo=Новий власник вже має репозиторій з такою назвою. Будь ласка, виберіть інше ім'я. @@ -2075,7 +2078,7 @@ settings.event_pull_request_review_desc=Запит на злиття схвал settings.event_pull_request_sync=Синхронізовано settings.event_pull_request_sync_desc=Гілку автоматично оновлено цільовою гілкою. settings.branch_filter=Фільтр гілок -settings.branch_filter_desc=Білий список гілок для push, створення гілок та видалення гілок, визначається як шаблон glob. Якщо він порожній або містить *, то реєструються події для всіх гілок. Дивіться синтаксис у документації на %[2]s. Наприклад: master, {master,release*}. +settings.branch_filter_desc=Білий список гілок для push, створення гілок та видалення гілок, визначається як шаблон glob. Якщо він порожній або містить *, то реєструються події для всіх гілок. Дивіться синтаксис у документації %[2]s. Приклади: master, {master,release*}. settings.active=Активний settings.active_helper=Інформацію про викликані події буде надіслано за цією веб-хук URL-адресою. settings.add_hook_success=Веб-хук було додано. @@ -2154,7 +2157,7 @@ settings.edit_protected_branch=Редагувати settings.protected_branch_required_approvals_min=Число необхідних схвалень не може бути від'ємним. settings.tags=Теги settings.tags.protection=Захист тегу -settings.tags.protection.pattern=Шаблон тега +settings.tags.protection.pattern=Шаблон тегів settings.tags.protection.allowed=Дозволено settings.tags.protection.allowed.users=Дозволені користувачі settings.tags.protection.allowed.teams=Дозволені команди @@ -2263,7 +2266,7 @@ release.detail=Деталі релізу release.tags=Теги release.new_release=Новий випуск release.draft=Чернетка -release.prerelease=Пре-реліз +release.prerelease=Попередній випуск release.stable=Стабільний release.compare=Порівняти release.edit=Редагувати @@ -2275,15 +2278,15 @@ release.edit_subheader=Публікація релізів допоможе ва release.tag_name=Назва тегу release.target=Ціль release.tag_helper=Виберіть існуючий тег або створіть новий. -release.prerelease_desc=Позначити як пре-реліз +release.prerelease_desc=Позначити як попередній випуск release.prerelease_helper=Позначте цей випуск непридатним для ПРОД використання. release.cancel=Відмінити -release.publish=Опублікувати реліз +release.publish=Опублікувати випуск release.save_draft=Зберегти чернетку -release.edit_release=Оновити реліз -release.delete_release=Видалити реліз +release.edit_release=Оновити випуск +release.delete_release=Видалити випуск release.delete_tag=Видалити тег -release.deletion=Видалити реліз +release.deletion=Видалити випуск release.deletion_success=Реліз, було видалено. release.deletion_tag_desc=Буде видалено цей тег із репозиторію. Вміст репозиторія та історія залишаться незмінними. Продовжити? release.deletion_tag_success=Тег видалено. @@ -2446,7 +2449,7 @@ settings.event_package = Пакунок settings.event_package_desc = Пакунок у репозиторії створено або видалено. settings.new_owner_blocked_doer = Новий власник заблокував вас. settings.transfer_quota_exceeded = Новий власник (%s) перевищив квоту. Репозиторій не передано. -release.title_empty = Заголовок не може бути порожнім. +release.title_empty = Назва не може бути порожньою. issues.role.member_helper = Цей користувач є членом організації, що володіє цим репозиторієм. wiki.page_content = Вміст сторінки wiki.page_title = Заголовок сторінки @@ -2530,7 +2533,7 @@ settings.add_web_hook_desc = Інтегрувати Увійдіть, щоб створити новий запит на злиття. @@ -2541,8 +2544,8 @@ auto_init_description = Почніть історію Git з README і за ба new_from_template_description = Можете вибрати наявний шаблон репозиторію на цьому екземплярі і застосувати його налаштування. form.string_too_long = Довжина введеного рядка більша за %d символів. form.name_reserved = Назву репозиторію «%s» зарезервовано. -form.name_pattern_not_allowed = Шаблон «%s» не допускається у назві репозиторію. -settings.wiki_rename_branch_main_desc = Перейменувати внутрішню гілку, яка використовується у вікі, на «%s». Ця зміна є остаточною і її неможливо скасувати. +form.name_pattern_not_allowed = Вираз «%s» не може бути частиною назви репозиторію. +settings.wiki_rename_branch_main_desc = Перейменувати внутрішню гілку, яка використовується для вікі, на «%s». Ця зміна є остаточною і її неможливо скасувати. wiki.reserved_page = Назву вікі-сторінки «%s» зарезервовано. stars = Зірки mirror_public_key = Відкритий SSH-ключ @@ -2696,6 +2699,84 @@ settings.sourcehut_builds.secrets_helper = Надати завданню дос settings.remove_protected_branch_failed = Не вдалося видалити правило захисту гілок «%s». release.summary_card_alt = Підсумкова картка випуску «%s» в репозиторії %s +view_git_blame = Переглянути git blame +vendored = Сторонній +editor.file_editing_no_longer_exists = Файл «%s», який ви редагуєте, більше не існує у цьому репозиторії. +editor.file_deleting_no_longer_exists = Файл «%s», який ви видаляєте, більше не існує у цьому репозиторії. +editor.file_already_exists = Файл із назвою «%s» вже є у цьому репозиторії. +editor.push_rejected_no_message = Зміну відхилено сервером без повідомлення. Будь ласка, перевірте Git-хуки. +editor.push_rejected = Зміну відхилено сервером. Будь ласка, перевірте Git-хуки. +editor.unable_to_upload_files = Не вдалося завантажити файли в «%s» через помилку: %v +editor.upload_files_to_dir = Завантажити файли в «%s» +editor.cannot_commit_to_protected_branch = Неможливо здійснити коміт до захищеної гілки «%s». +commits.renamed_from = Перейменовано з %s +commits.ssh_key_fingerprint = Відбиток ключа SSH +commits.view_path = Переглянути на цьому етапі історії +issues.filter_sort.relevance = За відповідністю +issues.action_check = Поставити/зняти позначку +issues.action_check_all = Поставити/зняти позначку з усіх елементів +issues.closed_by = від %[3]s закрито %[1]s +issues.closed_by_fake = від %[2]s закрито %[1]s +issues.label_archived_filter = Показати архівовані мітки +pulls.allow_edits_from_maintainers_err = Не вдалося оновити +pulls.has_viewed_file = Переглянуто +pulls.viewed_files_label = %[1]d з %[2]d файлів переглянуто +pulls.status_checks_hide_all = Приховати всі перевірки +milestones.new_subheader = Етапи допомагають організувати задачі та відстежувати прогрес їх виконання. +milestones.filter_sort.name = За назвою +wiki.delete_page_notice_1 = Видалення вікі-сторінки «%s» неможливо скасувати. Продовжити? +activity.published_prerelease_label = Пре-реліз +settings.mirror_settings.docs.doc_link_pull_section = розділ документації «Отримання з віддаленого репозиторію». +settings.mirror_settings.docs.pulling_remote_title = Отримання з віддаленого репозиторію +settings.mirror_settings.push_mirror.edit_sync_time = Змінити інтервал синхронізації дзеркала +settings.mirror_settings.push_mirror.none_ssh = Немає +settings.pull_mirror_sync_in_progress = Триває отримання змін з віддаленого репозиторію %s. +settings.pull_mirror_sync_quota_exceeded = Перевищено квоту, отримання змін неможливе. +settings.push_mirror_sync_in_progress = Триває надсилання змін до віддаленого репозиторію %s. +settings.pulls.allow_rebase_update = Увімкнути оновлення гілки запиту на злиття за допомогою перебазування +settings.releases_desc = Увімкнути випуски в репозиторії +settings.admin_code_indexer = Індексатор коду +settings.admin_stats_indexer = Індексатор статистики коду +settings.admin_indexer_commit_sha = Останній індексований коміт +settings.admin_indexer_unindexed = Не індексовано +settings.enter_repo_name = Уведіть ім'я власника і назву репозиторію, як показано: +settings.wiki_rename_branch_main = Нормалізувати назву вікі-гілки +settings.wiki_rename_branch_main_notices_1 = Цю операцію НЕМОЖЛИВО скасувати. +settings.wiki_rename_branch_main_notices_2 = Внутрішню вікі-гілку репозиторію %s буде назавжди перейменовано. Існуючі перевірки потрібно буде оновити. +settings.wiki_branch_rename_success = Назву вікі-гілки репозиторію успішно нормалізовано. +settings.wiki_branch_rename_failure = Не вдалося нормалізувати назву вікі-гілки репозиторію. +settings.add_webhook.invalid_path = Шлях не повинен містити частини «.» або «..» і не повинен бути порожнім. Він не може починатися або закінчуватися косою рискою. +settings.discord_icon_url.exceeds_max_length = URL-адреса значка не може бути довшою, ніж 2048 символи +settings.web_hook_name_telegram = Telegram +settings.sourcehut_builds.manifest_path = Шлях до маніфесту збірки +settings.sourcehut_builds.visibility = Видимість завдань +settings.sourcehut_builds.access_token_helper = Токен доступу, який має дозвіл JOBS:RW. Згенеруйте токен builds.sr.ht або токен builds.sr.ht з доступом до секретів на meta.sr.ht. +settings.protect_status_check_patterns_desc = Уведіть шаблони, щоб вказати, які перевірки стану повинні пройти гілки, перш ніж їх буде об'єднано у гілку, що відповідає цьому правилу. Кожен рядок визначає шаблон. Шаблони не можуть бути порожніми. +settings.protect_invalid_status_check_pattern = Недійсний шаблон перевірки стану: «%s». +settings.protect_no_valid_status_check_patterns = Немає дійсних шаблонів перевірки стану. +settings.protect_patterns = Шаблони +settings.protected_branch_duplicate_rule_name = Для цього набору гілок уже є правило +settings.thread_id = Thread ID +settings.matrix.access_token_helper = Рекомендується створити окремий обліковий запис Matrix. Токен доступу можна отримати у вебклієнті Element (у приватній/інкогніто вкладці): User menu (вгорі ліворуч) > All settings > Help & About > Advanced > Access Token (під URL-адресою Homeserver). Закрийте приватну вкладку (вихід із системи зробить токен недійсним). +settings.matrix.room_id_helper = ID кімнати можна отримати у вебклієнті Element: Room Settings > Advanced > Internal room ID. Приклад: %s. +settings.unarchive.header = Розархівувати цей репозиторій +settings.unarchive.text = Розархівування репозиторію відновить можливість надсилати до нього коміти і виконувати push, а також створювати задачі і запити на злиття. +settings.unarchive.success = Репозиторій успішно розархівовано. +settings.unarchive.error = Сталася помилка при спробі розархівувати репозиторій. Докладнішу інформацію див. у журналі. +diff.git-notes.remove-body = Цю примітку буде видалено. +diff.has_escaped = У цьому рядку є приховані символи Юнікоду +diff.show_file_tree = Показати дерево файлів +diff.hide_file_tree = Приховати дерево файлів +tag.ahead.target = до %s після цього тегу +release.title = Назва випуску +release.deletion_desc = Видалення випуску видаляє його лише з Forgejo. При цьому тег Git, вміст репозиторію чи його історію не буде змінено. Продовжити? +release.hide_archive_links = Приховати автоматично генеровані архіви +release.hide_archive_links_helper = Приховати для цього випуску архіви вихідного коду, що генеруються автоматично. Наприклад, якщо ви завантажуєте свої архіви. +release.system_generated = Це вкладення згенеровано автоматично. +branch.delete_desc = Видалення гілки є остаточним. Хоча видалена гілка може існувати ще деякий час до того, як її буде видалено, цю дію НЕМОЖЛИВО скасувати в більшості випадків. Продовжити? +branch.branch_name_conflict = Назва гілки «%s» конфліктує з наявною гілкою «%s». +branch.tag_collision = Неможливо створити гілку «%s», оскільки у репозиторії вже є тег із такою назвою. + [graphs] contributors.what = внески component_loading_info = Це може зайняти деякий час… @@ -2825,6 +2906,9 @@ teams.write_access = Запис teams.invite.by = Вас запрошує %s teams.invite_team_member.list = Запрошення в очікуванні +form.name_pattern_not_allowed = Вираз «%s» не може бути частиною назви організації. +teams.add_nonexistent_repo = Репозиторій, який ви намагаєтеся додати, не існує. Спочатку створіть його. + [admin] dashboard=Панель управління users=Облікові записи користувачів @@ -2935,7 +3019,7 @@ users.is_activated=Обліковий запис користувача увім users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора users.is_restricted=Обмежений -users.allow_git_hook=Може створювати Git хуки +users.allow_git_hook=Може створювати Git-хуки users.allow_git_hook_tooltip=Git хуки виконуються від імені користувача OS сервісу Forgejo і мають однаковий рівень доступу до хоста. Як результат, користувачі з доступом до Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних, що використовуються в Forgejo. Отже, вони також здатні отримати права адміністратора Forgejo. users.allow_import_local=Може імпортувати локальні репозиторії users.allow_create_organization=Може створювати організації @@ -3077,7 +3161,7 @@ auths.sspi_default_language_helper=Типова мова для користув auths.tips=Поради auths.tips.oauth2.general=Автентифікація OAuth2 auths.tip.oauth2_provider=Постачальник OAuth2 -auths.tip.bitbucket=Створіть OAuth URI на сторінці %s +auths.tip.bitbucket=Зареєструйте нового споживача OAuth на %s і додайте дозвіл «Обліковий запис» — «Читання» auths.tip.nextcloud=`Зареєструйте нового споживача OAuth у вашому екземплярі за допомогою наступного меню "Налаштування -> Безпека -> клієнт OAuth 2.0"` auths.tip.dropbox=Створіть новий додаток на %s auths.tip.facebook=Зареєструйте новий додаток на %s і додайте модуль «Facebook Login» @@ -3085,7 +3169,7 @@ auths.tip.github=Зареєструйте новий додаток OAuth на % auths.tip.gitlab=Додайте новий додаток на https://gitlab.com/profile/applications auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 в консолі Google API на сторінці %s auths.tip.openid_connect=Використовуйте OpenID Connect Discovery URL (/.well-known/openid-configuration) для автоматичної настройки входу OAuth -auths.tip.twitter=Перейдіть на %s, створіть програму і переконайтеся, що включена опція «Дозволити цю програму для входу в систему за допомогою Twitter» +auths.tip.twitter=Перейдіть на %s, створіть програму і переконайтеся, що ввімкнено опцію «Дозволити використання цієї програми для входу через Twitter» auths.tip.discord=Зареєструйте новий додаток на %s auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) @@ -3282,7 +3366,7 @@ users.purge_help = Примусово видалити користувача і dashboard.cleanup_packages = Очистити непридатні пакунки monitor.last_execution_result = Результат repos.lfs_size = Розмір LFS -config.allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на існуючі облікові записи. +config.allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на облікові записи, що вже існують. config.mailer_enable_helo = Увімкнути HELO users.organization_creation.description = Дозволити створення нових організацій. users.cannot_delete_self = Ви не можете видалити себе @@ -3341,6 +3425,12 @@ config.access_log_template = Шаблон журналу доступу config.set_setting_failed = Не вдалося встановити параметр %s monitor.download_diagnosis_report = Завантажити діагностичний звіт +dashboard.update_checker = Перевірка оновлень +auths.map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи LDAP +auths.enable_ldap_groups = Увімкнути групи LDAP +auths.oauth2_map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи. +config.mailer_smtp_addr = Адреса SMTP + [action] create_repo=створив(ла) репозиторій %s rename_repo=репозиторій перейменовано з %[1]s на %[3]s @@ -3443,7 +3533,7 @@ settings.delete.notice = Ви збираєтеся видалити %s (%s). Ц details.author = Автор title = Пакунки arch.version.backup = Резервне копіювання -arch.version.conflicts = Суперечки +arch.version.conflicts = Конфлікти arch.version.replaces = Заміни arch.version.provides = Надає arch.version.groups = Група @@ -3563,6 +3653,13 @@ owner.settings.chef.keypair.description = Запити до реєстру Chef nuget.dependency.framework = Цільовий фреймворк owner.settings.cleanuprules.preview.overview = Заплановано видалити %d пакунків. +arch.pacman.repo.multi = %s має одну й ту саму версію в різних дистрибутивах. +maven.install2 = Запустити з командного рядка: +maven.download = Щоб завантажити залежність, запустіть із командного рядка: +npm.dependencies.bundle = Пакетні залежності +npm.dependencies.peer = Однорангові залежності +owner.settings.cleanuprules.pattern_full_match = Застосувати шаблон до повної назви пакунка + [secrets] deletion = Видалити секрет creation.success = Секрет «%s» додано. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index d87ad53676..fd6e625b5e 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -70,7 +70,7 @@ your_starred=点赞 your_settings=设置 all=所有 -sources=来源 +sources=原创 mirrors=镜像 collaborative=协作 forks=派生 @@ -1062,7 +1062,7 @@ language.description = 此语言将保存到您的账号中,并在您登录后 language.localization_project = 帮助我们将 Forgejo 翻译成您的语言!了解更多。 user_block_yourself = 您不能屏蔽自己。 pronouns_custom_label = 自定义代词 -change_username_redirect_prompt.with_cooldown.one = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 +change_username_redirect_prompt.with_cooldown.one = 旧用户名将在 %[1]d 天的保护期后对所有人可用。您仍可以在此期间重新认领旧用户名。 change_username_redirect_prompt.with_cooldown.few = 旧用户名将在 %[1]d 天的保护期后对所有人可用,您仍可以在此期间重新认领旧用户名。 keep_pronouns_private = 仅向已认证用户显示代词 keep_pronouns_private.description = 这将对未登录的访问者隐藏您的代词。 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index fba51a391e..ddc31730d7 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -1062,7 +1062,7 @@ language.localization_project = 幫助我們翻譯 Forgejo 至您的語言!公開活動只有你和站點管理員可見。 quota.rule.exceeded = 已超出 diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json index 3d87c7ee95..a0fc1de24b 100644 --- a/options/locale_next/locale_ar.json +++ b/options/locale_next/locale_ar.json @@ -3,5 +3,7 @@ "home.welcome.no_activity": "لا يوجد نشاط", "home.explore_repos": "اكتشف المستودعات", "home.explore_users": "اكتشف المستخدمين", - "home.explore_orgs": "اكتشف المنظمات" + "home.explore_orgs": "اكتشف المنظمات", + "moderation.abuse_category.malware": "برمجية خبيثة", + "moderation.abuse_category.illegal_content": "المحتوى غير المشروع" } diff --git a/options/locale_next/locale_be.json b/options/locale_next/locale_be.json index 0967ef424b..40dfec0b41 100644 --- a/options/locale_next/locale_be.json +++ b/options/locale_next/locale_be.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "Шкодная праграма" +} diff --git a/options/locale_next/locale_bn.json b/options/locale_next/locale_bn.json index 0967ef424b..56a6e6dae4 100644 --- a/options/locale_next/locale_bn.json +++ b/options/locale_next/locale_bn.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "ম্যালওয়্যার" +} diff --git a/options/locale_next/locale_ca.json b/options/locale_next/locale_ca.json index 8eefc65a1a..606ab99173 100644 --- a/options/locale_next/locale_ca.json +++ b/options/locale_next/locale_ca.json @@ -1,3 +1,4 @@ { - "search.milestone_kind": "Cerca fites..." + "search.milestone_kind": "Cerca fites...", + "moderation.abuse_category.malware": "Programari maliciós" } diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 3a32d3a95a..7fb22a0d33 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -110,5 +110,6 @@ "profile.edit.link": "Upravit profil", "feed.atom.link": "Zdroj Atom", "keys.ssh.link": "Klíče SSH", - "keys.gpg.link": "Klíče GPG" + "keys.gpg.link": "Klíče GPG", + "mail.actions.run_info_sha": "Revize: %[1]s" } diff --git a/options/locale_next/locale_da.json b/options/locale_next/locale_da.json index 8315e06bcc..c43a7e3d37 100644 --- a/options/locale_next/locale_da.json +++ b/options/locale_next/locale_da.json @@ -97,5 +97,10 @@ "settings.visibility.description": "Profilsynlighed påvirker andres adgang til dine ikke-private depoter. Læs mere", "avatar.constraints_hint": "Brugerdefineret avatar må ikke overstige %[1]s i størrelse eller være større end %[2]dx%[3]d pixels", "repo.diff.commit.next-short": "Næste", - "repo.diff.commit.previous-short": "Forrige" + "repo.diff.commit.previous-short": "Forrige", + "profile.actions.tooltip": "Flere handlinger", + "profile.edit.link": "Redigere profil", + "feed.atom.link": "Atom feed", + "keys.ssh.link": "SSH Nøgler", + "keys.gpg.link": "GPG Nøgler" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index 3847de2b43..a582efdc27 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -102,5 +102,6 @@ "feed.atom.link": "Atom-Feed", "keys.ssh.link": "SSH-Schlüssel", "keys.gpg.link": "GPG-Schlüssel", - "profile.actions.tooltip": "Mehr Aktionen" + "profile.actions.tooltip": "Mehr Aktionen", + "mail.actions.run_info_sha": "Commit: %[1]s" } diff --git a/options/locale_next/locale_es-ES.json b/options/locale_next/locale_es-ES.json index 37edef9211..82fbcfb4b6 100644 --- a/options/locale_next/locale_es-ES.json +++ b/options/locale_next/locale_es-ES.json @@ -30,5 +30,6 @@ "relativetime.future": "en el futuro", "home.explore_repos": "Explorar repositorios", "home.explore_users": "Explorar usuarios", - "home.explore_orgs": "Explorar organizaciones" + "home.explore_orgs": "Explorar organizaciones", + "moderation.abuse_category.malware": "Malware" } diff --git a/options/locale_next/locale_fi-FI.json b/options/locale_next/locale_fi-FI.json index cb26d76e66..14481acf6f 100644 --- a/options/locale_next/locale_fi-FI.json +++ b/options/locale_next/locale_fi-FI.json @@ -64,5 +64,6 @@ "mail.actions.run_info_cur_status": "Tämän juoksun tila: %[1]s (juuri päivitetty %[2]s:sta)", "mail.actions.run_info_previous_status": "Edellisen ajon tila: %[1]s", "mail.actions.run_info_ref": "Haara: %[1]s (%[2]s)", - "mail.actions.run_info_trigger": "Laukaistui, koska: %[1]s, tekijänä: %[2]s" + "mail.actions.run_info_trigger": "Laukaistui, koska: %[1]s, tekijänä: %[2]s", + "moderation.abuse_category.malware": "Haittaohjelma" } diff --git a/options/locale_next/locale_hu-HU.json b/options/locale_next/locale_hu-HU.json index 9d52509102..1c02002e6e 100644 --- a/options/locale_next/locale_hu-HU.json +++ b/options/locale_next/locale_hu-HU.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "egyesítve %[1]d változás(ok) a %[2]s-ból %[3]s-ba %[4]s", "repo.pulls.title_desc": "egyesíteni szeretné %[1]d változás(oka)t a(z) %[2]s-ból %[3]s-ba", - "search.milestone_kind": "Mérföldkövek keresése..." + "search.milestone_kind": "Mérföldkövek keresése...", + "moderation.abuse_category.malware": "Malware" } diff --git a/options/locale_next/locale_id-ID.json b/options/locale_next/locale_id-ID.json index f2dac8114f..0cf7ea4799 100644 --- a/options/locale_next/locale_id-ID.json +++ b/options/locale_next/locale_id-ID.json @@ -1,8 +1,9 @@ { - "repo.pulls.merged_title_desc": { - "other": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s" - }, - "repo.pulls.title_desc": { - "other": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s" - } + "repo.pulls.merged_title_desc": { + "other": "commit %[1]d telah digabungkan dari %[2]s menjadi %[3]s %[4]s" + }, + "repo.pulls.title_desc": { + "other": "ingin menggabungkan komit %[1]d dari %[2]s menuju %[3]s" + }, + "moderation.abuse_category.malware": "Perangkat pembahaya" } diff --git a/options/locale_next/locale_ja-JP.json b/options/locale_next/locale_ja-JP.json index 40edf8cb90..c4b3a0a2e0 100644 --- a/options/locale_next/locale_ja-JP.json +++ b/options/locale_next/locale_ja-JP.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "が %[1]d 個のコミットを %[2]s から %[3]s へマージ %[4]s", "repo.pulls.title_desc": "が %[2]s から %[3]s への %[1]d コミットのマージを希望しています", - "search.milestone_kind": "マイルストーンを検索..." + "search.milestone_kind": "マイルストーンを検索...", + "moderation.abuse_category.malware": "悪意のコード" } diff --git a/options/locale_next/locale_ko-KR.json b/options/locale_next/locale_ko-KR.json index 98c949d517..2e51144cb7 100644 --- a/options/locale_next/locale_ko-KR.json +++ b/options/locale_next/locale_ko-KR.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "님이 %[2]s 에서 %[3]s 로 %[1]d 커밋을 %[4]s 병합함", "repo.pulls.title_desc": "%[2]s 에서 %[3]s 로 %[1]d개의 커밋들을 병합하려함", - "home.welcome.no_activity": "활동 없음" + "home.welcome.no_activity": "활동 없음", + "moderation.abuse_category.malware": "악성 소프트웨어" } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 242c22bbf3..75835bc89c 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -110,5 +110,6 @@ "profile.edit.link": "Labot profilu", "feed.atom.link": "Atom barotne", "keys.ssh.link": "SSH atslēgas", - "keys.gpg.link": "GPG atslēgas" + "keys.gpg.link": "GPG atslēgas", + "mail.actions.run_info_sha": "Iesūtījums: %[1]s" } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index 24268e2082..c1769e55e6 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -102,5 +102,6 @@ "keys.ssh.link": "SSH-Slötels", "keys.gpg.link": "GPG-Slötels", "profile.actions.tooltip": "Mehr Aktioonen", - "profile.edit.link": "Profil bewarken" + "profile.edit.link": "Profil bewarken", + "mail.actions.run_info_sha": "Kommitteren: %[1]s" } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 690a522b80..5c9c8a9b07 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -102,5 +102,6 @@ "repo.diff.commit.previous-short": "Vorige", "admin.dashboard.cleanup_offline_runners": "Offline runners opruimen", "settings.visibility.description": "Profielzichtbaarheid beïnvloedt de mogelijkheid van anderen om toegang te krijgen tot je niet-privé repositories. Lees meer", - "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels" + "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels", + "mail.actions.run_info_sha": "Commit: %[1]s" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index acff9ff364..0fc65a7dd6 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -83,7 +83,7 @@ "moderation.abuse_category": "Categoria", "moderation.abuse_category.placeholder": "Selecione uma categoria", "moderation.abuse_category.spam": "Spam", - "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.malware": "Software malicioso", "moderation.abuse_category.illegal_content": "Conteúdo ilegal", "moderation.abuse_category.other_violations": "Outras violações de regras da plataforma", "moderation.report_remarks": "Observações", @@ -110,5 +110,6 @@ "profile.edit.link": "Editar perfil", "feed.atom.link": "Feed Atom", "keys.ssh.link": "Chaves SSH", - "keys.gpg.link": "Chaves GPG" + "keys.gpg.link": "Chaves GPG", + "mail.actions.run_info_sha": "Commit: %[1]s" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 922e2612af..2b743ad8a5 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -110,5 +110,6 @@ "feed.atom.link": "Atom-лента", "keys.ssh.link": "Ключи SSH", "keys.gpg.link": "Ключи GPG", - "profile.edit.link": "Изменить профиль" + "profile.edit.link": "Изменить профиль", + "mail.actions.run_info_sha": "Коммит: %[1]s" } diff --git a/options/locale_next/locale_sr-SP.json b/options/locale_next/locale_sr-SP.json index 0967ef424b..611f997b3e 100644 --- a/options/locale_next/locale_sr-SP.json +++ b/options/locale_next/locale_sr-SP.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "Малвер" +} diff --git a/options/locale_next/locale_tr-TR.json b/options/locale_next/locale_tr-TR.json index b0e34e677f..fa71cb8e7d 100644 --- a/options/locale_next/locale_tr-TR.json +++ b/options/locale_next/locale_tr-TR.json @@ -1,5 +1,6 @@ { "repo.pulls.merged_title_desc": "%[4]s %[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirdi", "repo.pulls.title_desc": "%[2]s içindeki %[1]d işlemeyi %[3]s ile birleştirmek istiyor", - "search.milestone_kind": "Kilometre taşlarını ara..." + "search.milestone_kind": "Kilometre taşlarını ara...", + "moderation.abuse_category.malware": "Malware" } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 33cb5a41a3..81b69ec859 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -110,5 +110,6 @@ "keys.gpg.link": "Ключі GPG", "profile.edit.link": "Редагувати профіль", "feed.atom.link": "Стрічка Atom", - "profile.actions.tooltip": "Більше дій" + "profile.actions.tooltip": "Більше дій", + "mail.actions.run_info_sha": "Коміт: %[1]s" } diff --git a/options/locale_next/locale_vi.json b/options/locale_next/locale_vi.json index 0967ef424b..7ae64113b6 100644 --- a/options/locale_next/locale_vi.json +++ b/options/locale_next/locale_vi.json @@ -1 +1,3 @@ -{} +{ + "moderation.abuse_category.malware": "Phần mềm ác ý" +} diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 0f408997bf..9fc38dae15 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -71,12 +71,12 @@ "editor.textarea.shift_tab_hint": "此行无缩进。再次按 Shift + Tab 或按 Escape 退出编辑器。", "admin.dashboard.cleanup_offline_runners": "清理离线运行器", "settings.visibility.description": "个人资料可见性设置会影响他人对您的非私有仓库的访问。了解更多", - "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,或大于 %[2]d×%[3]d 像素", + "avatar.constraints_hint": "自定义头像大小不得超过 %[1]s,且分辨率不得大于 %[2]d×%[3]d 像素", "keys.ssh.link": "SSH 密钥", "keys.gpg.link": "GPG 密钥", "profile.actions.tooltip": "更多操作", - "repo.diff.commit.next-short": "下个", - "repo.diff.commit.previous-short": "上个", + "repo.diff.commit.next-short": "下一个", + "repo.diff.commit.previous-short": "上一个", "feed.atom.link": "Atom 订阅源", "profile.edit.link": "编辑个人资料" } diff --git a/options/locale_next/locale_zh-HK.json b/options/locale_next/locale_zh-HK.json index 6baf89e022..7d624f24df 100644 --- a/options/locale_next/locale_zh-HK.json +++ b/options/locale_next/locale_zh-HK.json @@ -1,5 +1,6 @@ { - "repo.pulls.merged_title_desc": { - "other": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s" - } + "repo.pulls.merged_title_desc": { + "other": "於 %[4]s 將 %[1]d 次代碼提交從 %[2]s合併至 %[3]s" + }, + "moderation.abuse_category.malware": "惡意程式" } From 592f1494418ac6764c112d4803797caeff141814 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 16 Jul 2025 19:33:28 +0200 Subject: [PATCH 18/80] [v12.0/forgejo] fix(packages): skip another stack frame from logging (#8532) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8530 Log the right stack frame line. We currently always show the `apiError` method call. related to #8529 Co-authored-by: Michael Kriese Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8532 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/api/packages/helper/helper.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/routers/api/packages/helper/helper.go b/routers/api/packages/helper/helper.go index f9b91d9a09..47d1f18623 100644 --- a/routers/api/packages/helper/helper.go +++ b/routers/api/packages/helper/helper.go @@ -25,7 +25,8 @@ func LogAndProcessError(ctx *context.Context, status int, obj any, cb func(strin message = fmt.Sprintf("%s", obj) } if status == http.StatusInternalServerError { - log.ErrorWithSkip(1, message) + // LogAndProcessError is always wrapped in a `apiError` call, so we need to skip two frames + log.ErrorWithSkip(2, message) if setting.IsProd && (ctx.Doer == nil || !ctx.Doer.IsAdmin) { message = "" From 034af02ed0191c439804142aabc54904220653b2 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Wed, 16 Jul 2025 22:49:59 +0500 Subject: [PATCH 19/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate Translation updates that were relevant to v12 branch were picked from this commit: 8efb6c09db70de54aa153f05b41a33ff7dbb4989 (#8490) Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Juno Takano Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: adf19 Co-authored-by: amv-bamboo Co-authored-by: justbispo Co-authored-by: oatbiscuits Co-authored-by: pixelcode Co-authored-by: xtex --- options/locale/locale_ar.ini | 168 ++++++++++++++----- options/locale/locale_de-DE.ini | 224 +++++++++++++------------- options/locale/locale_uk-UA.ini | 96 +++++++++-- options/locale_next/locale_ar.json | 67 +++++++- options/locale_next/locale_be.json | 7 +- options/locale_next/locale_de-DE.json | 8 +- options/locale_next/locale_fil.json | 7 +- options/locale_next/locale_pt-PT.json | 5 +- options/locale_next/locale_ru-RU.json | 2 +- options/locale_next/locale_zh-CN.json | 3 +- 10 files changed, 409 insertions(+), 178 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 956f9a302a..ca74a477ce 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -50,7 +50,7 @@ concept_user_organization = المنظمة link_account = ربط الحساب rerun_all = أعِد تشغيل جميع الوظائف your_profile = الملف الشخصي -sign_out = سجل الخروج +sign_out = سجّل الخروج settings = الإعدادات locked = مقفول error = خطأ @@ -87,7 +87,7 @@ add_all = أضف الكل new_fork = اشتقاق جديد لمستودع new_project_column = عمود جديد add = أضف -active_stopwatch = تتبع وقت الإنجاز +active_stopwatch = متتبِّع وقت النشاط organization = منظمة new_migrate = ترحيل جديد save = احفظ @@ -114,7 +114,7 @@ twofa_scratch = الرمز الاحتياطي للمصادقة بعاملين home = الرئيسية email = عنوان البريد الإلكتروني issues = المسائل -error404 = الصفحة التي تحاول الوصول لها إما لا توجد أو أنت لست مأذون لك بعرضها. +error404 = الصفحة التي تحاول الوصول لها إما غير موجودو أو أنك غير مصرح لك بعرضها. powered_by = مدعوم بواسطة %s retry = أعد المحاولة tracked_time_summary = ملخص للتتبع الزمني وفقًا لنتائج تصفية قائمة المسائل @@ -127,8 +127,8 @@ toggle_menu = تبديل القائمة more_items = عناصر اضافية copy_generic = نسخ إلى الحافظة invalid_data = بيانات غير صالحة: %v -filter.clear = مسح المرشحات -filter = مرشح +filter.clear = مسح عوامل التصفية +filter = عامل تصفية filter.is_archived = مؤرشف filter.is_template = قوالب filter.not_mirror = ليست مرايا @@ -137,7 +137,7 @@ filter.is_mirror = مرايا filter.is_fork = الاشتقاقات filter.not_fork = ليست اشتقاقات filter.not_archived = ليس مؤرشف -filter.public = علني +filter.public = عام filter.private = خاص new_repo.title = مستودع جديد new_migrate.title = انتقال جديد @@ -145,6 +145,11 @@ new_org.title = منظمة جديدة new_repo.link = مستودع جديد new_migrate.link = انتقال جديد +new_org.link = منظمة جديدة +test = اختبار +copy_path = نسخ المسار +error413 = لقد استنفدت حصتك. + [install] db_name = اسم قاعدة البيانات user = اسم المستخدم @@ -170,7 +175,7 @@ reinstall_confirm_check_2 = وقد يلزم إعادة تزامن المستود run_user = شغّل عبر مستخدم err_admin_name_is_invalid = اسم مستخدم المدير غير صالح reinstall_confirm_check_3 = أنتِ تؤكد أنكِ متأكد تماماً من أن فورجيو يعمل مع مسار app.ini الصحيح وأنك متأكد من أنه يجب عليك إعادة تثبيته. أنت تُؤكّدُ بأنّك تُقرّ بالمخاطر السالفة الذكر. -repo_path = المسار الجذري للمستودع +repo_path = المسار الجذر للمستودع err_empty_admin_email = عنوان بريد المدير لا يمكن أن يكون فارغ. no_admin_and_disable_registration = لا يمكنك تعطيل التسجيل الذاتي للمستخدمين بدون إنشاء حساب إداري. err_admin_name_pattern_not_allowed = اسم مستخدم المدير غير صالح، هذا الأسم يطابق نمطا محجوز @@ -179,10 +184,10 @@ repo_path_helper = ستُحفظ كلّ مستودعات جِت البعيدة ف general_title = الإعدادات العامة lfs_path_helper = الملفات التي تم تعقبها بواسطة Git LFS ستُخزن في هذا الدليل. اتركه فارغًا لتعطيله. err_empty_db_path = طريق قاعدة بيانات SQLite3 لا يمكن أن يكون فارغا. -lfs_path = مسار جذر جِت LFS -app_name_helper = يمكنك إدخال اسم شركتك هنا. +lfs_path = مسار جذر Git LFS +app_name_helper = أدخل اسم المثيل هنا. سيظهر هذا الاسم في كل الصفحات. err_admin_name_is_reserved = اسم مستخدم المدير غير صالح، هذا الأسم محجوز -app_name = عنوان الموقع +app_name = عنوان المثيل log_root_path = مسار السجل log_root_path_helper = ستُكتب ملفات السجل في هذا الدليل. smtp_addr = مضيف SMTP @@ -190,7 +195,7 @@ smtp_port = منفذ SMTP mailer_password = كلمة مرور SMTP app_url_helper = العنوان الأساسي لاستنساخ عناوين URL HTTP(S) وإشعارات البريد الإلكتروني. mailer_user = اسم مستخدم SMTP -disable_gravatar.description = عطل جرافاتار والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. +disable_gravatar.description = عطل Gravatar والجهات الخارجية للصور الرمزية. ستُستخدم صورة رمزية مبدئية حتى يرفع المستخدم صورة. offline_mode.description = عطل خدمات توصيل المحتوى من الجهات الخارجية، واخدم كل المحتوى محلياً. run_user_helper = اسم مستخدم نظام التشغيل الذي يشغل فورجيو. ملاحظة: هذا المستخدم يجب أن يكون له حق الوصول إلى المسار الجذري للمستودع. domain = نطاق الخادم @@ -199,28 +204,28 @@ smtp_from = أرسل البريد الإلكتروني كـ federated_avatar_lookup = تفعيل الصور الرمزية الاتحادية optional_title = إعدادات اختيارية domain_helper = نطاق أو عنوان المضيف لخادمك. -mail_notify = فعّل التنبيه عبر البريد الإلكتروني -app_url = الرابط الأساس لفورجيو +mail_notify = فعّل التنبيهات عبر البريد الإلكتروني +app_url = الرابط الأساس smtp_from_helper = عنوان البريد الإلكتروني الذي سيستخدمه فورجيو. أدخل عنوان بريد إلكتروني عادي أو استخدم صيغة"Name" . ssh_port_helper = رقم المنفذ الذي يستمع له خادم SSH. اتركه فارغاً لتعطيله. -http_port_helper = المنفذ الذي سيستمع إليه خادم الويب لفورجيو. -http_port = منفذ استماع HTTP لفورجيو +http_port_helper = المنفذ الذي سيستمع إليه خادم ويب Forgejo. +http_port = منفذ استماع HTTP ssh_port = منفذ خادم SSH email_title = إعدادات البريد الإلكتروني offline_mode = فعل الوضع المحلي server_service_title = إعدادات الخادم وخدمات الجهات الخارجية register_confirm = الزم تأكيد البريد الإلكتروني للتسجيل -allow_only_external_registration.description = لا يسمح بالتسجيل إلا من خلال الخدمات الخارجية +allow_only_external_registration.description = لن يتمكن المستخدمون من إنشاء حسابات جديدة إلا باستخدام خدمات خارجية مهيأة. disable_registration = عطّل التسجيل الذاتي -federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام ليبرافاتار. +federated_avatar_lookup.description = تفعيل الصور الرمزية الاتحادية باستخدام Libravatar. openid_signup = فعّل التسجيل الذاتي عبر OpenID -disable_registration.description = عطل التسجيل الذاتي. المديرون فقط سيكونون قادرين على إنشاء حسابات جديدة للمستخدمين. +disable_registration.description = سيتمكن مسؤولو المثيل فقط من إنشاء حسابات مستخدمين جديدة. يوصى بشدة بإبقاء التسجيل معطلاً إلا إذا كنت تنوي استضافة مثيل عام للجميع ومستعد للتعامل مع كميات كبيرة من الحسابات غير المرغوب بها. openid_signin = فعّل تسجيل الدخول عبر OpenID openid_signin.description = فعّل تسجيل دخول المستخدمين عبر OpenID. enable_captcha = فعّل كابتشا التسجيل -enable_captcha.description = الزم وجود كابتشا للتسجيل الذاتي للمستخدمين. +enable_captcha.description = مطالبة المستخدمين باجتياز اختبار CAPTCHA من أجل إنشاء حسابات. openid_signup.description = فعّل التسجيل الذاتي للمستخدمين عبر OpenID. -require_sign_in_view = الزم تسجيل الدخول لعرض الصفحات +require_sign_in_view = يتطلب تسجيل الدخول لعرض محتوى المثيل require_sign_in_view.description = مكّن وصول الصفحات للمستخدمين فقط. لن يرى الزائرون سوى صفحات التسجيل والتسجيل. admin_setting.description = إنشاء حساب إداري هو اختياري. أول مستخدم مُسجل سيصبح تلقائيا مديرا. admin_password = كلمة المرور @@ -233,7 +238,7 @@ test_git_failed = يتعذر اختبار أمر جِت: %v confirm_password = أكّد كلمة المرور invalid_admin_setting = إعداد حساب المدير غير صالح: %v invalid_log_root_path = مسار السجل غير صالح: %v -default_enable_timetracking = فعّل تتبع الوقت مبدئيا +default_enable_timetracking = فعّل التتبع الزمني افتراضيًا env_config_keys_prompt = ستطبق المتغيرات البيئية التالية أيضاً على ملف الإعدادات: admin_title = إعدادات حساب المدير no_reply_address_helper = النطاق للمستخدمين بعنوان بريد إلكتروني مخفي. مثلاً، اسم المستخدم "sarah" سوف يسجل في جِت كـ"sarah@noreply.example.org" لو كان نطاق البريد الإلكتروني الخفي مدخل كـ"noreply.example.org". @@ -242,9 +247,9 @@ default_enable_timetracking.description = فعل تتبع الوقت للمست run_user_not_match = مستخدم التشغيل غير مطابق لأسم المستخدم الحالي: %s -> %s invalid_db_setting = إعدادات قاعدة البيانات غير صالحة: %v invalid_db_table = جدول قاعدة البيانات "%s" غير صالح: %v -default_keep_email_private.description = أخفِ عناوين البريد الإلكتروني للحسابات الجديدة مبدئيا. +default_keep_email_private.description = قم بتمكين إخفاء عنوان البريد الإلكتروني للمستخدمين الجدد افتراضيًا حتى لا يتم تسريب هذه المعلومات فور التسجيل. env_config_keys = إعدادات بيئية -default_allow_create_organization = اسمح بإنشاء المنظمات مبدئيا +default_allow_create_organization = اسمح بإنشاء المنظمات بشكل افتراضي invalid_app_data_path = مسار بيانات التطبيق غير صالح: %v enable_update_checker_helper = يفحص لإيجاد اصدارات جديدة عن طريق الإتصال بسيرفرات فورجيو. invalid_repo_path = المسار الجزري للمستودع غير صالح: %v @@ -252,11 +257,17 @@ internal_token_failed = فشل توليد الرمز الداخلي: %v no_reply_address = نطاقات البريد الإلكتروني المخفية default_keep_email_private = أخفِ عناوين البريد الإلكتروني مبدئيا admin_name = اسم مستخدم المدير -default_allow_create_organization.description = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات مبدئيا. +default_allow_create_organization.description = السماح للمستخدمين الجدد بإنشاء منتديات المجموعة بشكل افتراضي. عند تعطيل هذا الخيار، سيتعين على المسؤول منح إذن لإنشاء منتديات المجموعة للمستخدمين الجدد. password_algorithm = خوارزمية تجزئة كلمة المرور invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. +app_slogan = شعار المثيل +app_slogan_helper = أدخل شعار المثيل الخاص بك هنا. اتركه فارغاً لتعطيله. +smtp_from_invalid = عنوان "،بريد الإرسال كـ" غير صالح +allow_only_external_registration = السماح بالتسجيل عبر الخدمات الخارجية فقط +config_location_hint = سيتم حفظ خيارات التهيئة هذه في: + [editor] buttons.list.ordered.tooltip = أضف قائمة مرقمة buttons.bold.tooltip = أضف نصًا عريضًا @@ -273,9 +284,22 @@ buttons.italic.tooltip = أضف نصًا مائلًا buttons.link.tooltip = اضف رابط buttons.disable_monospace_font = عطّل الخط الثابت العرض +buttons.indent.tooltip = تداخل العناصر بنفس المستوى +buttons.unindent.tooltip = ‪عناصر غير متساوية من نفس المستوى +buttons.new_table.tooltip = إضافة جدول +table_modal.header = إضافة جدول +table_modal.placeholder.header = الترويسة +table_modal.placeholder.content = المحتوى +table_modal.label.rows = الصفوف +table_modal.label.columns = الأعمدة +link_modal.header = إضافة رابط +link_modal.url = Url +link_modal.description = الوصف +link_modal.paste_reminder = تلميح: باستخدام عنوان URL في حافظتك، يمكنك اللصق مباشرةً في المحرر لإنشاء رابط. + [aria] navbar = شريط التنقل -footer.software = عن البرمجية +footer.software = عن هذه البرمجية footer.links = روابط footer = الذيل @@ -1388,6 +1412,22 @@ issue.action.ready_for_review = @%[1]s علّم هذا الطلب للس issue_assigned.pull = @%[1]s عيّنك إلى طلب سحب %[2]s في مستودع %[3]s. issue.action.review_dismissed = @%[1]s أستبعد آخر مراجعة من %[2]s لهذا الطلب للسحب. +password_change.subject = تم تغيير كلمة مرورك +password_change.text_1 = تم تغيير كلمة مرور حسابك للتو. +primary_mail_change.subject = تم تغيير البريد الأساسي الخاص بك +primary_mail_change.text_1 = تم تغيير البريد الإلكتروني الأساسي لحسابك إلى %[1]s. هذا يعني أن عنوان البريد الإلكتروني هذا لن يتلقى إشعارات البريد لحسابك بعد الآن. +totp_disabled.subject = تم تعطيل TOTP +totp_disabled.text_1 = تم تعطيل كلمة المرور لمرة واحدة المستندة إلى الوقت (TOTP) على حسابك للتو. +totp_disabled.no_2fa = لم تعد هناك طرق أُخرى للمصادقة الثنائية (2FA) قيد التهيئة عد الآن ، أي أنه لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +removed_security_key.subject = تمت إزالة مفتاح الأمان +removed_security_key.text_1 = تم إزالة مفتاح الأمان ”%[1] s“ للتو من حسابك. +removed_security_key.no_2fa = لم تعد هناك طرق أخرى للمصادقة الثنائية (2FA) قيد التهيئة بعد الآن، أي لم يعد من الضروري تسجيل الدخول إلى حسابك باستخدام المصادقة الثنائية (2FA). +account_security_caution.text_1 = إذا كان هذا أنت، فيمكنك تجاهل هذا البريد بأمان. +account_security_caution.text_2 = إذا لم تكن أنت، فهذا يعني أن حسابك مخترق. يرجى الاتصال بمسؤولي هذا الموقع. +totp_enrolled.subject = لقد قمت بتشيط TOTP كطريقة 2FA +totp_enrolled.text_1.no_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يجب عليك استخدام TOTP كطريقة للمصادقة الثنائية. +totp_enrolled.text_1.has_webauthn = لقد قمت للتو بتمكين TOTP لحسابك. هذا يعني أنه بالنسبة لجميع عمليات تسجيل الدخول المستقبلية إلى حسابك، يمكنك استخدام TOTP كطريقة للمصادقة الثنائية ، أو استخدام أي من مفاتيح الأمان الخاصة بك. + [error] not_found = تعذر العثور على الهدف. report_message = إن كنت متيقِّنًا أن هذه علة في فورجيو، رجاءً ابحث في كودبيرج أو افتح مسأله جديدة إذا لزم الأمر. @@ -1422,7 +1462,7 @@ joined_on = انضم في %s user_bio = السيرة الذاتية repositories = المستودعات activity = النشاط العام -projects = مشاريع +projects = المشاريع unfollow = إلغِ المتابعة settings = إعدادات المستخدم following_few = %d يتابع @@ -1430,7 +1470,7 @@ follow = تابع followers_few = %d متابعين form.name_reserved = اسم المستخدم "%s" محجوز. email_visibility.limited = عنوان بريدك الإلكتروني ظاهر لكل المستخدمين المُستَوثَقين -code = البرمجية +code = الكود overview = نظرة عامة watched = المستودعات المشاهدة disabled_public_activity = هذا المستخدم عطّل الظهور العام للنشاط. @@ -1440,6 +1480,18 @@ starred = المستودعات المميّزة بنجمة form.name_chars_not_allowed = اسم المستخدم "%s" يحتوي على رموز غير صالحة. form.name_pattern_not_allowed = النمط "s%" غير مسموح به في إسم المستخدم. +followers.title.one = متابِع +followers.title.few = متابعين +following.title.one = متابعة +following.title.few = متابعة +followers_one = %d متابِع +following_one = %d يُتابع +public_activity.visibility_hint.self_public = نشاطك مرئي للجميع، باستثناء التفاعلات في المساحات الخاصة. اضبط الإعدادات. +public_activity.visibility_hint.admin_public = هذا النشاط مرئي للجميع، ولكن بصفتك مسؤولاً يمكنك أيضًا رؤية التفاعلات في المساحات الخاصة. +public_activity.visibility_hint.self_private = نشاطك مرئي لك ولسُعاة المثيل فقط. تعديل الإعدادات. +public_activity.visibility_hint.admin_private = هذا النشاط مرئي لك لأنك مسؤول، ولكن المستخدم يريد أن يظل خاصاً. +public_activity.visibility_hint.self_private_profile = نشاطك مرئي لك ولسُعاة المثيل فقط لأن ملفك الشخصي خاص. تعديل الإعدادات. + [auth] change_unconfirmed_email_error = تعذر تغيير البريد الإلكتروني: %v change_unconfirmed_email_summary = تغيير البريد الإلكتروني الذي يُرسل التفعيل له. @@ -1459,11 +1511,11 @@ active_your_account = فعّل حسابك register_helper_msg = هل لديك حساب بالفعل؟ سجل الدخول! manual_activation_only = تواصل مع مدير موقعك لإكمال التفعيل. must_change_password = حدّث كلمة المرور الخاصة بك -send_reset_mail = أرسل رسالة استعادة حساب +send_reset_mail = أرسل بريد الاستعادة resend_mail = اضغط هنا لإعادة إرسالة رسالة تفعيل حسابك has_unconfirmed_mail = أهلا يا %s، لديك عنوان بريد إلكتروني غير مؤكَّد (%s). إن لم تستلم رسالة تأكيد أو تريد إرسال واحدة جديدة، فنرجو الضغط على الزر الذي بالأسفل. email_not_associate = عنوان البريد هذا غير مرتبط بأي حساب. -reset_password = استعادة حساب +reset_password = استعادة الحساب oauth_signin_tab = أربط بحساب موجود invalid_password = كلمة المرور الخاصة بك لا تطابق كلمة المرور التي استخدمت لتسجيل الحساب. oauth_signin_title = سجّل الدخول لتأذن للحساب المربوط @@ -1484,13 +1536,13 @@ reset_password_wrong_user = أنت مُسجل كـ %s، لكن رابط أعاد openid_connect_title = اتصل بحساب موجود confirmation_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية التسجيل. إذا كان عنوان البريد خاطئ، يمكنك تسجيل الدخول وطلب بريد تأكيد جديد يُرسل إلى عنوان آخر. scratch_code = رمز الخدش -invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهى اضغط هنا للإعادة. +invalid_code_forgot_password = رمز تأكيدك غير صحيح أو انتهت صلاحيته. اضغط هنا للإعادة. openid_register_title = أنشئ حسابًا جديدًا verify = تحقق twofa_scratch_used = لقد استخدمت رمز الخدش الخاص بك. لقد تم إعادة توجيهك إلى إعدادات المصادقة الثنائية حتى يمكنك إزالة تسجيل جهازك أو توليد رمز خدش جديد. oauth_signup_submit = أكمل الحساب oauth.signin.error = كان هناك خطأ في تجهيز طلب الإذن إذا استمر هذا الخطأ، يرجى الاتصال بالمدير. -invalid_code = رمز تأكيدك غير صحيح أو انتهى. +invalid_code = رمز تأكيدك غير صحيح أو انتهت صلاحيته. oauth_signup_title = أكمل حساب جديد resent_limit_prompt = لقد طلبت بالفعل بريداً إلكترونياً للتفعيل مؤخراً من فضلك انتظر 3 دقائق وحاول مرة أخرى. reset_password_mail_sent_prompt = تم إرسال بريد تأكيد جديد إلى %s. يرجى التأكد من صندوق بريدك في خلال %s حتى تكتمل عملية استعادة الحساب. @@ -1515,6 +1567,14 @@ remember_me = تذكر هذا الجهاز remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف. authorization_failed_desc = فشل التفويض لأننا اكتشفنا طلبًا غير صالح. يرجى الاتصال بمشرف التطبيق الذي حاولت ترخيصه. +hint_login = لديك حساب بالفعل؟ سجّل الدخول الآن! +hint_register = يلزمك حساب ؟ سجِّل الآن. +sign_up_button = سجِّل الآن. +unauthorized_credentials = بيانات الاعتماد غير صحيحة أو انتهت صلاحيتها. أعد محاولة تنفيذ الأمر أو راجع %s لمزيد من المعلومات +use_onetime_code = استخدم رمزًا لمرة واحدة +back_to_sign_in = العودة إلى تسجيل الدخول +sign_in_openid = المتابعة باستخدام OpenID + [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. rpm.repository.architectures = بنيات @@ -1558,6 +1618,10 @@ number_of_contributions_in_the_last_12_months = %s مساهم في آخر 12 ش contributions_zero = بلا مساهمات more = أكثر +contributions_format = {contributions} مساهمة في {day} {month} {year} +contributions_one = المساهمة +contributions_few = المساهمات + [admin] self_check.database_fix_mysql = لمستخدمين ميسكول/ماريا دي بي، يمكنك استخدام أمر "forgejo doctor convert" لإصلاح مشاكل التجمّع، أو يمكنك أيضاً إصلاح المشكلة عن طريق تعديل السيكول يدوياً. self_check.database_collation_mismatch = توقع قاعدة البيانات لتستعمل تجميع: %s @@ -1708,7 +1772,7 @@ enterred_invalid_org_name = اسم المنظمة التي أدخلته خطأ. lang_select_error = اختر لغة من القائمة. alpha_dash_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_").` alpha_dash_dot_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_") والنقطة (".").` -repo_name_been_taken = اسم المستودع مستعمل بالفعل. +repo_name_been_taken = اسم المستودع مستخدم بالفعل. Email = البريد الإلكتروني auth_failed = فشل الاستيثاق: %v email_error = ` ليس عنوان بريد إلكتروني صالح.` @@ -1728,10 +1792,10 @@ still_has_org = "حسابك عضو في منظمة أو أكثر؛ غادرهم repository_files_already_exist.adopt_or_delete = الملفات موجودة بالفعل لهذا المستودع. إما اعتمادها أو حذفها. repository_files_already_exist.delete = الملفات موجودة بالفعل لهذا المستودع. يجب عليك حذفها. repository_files_already_exist.adopt = الملفات موجودة بالفعل لهذا المستودع ويمكن اعتمادها فقط. -repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. تواصل مع مدير النظام. +repository_files_already_exist = الملفات موجودة بالفعل لهذا المستودع. اتصل بمدير النظام. TeamName = اسم الفريق username_has_not_been_changed = لم يتم تغيير اسم المستخدم -username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسماؤهم. +username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسمائهم. captcha_incorrect = الكابتشا خاطئة. AdminEmail = عنوان البريد الإلكتروني للمدير team_no_units_error = اسمح بالوصول إلى قسم واحد على الأقل في المستودعات. @@ -1759,6 +1823,24 @@ CommitChoice = إختيار الإداع regex_pattern_error = ` نمط التعبير النمطي غير صالح: %s.` username_error = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") و نقطة (".") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.` +FullName = الاسم الكامل +Description = الوصف +Pronouns = الضمائر +Biography = النبذة +Website = موقع الويب +Location = الموقع +To = اسم الفرع +AccessToken = رمز الوصول +invalid_group_team_map_error = ` التعيين غير صالح: %s ` +username_claiming_cooldown = لا يمكن المطالبة باسم المستخدم، لأن فترة تباطؤه لم تنتهِ بعد. يمكن المطالبة به عند %[1]s. +repository_force_private = وضع الخاص الإجباري مفعّل: لا يمكن تحويل المستودعات الخاصة إلى عامة. +visit_rate_limit = تناولت الزيارة عن بُعد الحد من معدلها. +email_domain_is_not_allowed = نطاق البريد الإلكتروني للمستخدم %s يتعارض مع قائمة النطاقات المسموحة ، أو الممنوعة. يرجى التأكد من إدخال عنوان البريد الإلكتروني بشكل صحيح. +unset_password = المستخدم المسجل لم يقم بتعيين كلمة مرور. +unsupported_login_type = نوع تسجيل الدخول غير مدعوم لحذف الحساب. +invalid_ssh_principal = أصل غير صالح: %s +required_prefix = المُدخل يجب أن يبدأ مع "%s" + [home] filter = تصفيات أخرى show_archived = مؤرشف @@ -1805,6 +1887,11 @@ relevant_repositories_tooltip = تم أخفاء المستودعات التي ه relevant_repositories = يتم اظهار المستودعات المتعلقة فقط. أظهر النتائج غير المصفاة. code_last_indexed_at = فُهرس آخر مرة %s +stars_one = %d نجمة +stars_few = %d نجوم +forks_one = %d نسخة +forks_few = %d نُسَخ + [actions] variables.none = لا توجد متغيرات بعد. variables.deletion = أزل المتغير @@ -1977,7 +2064,7 @@ component_failed_to_load = حدث خطأ غير متوقع. [search] org_kind = بحث في المنظمات… code_search_unavailable = البحث في الكود غير متوفر حاليًا. يرجى الاتصال بمدير الموقع. -search = ابحث... +search = البحث… type_tooltip = نوع البحث fuzzy = أجعد fuzzy_tooltip = قم بتضمين النتائج التي تتطابق أيضًا مع مصطلح البحث بشكل وثيق @@ -1986,10 +2073,19 @@ match_tooltip = قم بتضمين النتائج التي تطابق مصطلح repo_kind = بحث في المستودعات… user_kind = بحث عن المستخدمين… team_kind = بحث عن الفرق… -code_kind = بحث في الكود… +code_kind = بحث ضمن الكود… project_kind = البحث ضمن المشاريع… branch_kind = البحث ضمن الفروع… no_results = لا توجد نتائج مطابقة. issue_kind = البحث ضمن الأعطال… pull_kind = البحث ضمن طلبات السحب… keyword_search_unavailable = البحث من خلال الكلمات المفتاحية ليس متوفر حالياً. رجاءاً تواصل مع مشرف الموقع. +union = مطابقة عامة +union_tooltip = عرض النتائج التي تطابق أي من الكلمات المفتاحية المفصولة بمسافات +exact = مطابق +exact_tooltip = عرض النتائج التي تطابق مصطلح البحث بالضبط فقط +regexp = RegExp +regexp_tooltip = تعامل مع عبارة البحث على أنها تعبير نمطي +package_kind = البحث ضمن الحزم… +commit_kind = البحث ضمن الإيداعات… +runner_kind = البحث ضمن المشغِّلات… diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 35d1dfddaf..9f55f79fe9 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -251,12 +251,12 @@ db_schema_helper=Leer lassen, um den Datenbank-Standardwert („public“) zu ve ssl_mode=SSL path=Pfad sqlite_helper=Dateipfad zur SQLite3-Datenbank.
Gib einen absoluten Pfad an, wenn Forgejo als Service gestartet wird. -reinstall_error=Du versuchst, in eine bereits existierende Forgejo Datenbank zu installieren +reinstall_error=Du versuchst, in eine bereits existierende Forgejo-Datenbank zu installieren reinstall_confirm_message=Eine Neuinstallation mit einer bestehenden Forgejo-Datenbank kann mehrere Probleme verursachen. In den meisten Fällen solltest du deine vorhandene „app.ini“ verwenden, um Forgejo auszuführen. Wenn du weißt, was du tust, bestätige die folgenden Angaben: reinstall_confirm_check_1=Die von der SECRET_KEY in app.ini verschlüsselten Daten können verloren gehen: Benutzer können sich unter Umständen nicht mit 2FA/OTP einloggen und Spiegel könnten nicht mehr richtig funktionieren. Mit der Ankreuzung dieses Kästchens bestätigst du, dass die aktuelle app.ini-Datei den korrekten SECRET_KEY enthält. reinstall_confirm_check_2=Die Repositorys und Einstellungen müssen eventuell neu synchronisiert werden. Durch das Ankreuzen dieses Kästchens bestätigst du, dass du die Hooks für die Repositorys und die authorized_keys-Datei manuell neu synchronisierst. Du bestätigst, dass du sicherstellst, dass die Repository- und Spiegeleinstellungen korrekt sind. reinstall_confirm_check_3=Du bestätigst, dass du absolut sicher bist, dass diese Forgejo mit der richtigen app.ini läuft, und du sicher bist, dass du neu installieren musst. Du bestätigst, dass du die oben genannten Risiken anerkennst. -err_empty_db_path=Der SQLite3 Datenbankpfad darf nicht leer sein. +err_empty_db_path=Der SQLite3-Datenbankpfad darf nicht leer sein. no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen. err_empty_admin_password=Das Administrator-Passwort darf nicht leer sein. err_empty_admin_email=Die Administrator-E-Mail darf nicht leer sein. @@ -463,7 +463,7 @@ openid_register_title=Neues Konto einrichten openid_register_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu. openid_signin_desc=Gib deine OpenID-URI ein, zum Beispiel alice.openid.example.org oder https://openid.example.org/alice. disable_forgot_password_mail=Die Kontowiederherstellung ist deaktiviert, da keine E-Mail eingerichtet ist. Bitte kontaktiere den zuständigen Administrator. -disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail Adresse ein, um die Kontowiederherstellung freizuschalten. +disable_forgot_password_mail_admin=Die Kontowiederherstellung ist nur verfügbar, wenn eine E-Mail eingerichtet wurde. Bitte richte eine E-Mail-Adresse ein, um die Kontowiederherstellung freizuschalten. email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registrieren. authorize_application=Anwendung autorisieren authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst. @@ -530,8 +530,8 @@ issue.action.merge=@%[1]s hat #%[2]d in %[3]s zusammengeführt. issue.action.approve=@%[1]s hat diesen Pull-Request genehmigt. issue.action.reject=@%[1]s hat Änderungen auf diesem Pull-Request angefordert. issue.action.review=@%[1]s hat diesen Pull-Request kommentiert. -issue.action.review_dismissed=@%[1]s hat das letzte Review von %[2]s für diesen Pull-Request verworfen. -issue.action.ready_for_review=@%[1]s hat diesen Pull-Request zum Review freigegeben. +issue.action.review_dismissed=@%[1]s hat die letzte Sichtung von %[2]s für diesen Pull-Request verworfen. +issue.action.ready_for_review=@%[1]s hat diesen Pull-Request für die Sichtung freigegeben. issue.action.new=@%[1]s hat #%[2]d geöffnet. issue.in_tree_path=In %s: @@ -540,8 +540,8 @@ release.new.text=@%[1]s hat %[2]s in %[3]s released release.title=Titel: %s release.note=Anmerkung: release.downloads=Downloads: -release.download.zip=Quellcode (ZIP Datei) -release.download.targz=Quellcode (TAR.GZ Datei) +release.download.zip=Quellcode (ZIP) +release.download.targz=Quellcode (TAR.GZ) repo.transfer.subject_to=%s möchte „%s“ an %s übertragen repo.transfer.subject_to_you=%s möchte dir „%s“ übertragen @@ -787,7 +787,7 @@ comment_type_group_time_tracking=Zeiterfassung comment_type_group_deadline=Frist comment_type_group_dependency=Abhängigkeit comment_type_group_lock=Sperrstatus -comment_type_group_review_request=Angeforderte Reviews +comment_type_group_review_request=Angeforderte Sichtungen comment_type_group_pull_request_push=Hinzugefügte Commits comment_type_group_project=Projekt comment_type_group_issue_ref=Issue-Referenz @@ -873,7 +873,7 @@ gpg_key_matched_identities_long=Die eingebetteten Identitäten in diesem Schlüs gpg_key_verified=Verifizierter Schlüssel gpg_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen. gpg_key_verify=Verifizieren -gpg_invalid_token_signature=Der GPG-Key, die Signatur, und das Token stimmen nicht überein, oder das Token ist veraltet. +gpg_invalid_token_signature=Der GPG-Key, die Signatur und das Token stimmen nicht überein, oder das Token ist veraltet. gpg_token_required=Du musst eine Signatur für das folgende Token angeben gpg_token=Token gpg_token_help=Du kannst eine Signatur wie folgt generieren: @@ -902,10 +902,10 @@ add_principal_success=Die SSH-Zertifikatsidentität „%s“ wurde hinzugefügt. delete_key=Entfernen ssh_key_deletion=SSH-Schlüssel entfernen gpg_key_deletion=GPG-Schlüssel entfernen -ssh_principal_deletion=SSH-Zertifik-Identität entfernen +ssh_principal_deletion=SSH-Zertifikats-Principal entfernen ssh_key_deletion_desc=Wenn du einen SSH-Key entfernst, hast du mit diesem Key keinen Zugriff mehr. Fortfahren? gpg_key_deletion_desc=Wenn du einen GPG-Schlüssel entfernst, können damit unterschriebene Commits nicht mehr verifiziert werden. Fortfahren? -ssh_principal_deletion_desc=Das Entfernen einer SSH-Zertifikat-Identität entzieht den Zugriff auf dein Konto. Fortfahren? +ssh_principal_deletion_desc=Das Entfernen eines SSH-Zertifikats-Principals entzieht den Zugriff auf dein Konto. Fortfahren? ssh_key_deletion_success=Der SSH-Schlüssel wurde entfernt. gpg_key_deletion_success=Der GPG-Schlüssel wurde entfernt. ssh_principal_deletion_success=Die Identität wurde entfernt. @@ -931,7 +931,7 @@ unbind_success=Das soziale Konto wurde erfolgreich entfernt. manage_access_token=Zugriffstokens generate_new_token=Neuen Token erzeugen -tokens_desc=Diese Tokens gewähren vollen Zugriff auf dein Konto via die Forgejo-API. +tokens_desc=Diese Tokens gewähren vollen Zugriff auf dein Konto mit der Forgejo-API. token_name=Token-Name generate_token=Token generieren generate_token_success=Ein neuer Token wurde generiert. Kopiere diesen jetzt, da er nicht erneut angezeigt wird. @@ -970,8 +970,8 @@ save_application=Speichern oauth2_client_id=Client-ID oauth2_client_secret=Client-Geheimnis oauth2_regenerate_secret=Geheimnis neu generieren -oauth2_regenerate_secret_hint=Secret verloren? -oauth2_client_secret_hint=Das Secret wird nach dem Verlassen oder Aktualisieren dieser Seite nicht mehr angezeigt. Bitte stelle sicher, dass du es gespeichert hast. +oauth2_regenerate_secret_hint=Geheimnis verloren? +oauth2_client_secret_hint=Das Geheimnis wird nach dem Verlassen oder Aktualisieren dieser Seite nicht mehr angezeigt. Bitte stelle sicher, dass du es gespeichert hast. oauth2_application_edit=Bearbeiten oauth2_application_create_description=OAuth2-Anwendungen geben deiner Drittanwendung Zugriff auf Benutzeraccounts dieser Forgejo-Instanz. oauth2_application_remove_description=Das Entfernen einer OAuth2-Anwendung hat zur Folge, dass diese nicht mehr auf autorisierte Benutzeraccounts auf dieser Instanz zugreifen kann. Möchtest Du fortfahren? @@ -996,11 +996,11 @@ twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird regenerate_scratch_token_desc=Wenn du deinen Wiederherstellungsschlüssel verlegst oder es bereits benutzt hast, kannst du es hier zurücksetzen. twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert. scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App: -or_enter_secret=Oder gib das Secret ein: %s +or_enter_secret=Oder gib das Geheimnis ein: %s then_enter_passcode=Und gib dann die angezeigte PIN der Anwendung ein: passcode_invalid=Die PIN ist falsch. Probiere es erneut. twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre deinen einmalig verwendbaren Wiederherstellungsschlüssel (%s) an einem sicheren Ort auf, da er nicht wieder angezeigt werden wird. -twofa_failed_get_secret=Fehler beim Abrufen des Secrets. +twofa_failed_get_secret=Fehler beim Abrufen des Geheimnisses. webauthn_desc=Sicherheitsschlüssel sind Geräte, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Der Sicherheitsschlüssel muss den Standard „WebAuthn“ unterstützen. webauthn_register_key=Sicherheitsschlüssel hinzufügen @@ -1185,9 +1185,9 @@ blame.ignore_revs=Revisionen in .git-blame-ignore-revs werden i blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in .git-blame-ignore-revs. author_search_tooltip=Zeigt maximal 30 Benutzer -tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s -tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s -tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s +tree_path_not_found_commit=Pfad %[1]s existiert nicht im Commit %[2]s +tree_path_not_found_branch=Pfad %[1]s existiert nicht im Branch %[2]s +tree_path_not_found_tag=Pfad %[1]s existiert nicht im Tag %[2]s transfer.accept=Übertragung akzeptieren transfer.accept_desc=Übertragung nach „%s“ @@ -1248,7 +1248,7 @@ migrate.clone_local_path=oder ein lokaler Serverpfad migrate.permission_denied=Du hast keine Berechtigung zum Importieren lokaler Repositorys. migrate.permission_denied_blocked=Du kannst von keinen nicht erlaubten Hosts importieren. Bitte fragen deinen Administrator, die Einstellungen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS zu überprüfen. migrate.invalid_local_path=Der lokale Pfad ist ungültig. Er existiert nicht oder ist kein Verzeichnis. -migrate.invalid_lfs_endpoint=Ungültiger LFS Endpunkt. +migrate.invalid_lfs_endpoint=Der LFS-Endpunkt ist nicht gültig. migrate.failed=Fehler bei der Migration: %v migrate.migrate_items_options=Zugangs-Token wird benötigt, um zusätzliche Elemente zu migrieren migrated_from=Migriert von %[2]s @@ -1324,7 +1324,7 @@ commit=Commit release=Release releases=Releases tag=Tag -released_this=hat released +released_this=hat releast tagged_this=hat getaggt file.title=%s an %s file_raw=Originalformat @@ -1448,7 +1448,7 @@ commits.older=Älter commits.newer=Neuer commits.signed_by=Signiert von commits.signed_by_untrusted_user=Signiert von nicht vertrauenswürdigen Benutzern -commits.signed_by_untrusted_user_unmatched=Signiert von nicht vertrauenswürdigen Benutzern, der nicht mit dem Committer übereinstimmt +commits.signed_by_untrusted_user_unmatched=Von einem nicht vertrauenswürdigen Benutzer, der nicht auf den Committer passt, signiert commits.gpg_key_id=GPG-Schlüssel-ID commits.ssh_key_fingerprint=SSH-Schlüssel-Fingerabdruck commits.view_path=An diesem Punkt im Verlauf anzeigen @@ -1459,7 +1459,7 @@ commit.revert-header=Setze zurück: %s commit.revert-content=Branch auswählen, der zurückgesetzt werden soll: commit.cherry-pick=Cherry-Pick commit.cherry-pick-header=Cherry-Picke: %s -commit.cherry-pick-content=Branch auswählen, auf dem Cherry-Picked werden soll: +commit.cherry-pick-content=Branch auswählen, zu dem das Ergebnis des Cherry-Picks angewendet werden soll: commitstatus.error=Fehler commitstatus.failure=Fehler @@ -1515,7 +1515,7 @@ issues.filter_assignees=Verantwortliche filtern issues.filter_milestones=Meilenstein filtern issues.filter_projects=Projekt filtern issues.filter_labels=Label filtern -issues.filter_reviewers=Reviewer filtern +issues.filter_reviewers=Sichter filtern issues.new=Neues Issue issues.new.title_empty=Der Titel kann nicht leer sein issues.new.labels=Labels @@ -1535,7 +1535,7 @@ issues.new.closed_milestone=Geschlossene Meilensteine issues.new.assignees=Zuständige issues.new.clear_assignees=Zuständige entfernen issues.new.no_assignees=Niemand zuständig -issues.new.no_reviewers=Keine Reviewer +issues.new.no_reviewers=Keine Sichter issues.choose.get_started=Los geht's issues.choose.open_external_link=Öffnen issues.choose.blank=Standard @@ -1598,8 +1598,8 @@ issues.filter_type.all_issues=Alle Issues issues.filter_type.assigned_to_you=Dir zugewiesen issues.filter_type.created_by_you=Von dir erstellt issues.filter_type.mentioning_you=Hat dich erwähnt -issues.filter_type.review_requested=Review angefordert -issues.filter_type.reviewed_by_you=Von dir gereviewt +issues.filter_type.review_requested=Sichtung angefordert +issues.filter_type.reviewed_by_you=Von dir gesichtet issues.filter_sort=Sortieren issues.filter_sort.latest=Neueste issues.filter_sort.oldest=Älteste @@ -1621,8 +1621,8 @@ issues.action_milestone=Meilenstein issues.action_milestone_no_select=Kein Meilenstein issues.action_assignee=Zuständig issues.action_assignee_no_select=Niemand zuständig -issues.action_check=Auswählen/Auswahl aufheben -issues.action_check_all=Alles auswählen/Auswahl aufheben +issues.action_check=Auswählen / Auswahl aufheben +issues.action_check_all=Alles auswählen / Auswahl aufheben issues.opened_by=%[1]s von %[3]s geöffnet pulls.merged_by=von %[3]s wurde %[1]s zusammengeführt pulls.merged_by_fake=von %[2]s %[1]s zusammengeführt @@ -1673,12 +1673,12 @@ issues.role.first_time_contributor=Erstmaliger Mitwirkender issues.role.first_time_contributor_helper=Dies ist der erste Beitrag dieses Benutzers zum Repository. issues.role.contributor=Mitwirkender issues.role.contributor_helper=Dieser Benutzer hat schon zuvor zu dem Repository beigetragen. -issues.re_request_review=Review erneut anfordern -issues.is_stale=Seit diesem Review gab es Änderungen an diesem PR -issues.remove_request_review=Review-Anfrage entfernen -issues.remove_request_review_block=Review-Anfrage kann nicht entfernt werden -issues.dismiss_review=Review verwerfen -issues.dismiss_review_warning=Bist du dir sicher, dass du dieses Review verwerfen willst? +issues.re_request_review=Sichtung erneut anfordern +issues.is_stale=Seit dieser Sichtung gab es Änderungen an diesem PR +issues.remove_request_review=Sichtungsanfrage entfernen +issues.remove_request_review_block=Sichtungsanfrage kann nicht entfernt werden +issues.dismiss_review=Sichtung verwerfen +issues.dismiss_review_warning=Bist du dir sicher, dass du diese Sichtung verwerfen willst? issues.sign_in_require_desc=Anmelden, um an der Diskussion teilzunehmen. issues.edit=Bearbeiten issues.cancel=Abbrechen @@ -1809,20 +1809,20 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.approve=hat die Änderungen %s genehmigt -issues.review.comment=hat %s gereviewt +issues.review.comment=hat %s gesichtet issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed_label=Verworfen issues.review.left_comment=hat einen Kommentar hinterlassen issues.review.content.empty=Du musst einen Kommentar hinterlassen, der die gewünschte(n) Änderung(en) beschreibt. issues.review.reject=hat %s Änderungen angefragt -issues.review.wait=wurde für ein Review %s angefragt -issues.review.add_review_request=hat ein Review von %[1]s %[2]s angefragt -issues.review.remove_review_request=hat die Aufforderung zum Review an %[1]s %[2]s entfernt -issues.review.remove_review_request_self=hat das Review verweigert %s +issues.review.wait=wurde für eine Sichtung von %s angefragt +issues.review.add_review_request=hat eine Sichtung von %[1]s %[2]s angefragt +issues.review.remove_review_request=hat die Sichtungsanfrage an %[1]s %[2]s entfernt +issues.review.remove_review_request_self=hat die Sichtung %s verweigert issues.review.pending=Ausstehend issues.review.pending.tooltip=Dieser Kommentar ist derzeit nicht für andere Benutzer sichtbar. Um deine ausstehenden Kommentare einzureichen, wähle „%s“ -> „%s/%s/%s“ oben auf der Seite. issues.review.review=Review -issues.review.reviewers=Reviewer +issues.review.reviewers=Sichter issues.review.outdated=Veraltet issues.review.outdated_description=Der Inhalt hat sich geändert, seit dieser Kommentar abgegeben wurde issues.review.option.show_outdated_comments=Veraltete Kommentare anzeigen @@ -1833,7 +1833,7 @@ issues.review.show_resolved=Erledigte anzeigen issues.review.hide_resolved=Erledigte ausblenden issues.review.resolve_conversation=Diskussion als „erledigt“ markieren issues.review.un_resolve_conversation=Diskussion als „nicht erledigt“ markieren -issues.review.resolved_by=markierte diese Unterhaltung als gelöst +issues.review.resolved_by=markierte diese Unterhaltung als „erledigt“ issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Zuständigen hinzugefügt werden. issues.reference_issue.body=Beschreibung issues.content_history.deleted=gelöscht @@ -1847,7 +1847,7 @@ issues.reference_link=Referenz: %s compare.compare_base=Basis compare.compare_head=vergleichen -pulls.desc=Pull-Requests und Code-Reviews aktivieren. +pulls.desc=Pull-Requests und Code-Sichtungen aktivieren. pulls.new=Neuer Pull-Request pulls.view=Pull-Request ansehen pulls.compare_changes=Neuer Pull-Request @@ -1856,7 +1856,7 @@ pulls.allow_edits_from_maintainers_desc=Nutzer mit Schreibzugriff auf den Basisb pulls.allow_edits_from_maintainers_err=Aktualisieren fehlgeschlagen pulls.compare_changes_desc=Wähle den Zielbranch, in das zusammengeführt werden soll, und den Quellbranch, von dem gepullt werden soll, aus. pulls.has_viewed_file=Gesehen -pulls.has_changed_since_last_review=Seit deinem letzten Review geändert +pulls.has_changed_since_last_review=Seit deiner letzten Sichtung geändert pulls.viewed_files_label=%[1]d / %[2]d Dateien betrachtet pulls.expand_files=Alle Dateien ausklappen pulls.collapse_files=Alle Dateien einklappen @@ -1867,11 +1867,11 @@ pulls.switch_head_and_base=Head und Base vertauschen pulls.filter_branch=Branch filtern pulls.no_results=Keine Ergebnisse verfügbar. pulls.show_all_commits=Alle Commits anzeigen -pulls.show_changes_since_your_last_review=Zeige Änderungen seit deinem letzten Review +pulls.show_changes_since_your_last_review=Zeige Änderungen seit deiner letzten Sichtung pulls.showing_only_single_commit=Nur Änderungen aus Commit %[1]s werden angezeigt pulls.showing_specified_commit_range=Zeige nur die Änderungen zwischen %[1]s..%[2]s pulls.select_commit_hold_shift_for_range=Commit auswählen. Halte Shift + klicke, um eine Reihe auszuwählen -pulls.review_only_possible_for_full_diff=Ein Review ist nur möglich, wenn das vollständige Diff angezeigt wird +pulls.review_only_possible_for_full_diff=Eine Sichtung ist nur möglich, wenn der vollständige Diff angezeigt wird pulls.filter_changes_by_commit=Nach Commit filtern pulls.nothing_to_compare=Diese Branches sind identisch. Es muss kein Pull-Request erstellt werden. pulls.nothing_to_compare_and_allow_empty_pr=Diese Branches sind gleich. Der Pull-Request wird leer sein. @@ -1905,8 +1905,8 @@ pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht e pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin zusammenführen. pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt. -pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Reviewer angefragt wurden. -pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Reviewern fehlt. +pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Sichter angefragt wurden. +pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Sichtern fehlt. pulls.blocked_by_outdated_branch=Dieser Pull-Request ist blockiert, da er veraltet ist. pulls.blocked_by_changed_protected_files_1=Dieser Pull-Request ist blockiert, weil er eine geschützte Datei ändert: pulls.blocked_by_changed_protected_files_n=Dieser Pull-Request ist blockiert, weil er geschützte Dateien ändert: @@ -1919,14 +1919,14 @@ pulls.approve_count_1=%d Genehmigung pulls.approve_count_n=%d Genehmigungen pulls.reject_count_1=%d Änderungsanfrage pulls.reject_count_n=%d Änderungsanfragen -pulls.waiting_count_1=%d wartendes Review -pulls.waiting_count_n=%d wartende Reviews +pulls.waiting_count_1=%d wartende Sichtung +pulls.waiting_count_n=%d wartende Sichtungen pulls.wrong_commit_id=die Commit-ID muss eine Commit-ID auf dem Zielbranch sein pulls.no_merge_desc=Dieser Pull-Request kann nicht zusammengeführt werden, da alle Repository-Merge-Optionen deaktiviert sind. pulls.no_merge_helper=Aktiviere Mergeoptionen in den Repositoryeinstellungen oder führe den Pull-Request manuell zusammen. pulls.no_merge_wip=Dieser Pull-Request kann nicht zusammengeführt werden, da er als „Work in Progress“ (in Bearbeitung) markiert ist. -pulls.no_merge_not_ready=Dieser Pull-Request kann nicht zusammengeführt werden, überprüfe den Reviewstatus und die Statusprüfungen. +pulls.no_merge_not_ready=Dieser Pull-Request kann nicht zusammengeführt werden, überprüfe den Sichtungsstatus und die Statusprüfungen. pulls.no_merge_access=Du bist nicht berechtigt, diesen Pull-Request zusammenzuführen. pulls.merge_pull_request=Merge-Commit erstellen pulls.rebase_merge_pull_request=Rebasen und dann fast-forwarden @@ -2202,7 +2202,7 @@ settings.tracker_issue_style.numeric=Numerisch settings.tracker_issue_style.alphanumeric=Alphanumerisch settings.tracker_issue_style.regexp=Regulärer Ausdruck settings.tracker_issue_style.regexp_pattern=Regulärer Ausdruck -settings.tracker_issue_style.regexp_pattern_desc=Die erste gecapturte Gruppe wird statt {index} verwendet. +settings.tracker_issue_style.regexp_pattern_desc=Die erste gefundene Gruppe wird statt {index} verwendet. settings.tracker_url_format_desc=Du kannst die Platzhalter {user}, {repo}, {index} für den Benutzernamen, den Namen des Repositorys und die Issue-Nummer verwenden. settings.enable_timetracker=Zeiterfassung aktivieren settings.allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen @@ -2261,14 +2261,14 @@ settings.trust_model.collaborator=Mitarbeiter settings.trust_model.collaborator.long=Mitarbeiter: Vertraue Signaturen von Mitarbeitern settings.trust_model.collaborator.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als „vertrauenswürdig“ markiert (egal, ob sie mit dem Committer übereinstimmen oder nicht). Andernfalls werden gültige Signaturen als „nicht vertrauenswürdig“ markiert, falls die Signatur zum Committer passt, ansonsten werden sie als „nicht übereinstimmend“ markiert. settings.trust_model.committer=Committer -settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (Dies stimmt mit GitHub überein und zwingt signierte Commits von Forgejo dazu, Forgejo als Committer zu haben) +settings.trust_model.committer.long=Committer: Vertraue Signaturen, die zu Committern passen (dies stimmt mit GitHub überein und zwingt signierte Commits von Forgejo dazu, Forgejo als Committer zu haben) settings.trust_model.committer.desc=Gültige Signaturen werden nur dann als „vertrauenswürdig“ gekennzeichnet, wenn sie mit ihrem Committer übereinstimmen. Ansonsten werden sie als „nicht übereinstimmend“ markiert. Das führt dazu, dass Forgejo auf signierten Commits, bei denen der echte Committer als „Co-authored-by:“ oder „Co-committed-by:“ in der Beschreibung eingetragen wurde, als Committer gilt. Der Forgejo-Standard-Key muss zu einem Benutzer in der Datenbank passen. settings.trust_model.collaboratorcommitter=Mitarbeiter+Committer settings.trust_model.collaboratorcommitter.long=Mitarbeiter+Committer: Signaturen der Mitarbeiter vertrauen die mit dem Committer übereinstimmen settings.trust_model.collaboratorcommitter.desc=Gültige Signaturen von Mitarbeitern dieses Projekts werden als „vertrauenswürdig“ markiert, wenn sie mit dem Committer übereinstimmen. Andernfalls werden gültige Signaturen als „nicht vertrauenswürdig“ markiert, wenn die Signatur mit dem Committer übereinstimmt. Ansonsten werden sie als „nicht übereinstimmend“ markiert. Dies zwingt Forgejo, als Committer bei signierten Commits mit dem echten Committer als „Co-Authored-By:“ und „Co-Committed-By:“ im Commit zu markieren. Der Standard-Forgejo-Schlüssel muss mit einem Benutzer in der Datenbank übereinstimmen. settings.wiki_delete=Wiki-Daten löschen settings.wiki_delete_desc=Das Löschen von Wiki-Daten kann nicht rückgängig gemacht werden. Bitte sei vorsichtig. -settings.wiki_delete_notices_1=– Dies löscht und deaktiviert das Wiki für %s. +settings.wiki_delete_notices_1=– Dies wird das Repository-Wiki für %s dauerhaft löschen und deaktivieren. settings.confirm_wiki_delete=Wiki-Daten löschen settings.wiki_deletion_success=Repository-Wiki-Daten wurden gelöscht. settings.delete=Dieses Repository löschen @@ -2327,7 +2327,7 @@ settings.add_webhook_desc=Forgejo sendet eine POST-Anfrage mit fest settings.payload_url=Ziel-URL settings.http_method=HTTP-Methode settings.content_type=POST-Content-Type -settings.secret=Secret +settings.secret=Geheimnis settings.slack_username=Benutzername settings.slack_icon_url=Icon-URL settings.slack_color=Farbe @@ -2374,12 +2374,12 @@ settings.event_pull_request_milestone=Meilensteine settings.event_pull_request_milestone_desc=Meilenstein hinzugefügt, entfernt oder bearbeitet. settings.event_pull_request_comment=Kommentare settings.event_pull_request_comment_desc=Pull-Request-Kommentar angelegt, geändert oder gelöscht. -settings.event_pull_request_review=Reviews -settings.event_pull_request_review_desc=Pull-Request genehmigt, abgelehnt oder Review-Kommentare hinterlassen. +settings.event_pull_request_review=Sichtungen +settings.event_pull_request_review_desc=Pull-Request genehmigt, abgelehnt oder Sichtungskommentare hinterlassen. settings.event_pull_request_sync=Synchronisiert settings.event_pull_request_sync_desc=Branch automatisch mit Zielbranch aktualisiert. -settings.event_pull_request_review_request=Review-Anfragen -settings.event_pull_request_review_request_desc=Überprüfung des Pull-Requests angefragt oder die Anfrage entfernt. +settings.event_pull_request_review_request=Sichtungsanfragen +settings.event_pull_request_review_request_desc=Pull-Request-Sichtung angefragt oder Sichtungsanfrage entfernt. settings.event_pull_request_approvals=Genehmigungen zum Pull-Request settings.event_pull_request_merge=Pull-Request-Merge settings.event_package=Paket @@ -2467,11 +2467,11 @@ settings.protect_status_check_matched=Übereinstimmung settings.protect_invalid_status_check_pattern=Ungültiges Statusprüfungspattern: „%s“. settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Muster. settings.protect_required_approvals=Erforderliche Genehmigungen -settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Reviews. +settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Sichtungen. settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Positivliste beschränken -settings.protect_approvals_whitelist_enabled_desc=Nur Reviews von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. -settings.protect_approvals_whitelist_users=Nutzer, die reviewen dürfen -settings.protect_approvals_whitelist_teams=Teams, die reviewen dürfen +settings.protect_approvals_whitelist_enabled_desc=Nur Sichtungen von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Sichtungen von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. +settings.protect_approvals_whitelist_users=Nutzer, die sichten dürfen +settings.protect_approvals_whitelist_teams=Teams, die sichten dürfen settings.dismiss_stale_approvals=Entferne alte Genehmigungen settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt. settings.require_signed_commits=Signierte Commits erforderlich @@ -2489,10 +2489,10 @@ settings.remove_protected_branch_success=Branchschutzregel „%s“ wurde entfer settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ fehlgeschlagen. settings.protected_branch_deletion=Branch-Schutz löschen settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? -settings.block_rejected_reviews=Zusammenführung bei abgelehnten Reviews blockieren -settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn genügend Genehmigungen existieren. -settings.block_on_official_review_requests=Merge bei offiziellen Review-Anfragen blockieren -settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Review-Anfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. +settings.block_rejected_reviews=Zusammenführung bei abgelehnten Sichtungen blockieren +settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Sichter angefragt werden, auch wenn genügend Genehmigungen existieren. +settings.block_on_official_review_requests=Merge bei offiziellen Sichtungsanfragen blockieren +settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Sichtungsanfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist settings.block_outdated_branch_desc=Merge ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist. settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits: @@ -2539,7 +2539,7 @@ settings.lfs_filelist=LFS-Dateien, die in diesem Repository gespeichert sind settings.lfs_no_lfs_files=In diesem Repository sind keine LFS-Dateien gespeichert settings.lfs_findcommits=Commits finden settings.lfs_lfs_file_no_commits=Keine Commits für diese LFS-Datei gefunden -settings.lfs_noattribute=Dieser Pfad hat nicht das sperrbare Attribut im Standard-Branch +settings.lfs_noattribute=Dieser Pfad hat nicht das „lockable“-Attribut im Standard-Branch settings.lfs_delete=LFS-Datei mit OID %s löschen settings.lfs_delete_warning=Das Löschen einer LFS-Datei kann dazu führen, dass „Objekt existiert nicht“-Fehler beim Checkout auftreten. Bist du sicher? settings.lfs_findpointerfiles=Pointer-Dateien finden @@ -2580,8 +2580,8 @@ diff.show_unified_view=Gesamtansicht diff.whitespace_button=Leerzeichen diff.whitespace_show_everything=Alle Änderungen anzeigen diff.whitespace_ignore_all_whitespace=Ignoriere Leerzeichen beim Zeilen vergleichen -diff.whitespace_ignore_amount_changes=Ignoriere whitespace-Änderungen -diff.whitespace_ignore_at_eol=Ignoriere EOL-whitespace-Änderungen +diff.whitespace_ignore_amount_changes=Änderungen in der Anzahl der Leerzeichen und ähnlichen Zeichen ignorieren +diff.whitespace_ignore_at_eol=Änderungen an den Leerzeichen und ähnlichen Zeichen am Zeilenende ignorieren diff.stats_desc= %d geänderte Dateien mit %d neuen und %d gelöschten Zeilen diff.stats_desc_file=%d Änderungen: %d Ergänzungen und %d Löschungen diff.bin=BIN @@ -2604,11 +2604,11 @@ diff.comment.placeholder=Kommentieren diff.comment.markdown_info=Styling mit Markdown wird unterstützt. diff.comment.add_single_comment=Einzelnen Kommentar hinzufügen diff.comment.add_review_comment=Kommentar hinzufügen -diff.comment.start_review=Review starten +diff.comment.start_review=Sichtung starten diff.comment.reply=Antworten -diff.review=Review abschließen -diff.review.header=Review einreichen -diff.review.placeholder=Kommentar zum Review +diff.review=Sichtung abschließen +diff.review.header=Sichtung einreichen +diff.review.placeholder=Kommentar zur Sichtung diff.review.comment=Kommentieren diff.review.approve=Genehmigen diff.review.self_reject=Pull-Request-Autoren können keine Änderungen an ihren eigenen Pull-Request anfordern @@ -2656,7 +2656,7 @@ release.edit_release=Release aktualisieren release.delete_release=Release löschen release.delete_tag=Tag löschen release.deletion=Release löschen -release.deletion_desc=Beim Entfernen wird ein Release nur von Forgejo gelöscht. Es betrifft weder den Git-Tag, noch den Inhalt des Repos oder seinen Änderungsverlauf. Fortfahren? +release.deletion_desc=Beim Entfernen wird ein Release nur von Forgejo gelöscht. Es betrifft weder den Git-Tag noch den Inhalt des Repos oder seinen Änderungsverlauf. Fortfahren? release.deletion_success=Das Release wurde gelöscht. release.deletion_tag_desc=Löscht dieses Tag aus dem Projektarchiv. Repository-Inhalt und Verlauf bleiben unverändert. Fortfahren? release.deletion_tag_success=Der Tag wurde gelöscht. @@ -2666,7 +2666,7 @@ release.tag_name_protected=Der Tag-Name ist geschützt. release.tag_already_exist=Dieser Tag-Name existiert bereits. release.downloads=Downloads release.download_count=Downloads: %s -release.add_tag_msg=Titel und Beschreibung des Releases als Tag Nachricht verwenden. +release.add_tag_msg=Titel und Beschreibung des Releases als Tag-Nachricht verwenden. release.add_tag=Tag erstellen release.releases_for=Releases für %s release.tags_for=Tags für %s @@ -2766,7 +2766,7 @@ wiki.cancel = Abbrechen settings.wiki_globally_editable = Allen erlauben, das Wiki zu bearbeiten settings.protect_branch_name_pattern_desc = Geschützte Branch-Namens-Patterns. Siehe die Dokumentation für Pattern-Syntax. Beispiele: main, release/** settings.ignore_stale_approvals = Abgestandene Genehmigungen ignorieren -settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Reviews), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Reviews bereits verworfen werden. +settings.ignore_stale_approvals_desc = Genehmigungen, welche für ältere Commits gemacht wurden (abgestandene Sichtungen), nicht in die Gesamtzahl der Genehmigung des PRs mitzählen. Irrelevant, falls abgestandene Sichtungen bereits verworfen werden. pulls.commit_ref_at = `referenzierte diesen Pull-Request aus einem Commit %s` pulls.fast_forward_only_merge_pull_request = Nur Fast-forward pulls.cmd_instruction_checkout_desc = Checke einen neuen Branch aus deinem Projekt-Repository aus und teste die Änderungen. @@ -2782,7 +2782,7 @@ activity.navbar.contributors = Mitwirkende contributors.contribution_type.deletions = Löschungen contributors.contribution_type.additions = Einfügungen contributors.contribution_type.filter_label = Art des Beitrags: -vendored = Vendored +vendored = Gevendort activity.navbar.pulse = Puls pulls.made_using_agit = AGit settings.confirmation_string = Bestätigungsstring @@ -2795,7 +2795,7 @@ pulls.merged_title_desc_one = hat %[1]d Commit von %[2]s nach %[2]s nach %[3]s zusammenführen open_with_editor = Öffnen mit %s commits.search_branch = Dieser Branch -pulls.ready_for_review = Bereit zum Review? +pulls.ready_for_review = Bereit zur Sichtung? settings.rename_branch_failed_protected = Branch %s kann nicht umbenannt werden, weil er ein geschützter Branch ist. editor.commit_id_not_matching = Die Datei wurde geändert, während du sie bearbeitet hast. Committe in einen neuen Branch, dann führe einen Merge durch. editor.push_out_of_date = Der Push scheint veraltet zu sein. @@ -2824,7 +2824,7 @@ settings.sourcehut_builds.secrets_helper = Dem Job zugriff auf die Build-Geheimn settings.web_hook_name_sourcehut_builds = SourceHut-Builds settings.graphql_url = GraphQL-URL settings.matrix.room_id_helper = Die Raum-ID kann über den Element-Webclient ermittelt werden: Raumeinstellungen > erweitert > interne Raum-ID. Beispielsweise %s. -settings.sourcehut_builds.access_token_helper = Zugangstoken, der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen builds.sr.ht-Token oder einen builds.sr.ht-Token mit Zugriff auf die Secrets. +settings.sourcehut_builds.access_token_helper = Zugangstoken, der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen builds.sr.ht-Token oder einen builds.sr.ht-Token mit Zugriff auf die Geheimnisse. settings.matrix.access_token_helper = Es wird empfohlen, hierfür ein dediziertes Matrix-Konto anzulegen. Der Zugangstoken kann in einem Inkognito-Tab über den Element-Webclient geholt werden: Benutzermenü (oben links) > alle Einstellungen > Hilfe & Info > erweitert > Zugriffstoken (direkt unter der Heim-Server-URL). Schließe dann den Inkognito-Tab (Abmelden würde den Token ungültig machen). release.hide_archive_links = Automatisch generierte Archive verstecken release.hide_archive_links_helper = Verstecke automatisch generierte Quellcodearchive für diesen Release. Zum Beispiel, wenn du deine eigenen hochlädst. @@ -2873,9 +2873,9 @@ mirror_use_ssh.not_available = SSH-Authentifizierung ist nicht verfügbar. issues.new.assign_to_me = Mir selbst zuweisen issues.all_title = Alle settings.discord_icon_url.exceeds_max_length = Die Icon-URL darf eine Länge von 2048 Zeichen nicht überschreiten -issues.review.add_review_requests = hat Reviews von %[1]s %[2]s angefragt -issues.review.remove_review_requests = hat Aufforderungen zum Review an %[1]s %[2]s entfernt -issues.review.add_remove_review_requests = hat Reviews von %[1]s angefragt und hat die Aufforderungen zum Review an %[2]s %[3]s entfernt +issues.review.add_review_requests = hat Sichtungen von %[1]s %[2]s angefragt +issues.review.remove_review_requests = hat die Sichtungsanfragen an %[1]s %[2]s entfernt +issues.review.add_remove_review_requests = hat Sichtungen von %[1]s angefragt und die Sichtungsanfragen an %[2]s %[3]s entfernt pulls.delete_after_merge.head_branch.is_default = Der Head-Branch, den du löschen willst, ist der Standardbranch und kann nicht gelöscht werden. pulls.delete_after_merge.head_branch.is_protected = Der Head-Branch, den du löschen willst, ist ein geschützter Branch und kann nicht gelöscht werden. pulls.delete_after_merge.head_branch.insufficient_branch = Du hast keine Erlaubnis, den Head-Branch zu löschen. @@ -2883,9 +2883,9 @@ issues.filter_sort.relevance = Relevanz diff.git-notes.add = Anmerkung hinzufügen diff.git-notes.remove-header = Anmerkung entfernen diff.git-notes.remove-body = Diese Anmerkung wird entfernt. -issues.num_reviews_one = %d Review +issues.num_reviews_one = %d Sichtung issues.summary_card_alt = Zusammenfassung eines Issues mit dem Titel „%s“ im Repository %s -issues.num_reviews_few = %d Reviews +issues.num_reviews_few = %d Sichtungen editor.add_tmpl.filename = Dateiname settings.default_update_style_desc = Standard-Aktualisierungsart um Pull-Requests zu aktualisieren, die hinter dem Base-Branch sind. new_advanced = Erweiterte Einstellungen @@ -2902,7 +2902,7 @@ issues.reaction.alt_add = Füge %[1]s Reaktion zum Kommentar hinzu. issues.reaction.alt_remove = Entferne %[1]s Reaktion von diesem Kommentar. summary_card_alt = Zusammenfassungskarte des Repositorys %s release.summary_card_alt = Übersichtskarte eines Releases mit dem Titel „%s“ im Repository %s -archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gereviewt werden. +archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gesichtet werden. 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 @@ -2922,7 +2922,7 @@ settings.event_action_success_desc = Action-Run war erfolgreich. settings.event_action_failure = Fehlschlag settings.event_action_success = Erfolg settings.event_header_action = Action-Run-Ereignisse -settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Arbeitsablauf fehlgeschlagen ist. +settings.event_action_recover_desc = Action-Run war erfolgreich, nachdem der letzte Action-Run im selben Workflow fehlgeschlagen ist. settings.event_action_recover = Wiederherstellen issues.filter_type.all_pull_requests = Alle Pull-Requests @@ -3116,7 +3116,7 @@ dashboard.repo_health_check=Healthchecks für alle Repositorys ausführen dashboard.check_repo_stats=Überprüfe alle Repository-Statistiken dashboard.archive_cleanup=Alte Repository-Archive löschen dashboard.deleted_branches_cleanup=Gelöschte Branches bereinigen -dashboard.update_migration_poster_id=Migration Poster-IDs updaten +dashboard.update_migration_poster_id=Migrations-Poster-IDs aktualisieren dashboard.git_gc_repos=Garbage-Collection für alle Repositorys ausführen dashboard.resync_all_sshkeys=Die Datei „.ssh/authorized_keys“ mit Forgejo-SSH-Schlüsseln aktualisieren. dashboard.resync_all_sshprincipals=Aktualisiere die Datei „.ssh/authorized_principals“ mit Forgejo-SSH-Principals. @@ -3159,7 +3159,7 @@ dashboard.delete_old_actions=Alle alten Aktivitäten aus der Datenbank löschen dashboard.delete_old_actions.started=Löschen aller alten Aktivitäten aus der Datenbank gestartet. dashboard.update_checker=Update-Checker dashboard.delete_old_system_notices=Alle alten Systemmeldungen aus der Datenbank löschen -dashboard.gc_lfs=Garbage-Collection für LFS Meta-Objekte ausführen +dashboard.gc_lfs=Garbage-Collection für LFS-Meta-Objekte ausführen dashboard.stop_zombie_tasks=Zombie-Actions-Aufgaben stoppen dashboard.stop_endless_tasks=Endlose Actions-Aufgaben stoppen dashboard.cancel_abandoned_jobs=Aufgegebene Actions-Jobs abbrechen @@ -3335,7 +3335,7 @@ auths.pam_email_domain=PAM-E-Mail-Domain (optional) auths.oauth2_provider=OAuth2-Anbieter auths.oauth2_icon_url=Symbol-URL auths.oauth2_clientID=Client-ID (Schlüssel) -auths.oauth2_clientSecret=Client-Secret +auths.oauth2_clientSecret=Client-Geheimnis auths.openIdConnectAutoDiscoveryURL=OpenID-Connect-Auto-Discovery-URL auths.oauth2_use_custom_url=Benutzerdefinierte URLs anstelle von Standard-URLs verwenden auths.oauth2_tokenURL=Token-URL @@ -3343,14 +3343,14 @@ auths.oauth2_authURL=Authorisierungs-URL auths.oauth2_profileURL=Profil-URL auths.oauth2_emailURL=E-Mail-URL auths.skip_local_two_fa=Lokale 2FA überspringen -auths.skip_local_two_fa_helper=Leer lassen bedeutet, dass lokale User die 2FA immer noch bestehen müssen, um sich anzumelden +auths.skip_local_two_fa_helper=Das Leerlassen bedeutet, dass lokale User die 2FA immer noch bestehen müssen, um sich anzumelden auths.oauth2_tenant=Inhaber auths.oauth2_scopes=Zusätzliche Bereiche auths.oauth2_required_claim_name=Benötigter Claim-Name auths.oauth2_required_claim_name_helper=Setze diesen Namen, damit Nutzer aus dieser Quelle sich nur anmelden dürfen, wenn sie einen Claim mit diesem Namen besitzen auths.oauth2_required_claim_value=Benötigter Claim-Wert auths.oauth2_required_claim_value_helper=Setze diesen Wert, damit Nutzer aus dieser Quelle sich nur anmelden dürfen, wenn sie einen Claim mit diesem Namen und Wert besitzen -auths.oauth2_group_claim_name=Claim-Name, der Gruppennamen für diese Quelle angibt. (Optional) +auths.oauth2_group_claim_name=Claim-Name, der Gruppennamen für diese Quelle angibt (optional). auths.oauth2_admin_group=Gruppen-Claim-Wert für Administratoren (optional – erfordert Claim-Namen oben). auths.oauth2_restricted_group=Gruppen-Claim-Wert für eingeschränkte User. (Optional – erfordert Claim-Namen oben) auths.oauth2_map_group_to_team=Gruppen aus OAuth-Claims den Organisationsteams zuordnen (optional – oben muss der Name des Claims angegeben werden). @@ -3395,7 +3395,7 @@ auths.still_in_used=Diese Authentifizierungsquelle wird noch verwendet. Bearbeit auths.deletion_success=Die Authentifizierungsquelle „%s“ wurde gelöscht. auths.login_source_exist=Die Authentifizierungsquelle „%s“ existiert bereits. auths.login_source_of_type_exist=Eine Authentifizierungart dieses Typs existiert bereits. -auths.unable_to_initialize_openid=OpenID Connect Provider konnte nicht initialisiert werden: %s +auths.unable_to_initialize_openid=Provider für OpenID Connect konnte nicht initialisiert werden: %s auths.invalid_openIdConnectAutoDiscoveryURL=Ungültige Auto-Discovery-URL (dies muss eine gültige URL sein, die mit http:// oder https:// beginnt) config.server_config=Serverkonfiguration @@ -3526,7 +3526,7 @@ config.git_pull_timeout=Zeitlimit für Pull config.git_gc_timeout=Zeitlimit für GC config.log_config=Protokollierungs-Konfiguration -config.logger_name_fmt=Logger: %s +config.logger_name_fmt=Protokollierer: %s config.disabled_logger=Deaktiviert config.access_log_mode=Zugriffsprotokoll-Modus config.access_log_template=Zugriffsprotokoll-Vorlage @@ -3650,7 +3650,7 @@ mirror_sync_delete=hat die Referenz des Spiegels %[2]s in %[3]s#%[2]s genehmigt` reject_pull_request=`schlug Änderungen für %[3]s#%[2]s vor` publish_release=`veröffentlichte Release %[4]s in %[3]s` -review_dismissed=`verwarf das Review von %[4]s in %[3]s#%[2]s` +review_dismissed=`verwarf die Sichtung von %[4]s in %[3]s#%[2]s` review_dismissed_reason=Grund: create_branch=legte den Branch %[3]s in %[4]s an starred_repo=favorisierte %[2]s @@ -3765,9 +3765,9 @@ conda.registry=Richte diese Registry als Conda-Repository in deiner .conda conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befehl aus: container.details.type=Abbildtyp container.details.platform=Plattform -container.pull=Downloade das Container-Image aus der Kommandozeile: +container.pull=Lade das Container-Image von der Kommandozeile aus herunter: container.digest=Prüfsumme -container.multi_arch=Betriebsystem / Architektur +container.multi_arch=Betriebsystem/Architektur container.layers=Abbildebenen container.labels=Labels container.labels.key=Schlüssel @@ -3781,7 +3781,7 @@ debian.repository=Repository-Informationen debian.repository.distributions=Distributionen debian.repository.components=Komponenten debian.repository.architectures=Architekturen -generic.download=Downloade das Paket aus der Kommandozeile: +generic.download=Lade das Paket mit der Kommandozeile herunter: go.install=Installiere das Paket über die Kommandozeile: helm.registry=Diese Paketverwaltung über die Kommandozeile einrichten: helm.install=Nutze folgenden Befehl, um das Paket zu installieren: @@ -3813,10 +3813,10 @@ rubygems.install=Um das Paket mit gem zu installieren, führe den folgenden Befe rubygems.install2=oder füg es zum Gemfile hinzu: rubygems.dependencies.runtime=Laufzeitabhängigkeiten rubygems.dependencies.development=Entwicklungsabhängigkeiten -rubygems.required.ruby=Benötigt Ruby Version -rubygems.required.rubygems=Benötigt RubyGem Version +rubygems.required.ruby=Benötigt Ruby-Version +rubygems.required.rubygems=Benötigt RubyGem-Version swift.registry=Diese Registry über die Kommandozeile einrichten: -swift.install=Füge das Paket deiner Package.swift Datei hinzu: +swift.install=Füge das Paket deiner Package.swift-Datei hinzu: swift.install2=und führe den folgenden Befehl aus: vagrant.install=Um eine Vagrant-Box hinzuzufügen, führe den folgenden Befehl aus: settings.link=Dieses Paket einem Repository zuweisen @@ -3892,19 +3892,19 @@ alt.repository.architectures = Architekturen alt.repository.multiple_groups = Dieses Paket ist in verschiedenen Gruppen verfügbar. [secrets] -secrets=Secrets -description=Secrets werden an bestimmte Aktionen weitergegeben und können nicht anderweitig ausgelesen werden. -none=Noch keine Secrets vorhanden. -creation=Secret hinzufügen +secrets=Geheimnisse +description=Geheimnisse werden an bestimmte Aktionen weitergegeben und können nicht anderweitig ausgelesen werden. +none=Noch keine Geheimnisse vorhanden. +creation=Geheimnis hinzufügen creation.name_placeholder=Groß-/Kleinschreibung wird ignoriert, nur alphanumerische Zeichen oder Unterstriche, darf nicht mit GITEA_ oder GITHUB_ beginnen creation.value_placeholder=Beliebigen Inhalt eingeben. Leerzeichen am Anfang und Ende werden weggelassen. -creation.success=Das Secret „%s“ wurde hinzugefügt. -creation.failed=Secret konnte nicht hinzugefügt werden. -deletion=Secret entfernen -deletion.description=Das Entfernen eines Secrets kann nicht rückgängig gemacht werden. Fortfahren? -deletion.success=Das Secret wurde entfernt. -deletion.failed=Secret konnte nicht entfernt werden. -management=Secrets verwalten +creation.success=Das Geheimnis „%s“ wurde hinzugefügt. +creation.failed=Geheimnis konnte nicht hinzugefügt werden. +deletion=Geheimnis entfernen +deletion.description=Das Entfernen eines Geheimnisses kann nicht rückgängig gemacht werden. Fortfahren? +deletion.success=Das Geheimnis wurde entfernt. +deletion.failed=Geheimnis konnte nicht entfernt werden. +management=Geheimnisse verwalten [actions] actions=Actions diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index d85a3b13e7..2e0928bb1f 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -29,7 +29,7 @@ password=Пароль access_token=Токен доступу re_type=Підтвердження пароля captcha=CAPTCHA -twofa=Двофакторна авторизація +twofa=Двофакторна автентифікація twofa_scratch=Двофакторний одноразовий пароль passcode=Код доступу @@ -112,7 +112,7 @@ webauthn_error_unknown = Сталася невідома помилка. Буд webauthn_error_unable_to_process = Сервер не зміг обробити запит. webauthn_error_duplicated = Запит із наданим ключем безпеки відхилено. Впевніться, що цього ключа ще не зареєстровано. webauthn_error_empty = Ключ слід якось назвати. -new_project_column = Нова колонка +new_project_column = Новий стовпчик retry = Повторити rerun = Перезапустити rerun_all = Перезапустити всі завдання @@ -201,7 +201,7 @@ buttons.mention.tooltip = Згадати користувача чи коман buttons.ref.tooltip = Послатися на задачу чи на запит на злиття buttons.enable_monospace_font = Увімкнути моноширинний шрифт buttons.new_table.tooltip = Додати таблицю -table_modal.label.columns = Стовпці +table_modal.label.columns = Стовпчики table_modal.header = Додати таблицю table_modal.placeholder.header = Заголовок table_modal.placeholder.content = Вміст @@ -343,13 +343,13 @@ enable_update_checker = Увімкнути перевірку оновлень require_db_desc = Forgejo вимагає MySQL, PostgreSQL, SQLite3 чи TiDB (протокол MySQL). allow_only_external_registration = Дозволити реєстрацію тільки через зовнішні сервіси require_sign_in_view.description = Обмежити доступ до контенту лише користувачам, що увійшли. Гості зможуть лише відвідувати сторінки автентифікації. -password_algorithm_helper = Встановити алгоритм хешування паролів. Алгоритми мають різні вимоги та силу. Алгоритм argon2 є досить безпечним, проте споживає багато памʼяті та є недоречним для малих систем. +password_algorithm_helper = Установіть алгоритм хешування паролів. Алгоритми мають різні вимоги і стійкість. Алгоритм argon2 є досить безпечним, проте споживає багато памʼяті та є недоречним для малих систем. app_slogan = Гасло екземпляра app_slogan_helper = Уведіть гасло вашого екземпляра тут. Залиште порожнім, аби вимкнути. run_user_helper = Імʼя користувача операційної системи, від якого запущено Forgejo. Зауважте, що цей користувач повинен мати доступ до кореневої теки репозиторію. smtp_from_invalid = Адреса з «Відправляти email від імені» недійсна allow_dots_in_usernames = Дозволити використання крапки в іменах користувачів. Не впливає на облікові записи, що вже існують. -invalid_password_algorithm = Недійсний варіант алгоритму хешування паролів +invalid_password_algorithm = Недійсний алгоритм хешування паролів enable_update_checker_helper_forgejo = Наявність нових версій Forgejo періодично перевірятиметься через перевірку запису TXT DNS на release.forgejo.org. [home] @@ -734,7 +734,7 @@ applications=Додатки orgs=Організації repos=Репозиторії delete=Видалити обліковий запис -twofa=Двофакторна авторизація (TOTP) +twofa=Двофакторна автентифікація (TOTP) account_link=Прив'язані облікові записи organization=Організації @@ -1710,7 +1710,7 @@ pulls.reject_count_1=%d запит на зміну pulls.reject_count_n=%d запити на зміну pulls.waiting_count_1=очікується %d рецензія pulls.waiting_count_n=очікується %d рецензії(й) -pulls.wrong_commit_id=ID коміта повинен бути ID коміта в цільовій гілці +pulls.wrong_commit_id=ID коміту повинен бути ID коміту в цільовій гілці pulls.no_merge_desc=Цей запити на злиття неможливо злити, оскільки всі параметри об'єднання репозиторія вимкнено. pulls.no_merge_helper=Увімкніть параметри злиття в налаштуваннях репозиторія або злийте запити на злиття вручну. @@ -1728,7 +1728,7 @@ pulls.require_signed_wont_sign=Гілка вимагає підписаних к pulls.invalid_merge_option=Цей параметр злиття не можна використовувати для цього Pull Request'а. pulls.merge_conflict=Не вдалося об'єднати: при об'єднанні виник конфлікт. Підказка: спробуйте іншу стратегію pulls.merge_conflict_summary=Повідомлення про помилку -pulls.rebase_conflict=Не вдалося об'єднати: виник конфлікт під час перебазування коміта: %[1]s. Підказка: спробуйте іншу стратегію +pulls.rebase_conflict=Не вдалося об'єднати: виник конфлікт під час перебазування коміту: %[1]s. Підказка: спробуйте іншу стратегію pulls.rebase_conflict_summary=Повідомлення про помилку pulls.unrelated_histories=Не вдалося об'єднати: head та base злиття не мають спільної історії. Підказка: спробуйте іншу стратегію pulls.merge_out_of_date=Не вдалося об'єднати: base було оновлено, поки відбувалося об'єднання. Підказка: спробуйте знову. @@ -1897,7 +1897,7 @@ settings.mirror_settings.direction=Напрямок settings.mirror_settings.direction.pull=Pull settings.mirror_settings.direction.push=Push settings.mirror_settings.last_update=Останнє оновлення -settings.mirror_settings.push_mirror.none=Не налаштовано дзеркало push +settings.mirror_settings.push_mirror.none=Push-дзеркала не налаштовано settings.mirror_settings.push_mirror.remote_url=URL віддаленого репозиторію Git settings.mirror_settings.push_mirror.add=Додати push-дзеркало @@ -2344,7 +2344,7 @@ issues.author = Автор issues.author_helper = Цей користувач - автор. issues.close = Закрити задачу issues.role.owner_helper = Цей користувач є власником цього репозиторію. -settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push та pull дзеркала можна дізнатися тут: +settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push- та pull-дзеркала можна дізнатися тут: issues.comment.blocked_by_user = Ви не можете коментувати цю задачу, оскільки вас заблокував власник репозиторію або автор цієї задачі. editor.add_file = Додати файл from_comment = (коментар) @@ -2432,7 +2432,7 @@ pulls.is_ancestor = Цю гілку вже включено в цільову г pulls.has_merged = Помилка: запит на злиття вже об'єднано, неможливо об'єднати знову чи змінити цільову гілку. pulls.head_out_of_date = Не вдалося об'єднати: head було оновлено, поки відбувалося об'єднання. Підказка: спробуйте знову. no_eol.tooltip = У цьому файлі відсутній символ закінчення рядка (EOL) у кінці. -settings.trust_model.committer.desc = Допустимі підписи будуть позначатися як «довірені», тільки якщо вони відповідають автору коміта, в іншому випадку вони позначатимуться як «невідповідні». Це змусить Forgejo бути автором підписаних комітів, а фактичного автора зазначати в трейлерах «Co-authored-by» і «Co-committed-by» в описі коміта. Типовий ключ Forgejo повинен відповідати користувачу в базі даних. +settings.trust_model.committer.desc = Допустимі підписи будуть позначатися як «довірені», тільки якщо вони відповідають авторові коміту, в іншому випадку вони позначатимуться як «невідповідні». Це змусить Forgejo бути автором підписаних комітів, а фактичного автора зазначати в трейлерах «Co-authored-by» і «Co-committed-by» в описі коміту. Типовий ключ Forgejo повинен відповідати користувачу в базі даних. pulls.clear_merge_message_hint = Очищення повідомлення про об'єднання видалить лише вміст повідомлення коміту і збереже згенеровані git-трейлери, такі як «Co-Authored-By…». branch.delete_branch_has_new_commits = Гілку «%s» не можна видалити, оскільки після об'єднання було додано нові коміти. settings.graphql_url = Посилання GraphQL @@ -2515,7 +2515,7 @@ projects.column.set_default = Установити за замовчування settings.federation_following_repos = URL-адреси відстежуваних репозиторіїв. Через «;», без пробілів. settings.federation_not_enabled = Федерацію вимкнено у вашому екземплярі. settings.federation_settings = Налаштування федерації -signing.wont_sign.nokey = Цей екземпляр не має ключа для підписання цього коміта. +signing.wont_sign.nokey = Цей екземпляр не має ключа для підписання цього коміту. settings.federation_apapiurl = URL федерації цього репозиторію. Скопіюйте її та вставте в налаштування федерації іншого репозиторію як URL-адресу відстежуваного репозиторію. fork_branch = Гілка, яку буде клоновано у форк already_forked = Ви вже створили форк %s @@ -2555,7 +2555,7 @@ activity.navbar.pulse = Пульс open_with_editor = Відкрити в %s commits.view_single_diff = Переглянути зміни до цього файлу, внесені у цьому коміті pulls.editable = Редаговане -pulls.editable_explanation = Цей запит на злиття дозволяє редагування від розробників. Ви можете зробити свій внесок безпосередньо до нього. +pulls.editable_explanation = Цей запит на злиття дозволено редагувати супроводжувачам. Ви можете зробити свій внесок безпосередньо до нього. admin.failed_to_replace_flags = Не вдалося замінити прапорці репозиторія admin.enabled_flags = Для репозиторія ввімкнено прапорці: admin.flags_replaced = Прапорці репозиторія замінено @@ -2777,6 +2777,59 @@ branch.delete_desc = Видалення гілки є остаточним. Хо branch.branch_name_conflict = Назва гілки «%s» конфліктує з наявною гілкою «%s». branch.tag_collision = Неможливо створити гілку «%s», оскільки у репозиторії вже є тег із такою назвою. +blame.ignore_revs = Зміни в .git-blame-ignore-revs ігноруються. Натисніть тут, щоб обійти це і переглянути авторство у звичайному вигляді. +blame.ignore_revs.failed = Не вдалося проігнорувати зміни в .git-blame-ignore-revs. +template.git_hooks_tooltip = Наразі ви не можете змінювати або видаляти додані Git-хуки. Вибирайте лише якщо ви довіряєте репозиторію шаблону. +migrate.github_token_desc = Ви можете ввести тут один або кілька токенів через кому, щоб пришвидшити міграцію в обхід обмеження частоти звернень до API GitHub. ОБЕРЕЖНО: зловживання цією функцією може порушити політику постачальника послуг і призвести до блокування облікового запису. +migrate.github.description = Перенесіть дані з github.com або сервера GitHub Enterprise. +migrate.cancel_migrating_confirm = Бажаєте скасувати перенесення? +editor.new_branch_name = Укажіть назву нової гілки для цього коміту +projects.column.edit = Редагувати стовпчик +projects.column.new_submit = Створити стовпчик +projects.column.new = Новий стовпчик +projects.column.set_default_desc = Призначити цей стовпчик за замовчуванням для задач і запитів на злиття без категорії +projects.column.delete = Видалити стовпчик +projects.column.deletion_desc = Видалення стовпчика проєкту призведе до переміщення всіх пов'язаних із ним задач до стовпчика за замовчуванням. Продовжити? +issues.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст задачі. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +issues.label_templates.fail_to_load_file = Не вдалося завантажити файл шаблону міток «%s»: %v +issues.reaction.add = Додати реакцію +issues.reaction.alt_many = %[1]s і ще %[2]d реагують %[3]s. +issues.reaction.alt_remove = Прибрати реакцію %[1] з коментаря. +issues.reaction.alt_add = Додати реакцію %[1]s до коментаря. +issues.archived_label_description = (Архівна) %s +issues.delete.text = Ви дійсно хочете видалити цю задачу? (Весь її вміст буде остаточно видалено. Можливо, варто її закрити і зберегти в архіві) +pulls.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст запиту на злиття. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +pulls.allow_edits_from_maintainers = Дозволити редагування від супроводжувачів +pulls.showing_only_single_commit = Показано тільки зміни коміту %[1]s +pulls.showing_specified_commit_range = Показано тільки зміни між %[1]s..%[2]s +pulls.blocked_by_approvals = Цей запит на злиття ще не має достатньої кількості схвалень. Отримано %d з %d схвалень. +pulls.blocked_by_rejection = Цей запит на злиття містить зміни, запропоновані офіційним рецензентом. +pulls.blocked_by_official_review_requests = Цей запит на злиття заблоковано, оскільки йому бракує схвалення від одного або кількох офіційних рецензентів. +pulls.blocked_by_outdated_branch = Цей запит на злиття заблоковано, оскільки він застарів. +pulls.blocked_by_changed_protected_files_1 = Цей запит на злиття заблоковано, оскільки він змінює захищений файл: +pulls.blocked_by_changed_protected_files_n = Цей запит на злиття заблоковано, оскільки він змінює захищені файли: +pulls.auto_merge_newly_scheduled = Заплановано об'єднати запит на злиття після успішного завершення всіх перевірок. +pulls.auto_merge_has_pending_schedule = %[1]s планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[2]s. +pulls.auto_merge_newly_scheduled_comment = `планує автоматично об'єднати цей запит на злиття після успішного завершення всіх перевірок %[1]s` +pulls.auto_merge_canceled_schedule_comment = `скасовує автоматичне об'єднання цього запиту на злиття після успішного завершення всіх перевірок %[1]s` +pulls.delete.text = Ви дійсно хочете видалити цей запит на злиття? (Весь його вміст буде остаточно видалено. Можливо, варто його закрити і зберегти в архіві) +comments.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст коментаря. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін +signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає публічного ключа, пов'язаного з вашим обліковим записом. +signing.wont_sign.twofa = Щоб підписувати коміти, у вас повинна бути ввімкнена двофакторна автентифікація. +signing.wont_sign.parentsigned = Цей коміт не буде підписано, оскільки не підписано батьківський коміт. +signing.wont_sign.basesigned = Злиття не буде підписано, оскільки не підписано базовий коміт. +signing.wont_sign.headsigned = Злиття не буде підписано, оскільки не підписано головний коміт. +signing.wont_sign.commitssigned = Злиття не буде підписано, оскільки всі пов'язані з ним коміти не підписані. +signing.wont_sign.approved = Злиття не буде підписано, оскільки запит на злиття не схвалено. +settings.mirror_settings.docs = Налаштуйте свій репозиторій на автоматичну синхронізацію комітів, тегів і гілок з іншим репозиторієм. +settings.mirror_settings.docs.disabled_pull_mirror.instructions = Налаштуйте свій проєкт на автоматичне надсилання комітів, тегів і гілок до іншого репозиторію. Pull-дзеркала вимкнено адміністратором сайту. +settings.mirror_settings.docs.disabled_push_mirror.instructions = Налаштуйте свій проєкт на автоматичне отримання комітів, тегів і гілок з іншого репозиторію. +settings.mirror_settings.docs.disabled_push_mirror.info = Push-дзеркала вимкнено адміністратором сайту. +settings.pulls.default_allow_edits_from_maintainers = За замовчуванням дозволити редагування від супроводжувачів +settings.authorization_header = Заголовок авторизації +settings.authorization_header_desc = За наявності буде включено як заголовок авторизації для запитів. Приклади: %s. +settings.ignore_stale_approvals = Ігнорувати застарілі схвалення + [graphs] contributors.what = внески component_loading_info = Це може зайняти деякий час… @@ -3020,7 +3073,7 @@ users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора users.is_restricted=Обмежений users.allow_git_hook=Може створювати Git-хуки -users.allow_git_hook_tooltip=Git хуки виконуються від імені користувача OS сервісу Forgejo і мають однаковий рівень доступу до хоста. Як результат, користувачі з доступом до Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних, що використовуються в Forgejo. Отже, вони також здатні отримати права адміністратора Forgejo. +users.allow_git_hook_tooltip=Git-хуки виконуються від імені користувача ОС, від якого запущено Forgejo, і мають той самий рівень доступу до хоста. Таким чином, користувачі зі спеціальними правами Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних Forgejo. Вони також здатні отримати права адміністратора Forgejo. users.allow_import_local=Може імпортувати локальні репозиторії users.allow_create_organization=Може створювати організації users.update_profile=Оновити обліковий запис @@ -3174,7 +3227,7 @@ auths.tip.discord=Зареєструйте новий додаток на %s auths.tip.yandex=Створіть новий додаток на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) auths.edit=Редагувати джерело автентифікації -auths.activated=Це джерело авторизації активоване +auths.activated=Це джерело автентифікація активоване auths.update_success=Параметри аутентифікації оновлені. auths.update=Оновити джерело автентифікації auths.delete=Видалити джерело автентифікації @@ -3200,7 +3253,7 @@ config.repo_root_path=Шлях до кореня репозиторію config.lfs_root_path=Кореневий шлях LFS config.log_file_root_path=Шлях до лог файлу config.script_type=Тип скрипта -config.reverse_auth_user=Ім'я користувача для авторизації на reverse proxy +config.reverse_auth_user=Користувач для авторизації на зворотному проксі config.ssh_config=Конфігурація SSH config.ssh_enabled=Увімкнено @@ -3431,6 +3484,13 @@ auths.enable_ldap_groups = Увімкнути групи LDAP auths.oauth2_map_group_to_team_removal = Видаляти користувачів із синхронізованих команд, якщо користувачі не належать до відповідної групи. config.mailer_smtp_addr = Адреса SMTP +auths.new_success = Метод автентифікації «%s» додано. +auths.unable_to_initialize_openid = Не вдалося ініціалізувати постачальника OpenID Connect: %s +config.cache_test = Перевірити кеш +config.cache_test_failed = Не вдалося перевірити кеш: %v. +config.cache_test_slow = Перевірку кешу завершено успішно, але відповідь повільна: %s. +config.cache_test_succeeded = Перевірку кешу завершено успішно, відповідь отримано через %s. + [action] create_repo=створив(ла) репозиторій %s rename_repo=репозиторій перейменовано з %[1]s на %[3]s @@ -3712,7 +3772,7 @@ variables.management = Керування змінними variables.id_not_exist = Змінної з ідентифікатором %d не існує. variables.edit = Редагувати змінну runs.expire_log_message = Журнали очищено, тому що вони були занадто старі. -runs.empty_commit_message = (порожнє повідомлення коміта) +runs.empty_commit_message = (порожнє повідомлення коміту) runners.status.unspecified = Невідомо runs.status_no_select = Усі стани runs.status = Стан @@ -3836,6 +3896,8 @@ issues.read = Читати: дивитись і створювати за wiki.read = Читати: переглядати вбудовану вікі та її історію. actions.write = Писати: вручну запускати, перезапускати, скасовувати або схвалювати конвеєри CI/CD в очікуванні. +projects.write = Писати: створювати проєкти і стовпчики та редагувати їх. + [munits.data] pib = ПіБ eib = ЕіБ diff --git a/options/locale_next/locale_ar.json b/options/locale_next/locale_ar.json index a0fc1de24b..1574d3eb7c 100644 --- a/options/locale_next/locale_ar.json +++ b/options/locale_next/locale_ar.json @@ -5,5 +5,70 @@ "home.explore_users": "اكتشف المستخدمين", "home.explore_orgs": "اكتشف المنظمات", "moderation.abuse_category.malware": "برمجية خبيثة", - "moderation.abuse_category.illegal_content": "المحتوى غير المشروع" + "moderation.abuse_category.illegal_content": "محتوى غير مشروع", + "stars.list.none": "لم يقم أحد بتمييز هذا المستودع بنجمة.", + "watch.list.none": "لا أحد يشاهد هذا المستودع.", + "followers.incoming.list.self.none": "لا أحد يتابع ملفك الشخصي.", + "followers.incoming.list.none": "لا أحد يتابع هذا المستخدم.", + "followers.outgoing.list.self.none": "أنت لا تتبع أي شخص.", + "followers.outgoing.list.none": "لا يتابع %s أي شخص.", + "relativetime.now": "الآن", + "relativetime.future": "في المستقبل", + "relativetime.1day": "الأمس", + "relativetime.2days": "منذ يومين", + "relativetime.1week": "أخر أسبوع", + "relativetime.2weeks": "منذ أسبوعين", + "relativetime.1month": "الشهر الفائت", + "relativetime.2months": "منذ شهرين", + "relativetime.1year": "السنة الفائتة", + "relativetime.2years": "منذ سنتين", + "repo.form.cannot_create": "بلغت جميع المساحات التي يمكنك إنشاء مستودعات بها حدها.", + "repo.issue_indexer.title": "مفهرس الإبلاغات", + "search.milestone_kind": "معالم البحث…", + "incorrect_root_url": "تم تكوين هذه النسخة من Forgejo لتعمل على العنوان \"%s\". أنت تقوم حاليًا بتصفّح Forgejo عبر رابط مختلف، مما قد يتسبب في تعطل بعض أجزاء التطبيق. يتم تحديد الرابط الرسمي (canonical URL) من قِبل مسؤولي Forgejo من خلال إعداد `ROOT_URL` في ملف `app.ini`.", + "themes.names.forgejo-auto": "فورجيو (اتبع سمة النظام)", + "themes.names.forgejo-light": "فورجيجو المضيء", + "themes.names.forgejo-dark": "فورجيجو الداكن", + "error.not_found.title": "الصفحة غير موجودة", + "alert.asset_load_failed": "تعذّر تحميل ملفات الأصول من {path}. تأكد من أن الملفات متاحة للوصول.", + "alert.range_error": " يجب أن يكون رقمًا بين %[1]s و %[2]s.", + "install.invalid_lfs_path": "غير قادر على إنشاء جذر LFS في المسار المحدد: %[1]s", + "profile.actions.tooltip": "إجراءات إضافية", + "profile.edit.link": "عدِّل ملف التعريف", + "feed.atom.link": "موجز Atom", + "keys.ssh.link": "مفاتيح SSH", + "keys.gpg.link": "مفاتيح GPG", + "admin.config.moderation_config": "تهيئة الإشراف", + "moderation.report_abuse": "الإبلاغ عن إساءة الاستخدام", + "moderation.report_content": "محتوى التقارير", + "moderation.report_abuse_form.header": "الإبلاغ عن الإساءة إلى المسؤول", + "moderation.report_abuse_form.details": "يتعين استخدام هذا النموذج للإبلاغ عن المستخدمين الذين ينشئون ملفات تعريف ، أو مستودعات ، أو إبلاغات ، أو تعليقات ، أو يتصرفون بشكل غير لائق.", + "moderation.report_abuse_form.invalid": "معاملا غير صالحة", + "moderation.report_abuse_form.already_reported": "‍لقد قمت بالفعل بالإبلاغ عن هذا المحتوى", + "moderation.abuse_category": "الفئة", + "moderation.abuse_category.placeholder": "حدد الفئة", + "moderation.abuse_category.spam": "غير مرغوب به", + "moderation.abuse_category.other_violations": "انتهاكات أخرى لقواعد المنصة", + "moderation.report_remarks": "الملاحظات", + "moderation.report_remarks.placeholder": "يُرجى تقديم بعض التفاصيل المتعلقة بالإساءة التي أبلغت عنها.", + "moderation.submit_report": "إرسال التقرير", + "moderation.reporting_failed": "تعذر إرسال تقرير إساءة الاستخدام الجديد: %v", + "moderation.reported_thank_you": "شكراُ لك على تقريرك. وقد تم إبلاغ الإدارة به.", + "mail.actions.successful_run_after_failure_subject": "تم استعادة سير العمل %[1]s في المستودع %[2]s", + "mail.actions.not_successful_run_subject": "سير العمل %[1]s فشل في المستودت %[2]s", + "mail.actions.successful_run_after_failure": "تم استعادة سير العمل %[1]s في المستودع %[2]s", + "mail.actions.not_successful_run": "فشل سير العمل %[1]s في المستودع %[2]s", + "mail.actions.run_info_cur_status": "حالة هذا التشغيل: %[1]s (تم تحديثها من %[2]s للتو)", + "mail.actions.run_info_previous_status": "حالة التشغيل السابقة: %[1]s", + "mail.actions.run_info_sha": "إيداع: %[1]s", + "mail.actions.run_info_trigger": "تم تشغيله بسبب: %[1]s عبر: %[2]s", + "repo.diff.commit.next-short": "التالي", + "repo.diff.commit.previous-short": "السابق", + "discussion.locked": "تم إغلاق هذه المناقشة. يقتصر التعليق على المساهمين فقط.", + "editor.textarea.tab_hint": "السطر مُزاح بالفعل. اضغط Tab مرة أخرى أو Escape لمغادرة المحرر.", + "editor.textarea.shift_tab_hint": "لا توجد مسافة بادئة في هذا السطر. اضغط Shift + Tab مرة أخرى أو Escape لمغادرة المحرر.", + "admin.dashboard.cleanup_offline_runners": "تنظيف وحدات التشغيل غير المتصلة", + "settings.visibility.description": "رؤية ملفك الشخصي تؤثر في قدرة الآخرين على الوصول إلى مستودعاتك غير الخاصة. اعرف المزيد", + "avatar.constraints_hint": "لا يمكن أن يتجاوز حجم الصورة الشخصية المخصصة %[1]s، ولا أبعادها عن %[2]d×%[3]d بكسل", + "meta.last_line": "شكرًا لك على ترجمة Forgejo! هذا السطر لا يراه المستخدمون ولكنه يخدم أغراضًا أخرى في إدارة الترجمة. يمكنك وضع حقيقة ممتعة في الترجمة بدلاً من ترجمتها." } diff --git a/options/locale_next/locale_be.json b/options/locale_next/locale_be.json index 40dfec0b41..c8ff00bd87 100644 --- a/options/locale_next/locale_be.json +++ b/options/locale_next/locale_be.json @@ -1,3 +1,8 @@ { - "moderation.abuse_category.malware": "Шкодная праграма" + "moderation.abuse_category.malware": "Шкодная праграма", + "home.welcome.no_activity": "Няма падзей", + "home.welcome.activity_hint": "У вашай стужцы пакуль што нічога няма. Вашыя дзеяньні ды падзеі з рэпазыторыяў зьявяцца тут.", + "home.explore_repos": "Агляд рэпазыторыяў", + "home.explore_users": "Агляд карыстальнікаў", + "home.explore_orgs": "Агляд арганізацый" } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index a582efdc27..c64e25b242 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -91,11 +91,11 @@ "followers.outgoing.list.none": "%s folgt niemanden.", "stars.list.none": "Niemand hat dieses Repo favorisiert.", "followers.incoming.list.none": "Niemand folgt diesem Benutzer.", - "editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals Tab oder Escape um den Editor zu verlassen.", - "editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals Shift + Tab oder Escape um den Editor zu verlassen.", - "admin.dashboard.cleanup_offline_runners": "Aufräumen der offline Runner", + "editor.textarea.tab_hint": "Zeile bereits eingerückt. Drücke nochmals Tab oder Escape, um den Editor zu verlassen.", + "editor.textarea.shift_tab_hint": "Keine Einrückung auf dieser Zeile. Drücke nochmals Shift + Tab oder Escape, um den Editor zu verlassen.", + "admin.dashboard.cleanup_offline_runners": "Aufräumen der Offline-Runner", "settings.visibility.description": "Die Profilsichtbarkeit beeinflusst die Möglichkeit anderer, auf deine nicht-privaten Repositorys zuzugreifen. Erfahre mehr", - "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]dx%[3]d Pixel sein", + "avatar.constraints_hint": "Individuelles Profilbild darf %[1]s in der Größe nicht überschreiten, und nicht größer als %[2]d×%[3]d Pixel sein", "repo.diff.commit.next-short": "Nächste", "repo.diff.commit.previous-short": "Vorherige", "profile.edit.link": "Profil bearbeiten", diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 884a7b44eb..20b35b8edb 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -1,6 +1,6 @@ { "repo.pulls.merged_title_desc": { - "one": "isinali ang %[1]d commit mula%[2]s patungong %[3]s %[4]s", + "one": "isinali ang %[1]d commit mula %[2]s patungong %[3]s %[4]s", "other": "isinali ang %[1]d mga commit mula sa %[2]s patungong %[3]s %[4]s" }, "repo.pulls.title_desc": { @@ -21,7 +21,7 @@ "alert.asset_load_failed": "Nabigong i-load ang mga asset file mula sa {path}. Siguraduhin na maa-access ang mga asset file.", "install.invalid_lfs_path": "Nabigong gawin ang LFS root sa tinakdang path: %[1]s", "alert.range_error": " dapat ay numero sa pagitan ng %[1]s at %[2]s.", - "meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices. What will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings. What good are words when a smile says it all? And if this world won't write me an ending... What will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat What do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.", + "meta.last_line": "Every day, I imagine a future where I can be with you. In my hand is a pen that will write a poem of me and you. The ink flows down into a dark puddle... Just move your hand, write the way into his heart. But in this world of infinite choices, what will it take just to find that special day? Have I found everybody a fun assignment to do today? When you're here, everything that we do is fun for them anyway... When I can't even read my own feelings, what good are words when a smile says it all? And if this world won't write me an ending, what will it take just for me to have it all? Does my pen only write bitter words for those who are dear to me? Is it love if I take you, or is it love if I set you free? The ink flows down into a dark puddle... How can I write love into reality? If I can't hear the sound of your heartbeat, what do you call love in your reality? And in your reality, if I don't know how to love you... I'll leave you be.", "mail.actions.successful_run_after_failure": "Na-recover ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.not_successful_run": "Nabigo ang workflow na %[1]s sa repositoryong %[2]s", "mail.actions.run_info_previous_status": "Nakaraang Status ng Run: %[1]s", @@ -102,5 +102,6 @@ "feed.atom.link": "Atom feed", "keys.ssh.link": "Mga SSH key", "keys.gpg.link": "Mga GPG key", - "profile.actions.tooltip": "Higit pang mga aksyon" + "profile.actions.tooltip": "Higit pang mga aksyon", + "mail.actions.run_info_sha": "Commit: %[1]s" } diff --git a/options/locale_next/locale_pt-PT.json b/options/locale_next/locale_pt-PT.json index 8697bbaec0..5d68db624b 100644 --- a/options/locale_next/locale_pt-PT.json +++ b/options/locale_next/locale_pt-PT.json @@ -84,7 +84,7 @@ "moderation.abuse_category": "Categoria", "moderation.abuse_category.placeholder": "Escolha uma categoria", "moderation.abuse_category.spam": "Spam", - "moderation.abuse_category.malware": "Malware", + "moderation.abuse_category.malware": "Software malicioso", "moderation.abuse_category.illegal_content": "Conteúdo ilegal", "moderation.abuse_category.other_violations": "Outras violações das regras da plataforma", "moderation.report_remarks": "Observações", @@ -110,5 +110,6 @@ "keys.ssh.link": "Chaves SSH", "keys.gpg.link": "Chaves GPG", "repo.diff.commit.next-short": "Seg.", - "repo.diff.commit.previous-short": "Ant." + "repo.diff.commit.previous-short": "Ant.", + "mail.actions.run_info_sha": "Cometimento: %[1]s" } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index 2b743ad8a5..e49eb79ca2 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -23,7 +23,7 @@ "alert.asset_load_failed": "Не удалось получить ресурсы из {path}. Убедитесь, что файлы ресурсов доступны.", "install.invalid_lfs_path": "Не удалось расположить корень LFS по указанному пути: %[1]s", "alert.range_error": " - число должно быть в диапазоне от %[1]s-%[2]s.", - "meta.last_line": "Unskip..", + "meta.last_line": "...ъъ", "mail.actions.not_successful_run_subject": "Провал раб. потока %[1]s в репозитории %[2]s", "mail.actions.successful_run_after_failure_subject": "Возобновление раб. потока %[1]s в репозитории %[2]s", "mail.actions.run_info_ref": "Ветвь: %[1]s (%[2]s)", diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 9fc38dae15..743cced8c1 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -78,5 +78,6 @@ "repo.diff.commit.next-short": "下一个", "repo.diff.commit.previous-short": "上一个", "feed.atom.link": "Atom 订阅源", - "profile.edit.link": "编辑个人资料" + "profile.edit.link": "编辑个人资料", + "mail.actions.run_info_sha": "提交:%[1]s" } From c2cd3fb19b0944d4c49017ab20b1b6aa543c22e4 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 17 Jul 2025 21:21:43 +0200 Subject: [PATCH 20/80] [v12.0/forgejo] fix: use correct ACME default (#8552) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8550 - The `ACME_URL` setting is documented to default to Let's encrypt production server if left empty, so do precisely that. - Use a HTTP proxy to communicate with ACME if Forgejo is configured to use that. - Regression of forgejo/forgejo#7409 (previously certmagic took care of setting these defaults). - Resolves forgejo/forgejo#8548 ## Testing 1. Configure Forgejo's root URL to a public facing domain (that can pass a ACME challenge) 2. Configure Forgejo to use ACME by setting `[server].ENABLE_ACME = true` and `[server].ACME_ACCEPTTOS = true`. 3. Start Forgejo. 4. Observe that it's available via https. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8552 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- cmd/web_acme.go | 7 +++++++ modules/setting/server.go | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd/web_acme.go b/cmd/web_acme.go index 03b3b9f0da..be6314addb 100644 --- a/cmd/web_acme.go +++ b/cmd/web_acme.go @@ -15,6 +15,7 @@ import ( "forgejo.org/modules/graceful" "forgejo.org/modules/log" "forgejo.org/modules/process" + "forgejo.org/modules/proxy" "forgejo.org/modules/setting" "github.com/caddyserver/certmagic" @@ -76,6 +77,12 @@ func runACME(listenAddr string, m http.Handler) error { ListenHost: setting.HTTPAddr, AltTLSALPNPort: altTLSALPNPort, AltHTTPPort: altHTTPPort, + HTTPProxy: proxy.Proxy(), + } + + // Preserve behavior to use Let's encrypt test CA when Let's encrypt is CA. + if certmagic.DefaultACME.CA == certmagic.LetsEncryptProductionCA { + certmagic.DefaultACME.TestCA = certmagic.LetsEncryptStagingCA } magic := certmagic.NewDefault() diff --git a/modules/setting/server.go b/modules/setting/server.go index bff51f787d..3ff91d2cde 100644 --- a/modules/setting/server.go +++ b/modules/setting/server.go @@ -16,6 +16,8 @@ import ( "forgejo.org/modules/json" "forgejo.org/modules/log" "forgejo.org/modules/util" + + "github.com/caddyserver/certmagic" ) // Scheme describes protocol types @@ -206,7 +208,7 @@ func loadServerFrom(rootCfg ConfigProvider) { EnableAcme = sec.Key("ENABLE_LETSENCRYPT").MustBool(false) } if EnableAcme { - AcmeURL = sec.Key("ACME_URL").MustString("") + AcmeURL = sec.Key("ACME_URL").MustString(certmagic.LetsEncryptProductionCA) AcmeCARoot = sec.Key("ACME_CA_ROOT").MustString("") if sec.HasKey("ACME_ACCEPTTOS") { From 1d7c366588ec507810e303fa3cf9db7e3269dd73 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Fri, 18 Jul 2025 14:01:38 +0200 Subject: [PATCH 21/80] [v12.0/forgejo] fix(ui): prevent render failure on faulty org settings post (#8555) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8553 Fix regression of https://codeberg.org/forgejo/forgejo/pulls/7998 Same as https://codeberg.org/forgejo/forgejo/pulls/8236 but for orgs Amended existing tests to verify which error messages show up and not show up. Co-authored-by: 0ko <0ko@noreply.codeberg.org> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8555 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/org/setting.go | 3 +++ tests/integration/org_settings_test.go | 14 +++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index c83242754b..9b4e01597b 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -71,6 +71,9 @@ func SettingsPost(ctx *context.Context) { ctx.Data["PageIsSettingsOptions"] = true ctx.Data["CurrentVisibility"] = ctx.Org.Organization.Visibility ctx.Data["CooldownPeriod"] = setting.Service.UsernameCooldownPeriod + ctx.Data["MaxAvatarFileSize"] = setting.Avatar.MaxFileSize + ctx.Data["MaxAvatarWidth"] = setting.Avatar.MaxWidth + ctx.Data["MaxAvatarHeight"] = setting.Avatar.MaxHeight if ctx.HasError() { ctx.HTML(http.StatusOK, tplSettingsOptions) diff --git a/tests/integration/org_settings_test.go b/tests/integration/org_settings_test.go index fde57e2e26..3ea6f10bf6 100644 --- a/tests/integration/org_settings_test.go +++ b/tests/integration/org_settings_test.go @@ -57,7 +57,9 @@ func TestOrgSettingsChangeEmail(t *testing.T) { settings := getOrgSettingsFormData(t, session, orgName) settings["email"] = "invalid" - session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusOK) + doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusOK).Body) + doc.AssertElement(t, ".status-page-500", false) + doc.AssertElement(t, ".flash-error", true) org := getOrgSettings(t, token, orgName) assert.Equal(t, "org3@example.com", org.Email) @@ -69,7 +71,10 @@ func TestOrgSettingsChangeEmail(t *testing.T) { settings := getOrgSettingsFormData(t, session, orgName) settings["email"] = "example@example.com" - session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther) + doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther).Body) + doc.AssertElement(t, "body", true) + doc.AssertElement(t, ".status-page-500", false) + doc.AssertElement(t, ".flash-error", false) org := getOrgSettings(t, token, orgName) assert.Equal(t, "example@example.com", org.Email) @@ -81,7 +86,10 @@ func TestOrgSettingsChangeEmail(t *testing.T) { settings := getOrgSettingsFormData(t, session, orgName) settings["email"] = "" - session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther) + doc := NewHTMLParser(t, session.MakeRequest(t, NewRequestWithValues(t, "POST", settingsURL, settings), http.StatusSeeOther).Body) + doc.AssertElement(t, "body", true) + doc.AssertElement(t, ".status-page-500", false) + doc.AssertElement(t, ".flash-error", false) org := getOrgSettings(t, token, orgName) assert.Empty(t, org.Email) From 5095cafe49c4e4b93a0ab7f84873c3734338a8a4 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 19 Jul 2025 16:33:30 +0200 Subject: [PATCH 22/80] [v12.0/forgejo] fix: correct image source for quoted reply (#8574) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8565 - Since v10 replies are generated on the fly to handle quoted reply (forgejo/forgejo#5677), this means that we have to do some work to construct markdown that is equivalent to the HTML of the comment. - Images are slightly strange in the context of issues and pull requests, as Forgejo will render them in the context of the repository and as such links such as `/attachments` become `/user/repo/attachments`, the quoted reply did not take into account and would use `/user/repo/attachments` as link which means it gets transformed to `/user/repo//user/repo/attachments`. - Instead of fixing this on the backend (and maybe break some existing links), teach the quoted reply about this context and remove it from the image source before generating the markdown. Reported-by: mrwusel (via Matrix) Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8574 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- .../repo/issue/view_content/context_menu.tmpl | 2 +- tests/e2e/fixtures/comment.yml | 4 +- tests/e2e/issue-comment.test.e2e.ts | 6 ++- web_src/js/features/repo-legacy.js | 53 +++++++++++-------- 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/templates/repo/issue/view_content/context_menu.tmpl b/templates/repo/issue/view_content/context_menu.tmpl index 6f43f768b3..8cd4a056f0 100644 --- a/templates/repo/issue/view_content/context_menu.tmpl +++ b/templates/repo/issue/view_content/context_menu.tmpl @@ -11,7 +11,7 @@ {{end}}
{{ctx.Locale.Tr "repo.issues.context.copy_link"}}
{{if and .ctxData.IsSigned (not .ctxData.Repository.IsArchived)}} -
{{ctx.Locale.Tr "repo.issues.context.quote_reply"}}
+
{{ctx.Locale.Tr "repo.issues.context.quote_reply"}}
{{if not .ctxData.UnitIssuesGlobalDisabled}}
{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}
{{end}} diff --git a/tests/e2e/fixtures/comment.yml b/tests/e2e/fixtures/comment.yml index f3f8a2d8e8..ae7c850036 100644 --- a/tests/e2e/fixtures/comment.yml +++ b/tests/e2e/fixtures/comment.yml @@ -3,7 +3,7 @@ type: 0 # comment poster_id: 2 issue_id: 1 # in repo_id 1 - content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:" + content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:\n![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n\nsomething something" created_unix: 946684811 updated_unix: 946684811 content_version: 1 @@ -13,7 +13,7 @@ type: 21 # code comment poster_id: 2 issue_id: 19 - content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:" + content: "## Lorem Ipsum\nI would like to say that **I am not appealed** that it took _so long_ for this `feature` to be [created](https://example.com) $e^{\\pi i} + 1 = 0$\n$$e^{\\pi i} + 1 = 0$$\n#1\n```js\nconsole.log('evil')\nalert('evil')\n```\n:+1: :100:\n![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n\nsomething something" review_id: 1001 line: 1 tree_path: "test1.txt" diff --git a/tests/e2e/issue-comment.test.e2e.ts b/tests/e2e/issue-comment.test.e2e.ts index d2dbafafd0..2017e4563e 100644 --- a/tests/e2e/issue-comment.test.e2e.ts +++ b/tests/e2e/issue-comment.test.e2e.ts @@ -123,7 +123,8 @@ test('Quote reply', async ({page}, workerInfo) => { "> alert('evil')\n" + '> ```\n' + '> \n' + - '> :+1: :100:\n\n'); + '> :+1: :100: [![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)](/user2/repo1/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n' + + '> something something\n\n'); await editorTextarea.fill(''); @@ -197,7 +198,8 @@ test('Pull quote reply', async ({page}, workerInfo) => { "> alert('evil')\n" + '> ```\n' + '> \n' + - '> :+1: :100:\n\n'); + '> :+1: :100: [![hi there](/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)](/user2/commitsonpr/attachments/3f4f4016-877b-46b3-b79f-ad24519a9cf2)\n' + + '> something something\n\n'); await editorTextarea.fill(''); }); diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index e6af4cbf04..66ee945d02 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -531,6 +531,13 @@ const filters = { } return el; }, + IMG(el, context) { + const src = el.getAttribute('src'); + if (src?.startsWith(context)) { + el.src = src.slice(context.length); + } + return el; + }, }; function hasContent(node) { @@ -538,32 +545,34 @@ function hasContent(node) { } // This code matches that of what is done by @github/quote-selection -function preprocessFragment(fragment) { - const nodeIterator = document.createNodeIterator(fragment, NodeFilter.SHOW_ELEMENT, { - acceptNode(node) { - if (node.nodeName in filters && hasContent(node)) { - return NodeFilter.FILTER_ACCEPT; +function preprocessFragment(context) { + return function(fragment) { + const nodeIterator = document.createNodeIterator(fragment, NodeFilter.SHOW_ELEMENT, { + acceptNode(node) { + if (node.nodeName in filters && hasContent(node)) { + return NodeFilter.FILTER_ACCEPT; + } + + return NodeFilter.FILTER_SKIP; + }, + }); + const results = []; + let node = nodeIterator.nextNode(); + + while (node) { + if (node instanceof HTMLElement) { + results.push(node); } - - return NodeFilter.FILTER_SKIP; - }, - }); - const results = []; - let node = nodeIterator.nextNode(); - - while (node) { - if (node instanceof HTMLElement) { - results.push(node); + node = nodeIterator.nextNode(); } - node = nodeIterator.nextNode(); - } // process deepest matches first - results.reverse(); + results.reverse(); - for (const el of results) { - el.replaceWith(filters[el.nodeName](el)); - } + for (const el of results) { + el.replaceWith(filters[el.nodeName](el, context)); + } + }; } function initRepoIssueCommentEdit() { @@ -573,7 +582,7 @@ function initRepoIssueCommentEdit() { // Quote reply $(document).on('click', '.quote-reply', async (event) => { event.preventDefault(); - const quote = new MarkdownQuote('', preprocessFragment); + const quote = new MarkdownQuote('', preprocessFragment(event.target.getAttribute('data-context'))); let editorTextArea; if (event.target.classList.contains('quote-reply-diff')) { From 816a63ef284dea9c5f89d49e89df018828a918f3 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 19 Jul 2025 20:45:36 +0200 Subject: [PATCH 23/80] [v12.0/forgejo] fix: allow for tracked time to be removed again (#8576) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8575 - `getElementById` requires a id to be passed and not a query selector, change it to `querySelector`. - Regression of forgejo/forgejo#7408 - Resolves forgejo/forgejo#8571 - Add E2E tests for adding manual tracked time and removing it. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8576 Reviewed-by: Beowulf Reviewed-by: Otto Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- tests/e2e/issue-timetracking.test.e2e.ts | 48 ++++++++++++++++++++++++ web_src/js/features/repo-issue.js | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/issue-timetracking.test.e2e.ts diff --git a/tests/e2e/issue-timetracking.test.e2e.ts b/tests/e2e/issue-timetracking.test.e2e.ts new file mode 100644 index 0000000000..901cbe793f --- /dev/null +++ b/tests/e2e/issue-timetracking.test.e2e.ts @@ -0,0 +1,48 @@ +// @watch start +// web_src/js/features/comp/** +// web_src/js/features/repo-** +// templates/repo/issue/view_content/* +// @watch end + +import {expect} from '@playwright/test'; +import {test, save_visual} from './utils_e2e.ts'; + +test.use({user: 'user2'}); + +test('Issue timetracking', async ({page}) => { + await page.goto('/user2/repo1/issues/new'); + + // Create temporary issue. + await page.getByPlaceholder('Title').fill('Just a title'); + await page.getByPlaceholder('Leave a comment').fill('Hi, have you considered using a rotating fish as logo?'); + await page.getByRole('button', {name: 'Create issue'}).click(); + await expect(page).toHaveURL(/\/user2\/repo1\/issues\/\d+$/); + + // Manually add time to the time tracker. + await page.getByRole('button', {name: 'Add time'}).click(); + await page.getByPlaceholder('Hours').fill('5'); + await page.getByPlaceholder('Minutes').fill('32'); + await page.getByRole('button', {name: 'Add time', exact: true}).click(); + + // Verify this was added in the timeline. + await expect(page.locator('.ui.timeline')).toContainText('added spent time'); + await expect(page.locator('.ui.timeline')).toContainText('5 hours 32 minutes'); + + // Verify it is shown in the issue sidebar + await expect(page.locator('.issue-content-right .comments')).toContainText('Total time spent: 5 hours 32 minutes'); + + await save_visual(page); + + // Delete the added time. + await page.getByRole('button', {name: 'Delete this time log'}).click(); + await page.getByRole('button', {name: 'Yes'}).click(); + + // Verify this was removed in the timeline. + await expect(page.locator('.ui.timeline')).toContainText('deleted spent time'); + await expect(page.locator('.ui.timeline')).toContainText('- 5 hours 32 minutes'); + + // Delete the issue. + await page.getByRole('button', {name: 'Delete'}).click(); + await page.getByRole('button', {name: 'Yes'}).click(); + await expect(page).toHaveURL('/user2/repo1/issues'); +}); diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index c8d7032048..d3f81c3a86 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -57,7 +57,7 @@ export function initRepoIssueTimeTracking() { $(sel).modal({ duration: 200, onApprove() { - document.getElementById(`${sel} form`).requestSubmit(); + document.querySelector(`${sel} form`).requestSubmit(); }, }).modal('show'); }); From 9d47719545add914870637de0a093c7fbe4c5411 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Tue, 22 Jul 2025 03:08:12 +0200 Subject: [PATCH 24/80] [v12.0/forgejo] fix: make sure to use unaltered fields when saving a shadow copy for updated profiles or comments (#8584) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8533 Follow-up of !6977 ### Manual testing - User **S** creates an organization **O** and posts a comment **C** (on a random issue); - User **R** report as abuse the comment **C**, the organization **O** as well as the user **S**; - User **S** changes the content of comment **C** and the description of organization **O** as well as the description of their own profile; - Check (within DB) that shadow copies are being created (and linked to corresponding abuse reports) for comment **C**, organization **O** and user **S** and the content is the one from the moment when the reports were submitted (therefore before the updates made by **S**). Co-authored-by: floss4good Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8584 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- .../ModerationFeatures/abuse_report.yml | 21 +++++++ .../fixtures/ModerationFeatures/comment.yml | 7 +++ models/fixtures/ModerationFeatures/user.yml | 22 ++++++++ models/issues/comment.go | 4 +- models/issues/moderation.go | 8 ++- models/user/moderation.go | 19 +++++-- models/user/user.go | 4 +- services/issue/comments_test.go | 39 +++++++++++++ services/user/delete.go | 2 +- services/user/email.go | 2 +- services/user/user_test.go | 56 +++++++++++++++++++ 11 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 models/fixtures/ModerationFeatures/abuse_report.yml create mode 100644 models/fixtures/ModerationFeatures/comment.yml create mode 100644 models/fixtures/ModerationFeatures/user.yml diff --git a/models/fixtures/ModerationFeatures/abuse_report.yml b/models/fixtures/ModerationFeatures/abuse_report.yml new file mode 100644 index 0000000000..f2e371ee35 --- /dev/null +++ b/models/fixtures/ModerationFeatures/abuse_report.yml @@ -0,0 +1,21 @@ +- + id: 1 + status: 1 + reporter_id: 2 # @user2 + content_type: 4 # Comment + content_id: 18 # user2/repo2/issues/2#issuecomment-18 + category: 2 # Spam + remarks: The comment I'm reporting is pure SPAM. + shadow_copy_id: null + created_unix: 1752697980 # 2025-07-16 20:33:00 + +- + id: 2 + status: 1 # Open + reporter_id: 2 # @user2 + content_type: 1 # User (users or organizations) + content_id: 1002 # @alexsmith + category: 2 # Spam + remarks: This user just posted a spammy comment on my issue. + shadow_copy_id: null + created_unix: 1752698010 # 2025-07-16 20:33:30 diff --git a/models/fixtures/ModerationFeatures/comment.yml b/models/fixtures/ModerationFeatures/comment.yml new file mode 100644 index 0000000000..a4d41ad997 --- /dev/null +++ b/models/fixtures/ModerationFeatures/comment.yml @@ -0,0 +1,7 @@ +- # This is a spam comment (abusive content), created for testing moderation functionalities. + id: 18 + type: 0 # Standard comment + poster_id: 1002 # @alexsmith + issue_id: 7 # user2/repo2#2 + content: If anyone needs help for promoting their business online using SEO, just contact me (check my profile page). + created_unix: 1752697860 # 2025-07-16 20:31:00 diff --git a/models/fixtures/ModerationFeatures/user.yml b/models/fixtures/ModerationFeatures/user.yml new file mode 100644 index 0000000000..662c61a3e9 --- /dev/null +++ b/models/fixtures/ModerationFeatures/user.yml @@ -0,0 +1,22 @@ +- # This user is a spammer and will create abusive content (for testing moderation functionalities). + id: 1002 + lower_name: alexsmith + name: alexsmith + full_name: Alex Smith + email: alexsmith@example.org + keep_email_private: false + passwd: passwdSalt:password + passwd_hash_algo: dummy + type: 0 + location: '@master@seo.net' + website: http://promote-your-business.biz + pronouns: SEO + salt: passwdSalt + description: I can help you promote your business online using SEO. + created_unix: 1752697800 # 2025-07-16 20:30:00 + is_active: true + is_admin: false + is_restricted: false + avatar: avatar-hash-1002 + avatar_email: alexsmith@example.org + use_custom_avatar: false diff --git a/models/issues/comment.go b/models/issues/comment.go index a81221caf4..523a6ba9b9 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -1156,7 +1156,7 @@ func UpdateComment(ctx context.Context, c *Comment, contentVersion int, doer *us defer committer.Close() // If the comment was reported as abusive, a shadow copy should be created before first update. - if err := IfNeededCreateShadowCopyForComment(ctx, c); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, c, true); err != nil { return err } @@ -1197,7 +1197,7 @@ func DeleteComment(ctx context.Context, comment *Comment) error { e := db.GetEngine(ctx) // If the comment was reported as abusive, a shadow copy should be created before deletion. - if err := IfNeededCreateShadowCopyForComment(ctx, comment); err != nil { + if err := IfNeededCreateShadowCopyForComment(ctx, comment, false); err != nil { return err } diff --git a/models/issues/moderation.go b/models/issues/moderation.go index 635d295db0..921f770d4d 100644 --- a/models/issues/moderation.go +++ b/models/issues/moderation.go @@ -87,13 +87,19 @@ func IfNeededCreateShadowCopyForIssue(ctx context.Context, issue *Issue) error { // IfNeededCreateShadowCopyForComment checks if for the given comment there are any reports of abusive content submitted // and if found a shadow copy of relevant comment fields will be stored into DB and linked to the above report(s). // This function should be called before a comment is deleted or updated. -func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment) error { +func IfNeededCreateShadowCopyForComment(ctx context.Context, comment *Comment, forUpdates bool) error { shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeComment, comment.ID) if err != nil { return err } if shadowCopyNeeded { + if forUpdates { + // get the unaltered comment fields (for updates the provided variable is already altered but not yet saved) + if comment, err = GetCommentByID(ctx, comment.ID); err != nil { + return err + } + } commentData := newCommentData(comment) content, err := json.Marshal(commentData) if err != nil { diff --git a/models/user/moderation.go b/models/user/moderation.go index afda497f02..f9c16a17b3 100644 --- a/models/user/moderation.go +++ b/models/user/moderation.go @@ -73,16 +73,20 @@ var userDataColumnNames = sync.OnceValue(func() []string { // and if found a shadow copy of relevant user fields will be stored into DB and linked to the above report(s). // This function should be called before a user is deleted or updated. // +// In case the User object was already altered before calling this method, just provide the userID and +// nil for unalteredUser; when it is decided that a shadow copy should be created and unalteredUser is nil, +// the user will be retrieved from DB based on the provided userID. +// // For deletions alteredCols argument must be omitted. // // In case of updates it will first checks whether any of the columns being updated (alteredCols argument) // is relevant for moderation purposes (i.e. included in the UserData struct). -func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCols ...string) error { +func IfNeededCreateShadowCopyForUser(ctx context.Context, userID int64, unalteredUser *User, alteredCols ...string) error { // TODO: this can be triggered quite often (e.g. by routers/web/repo/middlewares.go SetDiffViewStyle()) shouldCheckIfNeeded := len(alteredCols) == 0 // no columns being updated, therefore a deletion if !shouldCheckIfNeeded { - // for updates we need to go further only if certain column are being changed + // for updates we need to go further only if certain columns are being changed for _, colName := range userDataColumnNames() { if shouldCheckIfNeeded = slices.Contains(alteredCols, colName); shouldCheckIfNeeded { break @@ -94,18 +98,23 @@ func IfNeededCreateShadowCopyForUser(ctx context.Context, user *User, alteredCol return nil } - shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, user.ID) + shadowCopyNeeded, err := moderation.IsShadowCopyNeeded(ctx, moderation.ReportedContentTypeUser, userID) if err != nil { return err } if shadowCopyNeeded { - userData := newUserData(user) + if unalteredUser == nil { + if unalteredUser, err = GetUserByID(ctx, userID); err != nil { + return err + } + } + userData := newUserData(unalteredUser) content, err := json.Marshal(userData) if err != nil { return err } - return moderation.CreateShadowCopyForUser(ctx, user.ID, string(content)) + return moderation.CreateShadowCopyForUser(ctx, userID, string(content)) } return nil diff --git a/models/user/user.go b/models/user/user.go index b124572bb6..6b54776adf 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -927,7 +927,9 @@ func UpdateUserCols(ctx context.Context, u *User, cols ...string) error { // If the user was reported as abusive and any of the columns being updated is relevant // for moderation purposes a shadow copy should be created before first update. - if err := IfNeededCreateShadowCopyForUser(ctx, u, cols...); err != nil { + // Since u is already altered at this point we are sending nil instead as an argument + // so that the unaltered version will be retrieved from DB. + if err := IfNeededCreateShadowCopyForUser(ctx, u.ID, nil, cols...); err != nil { return err } diff --git a/services/issue/comments_test.go b/services/issue/comments_test.go index 8fa410c0f0..fcf06d9ec8 100644 --- a/services/issue/comments_test.go +++ b/services/issue/comments_test.go @@ -8,9 +8,11 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" + "forgejo.org/models/moderation" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" webhook_model "forgejo.org/models/webhook" + "forgejo.org/modules/json" "forgejo.org/modules/setting" "forgejo.org/modules/test" issue_service "forgejo.org/services/issue" @@ -148,3 +150,40 @@ func TestUpdateComment(t *testing.T) { unittest.AssertNotExistsBean(t, &issues_model.ContentHistory{CommentID: comment.ID}) }) } + +func TestCreateShadowCopyOnCommentUpdate(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")() + require.NoError(t, unittest.PrepareTestDatabase()) + + userAlexSmithID := int64(1002) + spamCommentID := int64(18) // posted by @alexsmith + abuseReportID := int64(1) // submitted for above comment + newCommentContent := "If anyone needs help, just contact me." + + // Retrieve the abusive user (@alexsmith), their SPAM comment and the abuse report already created for this comment. + poster := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID}) + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: spamCommentID, PosterID: poster.ID}) + report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ + ID: abuseReportID, + ContentType: moderation.ReportedContentTypeComment, + ContentID: comment.ID, + }) + // The report should not already have a shadow copy linked. + assert.False(t, report.ShadowCopyID.Valid) + + // The abusive user is updating their comment. + oldContent := comment.Content + comment.Content = newCommentContent + require.NoError(t, issue_service.UpdateComment(t.Context(), comment, 0, poster, oldContent)) + + // Reload the report. + report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID}) + // A shadow copy should have been created and linked to our report. + assert.True(t, report.ShadowCopyID.Valid) + // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values. + shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64}) + shadowCopyCommentData := new(issues_model.CommentData) + require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyCommentData)) + // Check to see if the initial content of the comment was stored within the shadow copy. + assert.Equal(t, oldContent, shadowCopyCommentData.Content) +} diff --git a/services/user/delete.go b/services/user/delete.go index 9caa24c373..bed7abde07 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -218,7 +218,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) // ***** END: ExternalLoginUser ***** // If the user was reported as abusive, a shadow copy should be created before deletion. - if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u); err != nil { + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u); err != nil { return err } diff --git a/services/user/email.go b/services/user/email.go index 7a01fa77b3..36a1145aec 100644 --- a/services/user/email.go +++ b/services/user/email.go @@ -205,7 +205,7 @@ func MakeEmailAddressPrimary(ctx context.Context, u *user_model.User, newPrimary oldPrimaryEmail := u.Email // If the user was reported as abusive, a shadow copy should be created before first update (of certain columns). - if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u, "email"); err != nil { + if err = user_model.IfNeededCreateShadowCopyForUser(ctx, u.ID, u, "email"); err != nil { return err } diff --git a/services/user/user_test.go b/services/user/user_test.go index 4678d3bc9a..36f2776ad8 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -15,10 +15,13 @@ import ( asymkey_model "forgejo.org/models/asymkey" "forgejo.org/models/auth" "forgejo.org/models/db" + "forgejo.org/models/moderation" "forgejo.org/models/organization" repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" + "forgejo.org/modules/json" + "forgejo.org/modules/optional" "forgejo.org/modules/setting" "forgejo.org/modules/test" "forgejo.org/modules/timeutil" @@ -277,3 +280,56 @@ func TestDeleteInactiveUsers(t *testing.T) { unittest.AssertExistsIf(t, true, newUser) unittest.AssertExistsIf(t, true, newEmail) } + +func TestCreateShadowCopyOnUserUpdate(t *testing.T) { + defer unittest.OverrideFixtures("models/fixtures/ModerationFeatures")() + require.NoError(t, unittest.PrepareTestDatabase()) + + userAlexSmithID := int64(1002) + abuseReportID := int64(2) // submitted for @alexsmith + newDummyValue := "[REDACTED]" // used for updating profile text fields + + // Retrieve the abusive user (@alexsmith) and the abuse report already created for this user. + abuser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userAlexSmithID}) + report := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ + ID: abuseReportID, + ContentType: moderation.ReportedContentTypeUser, + ContentID: abuser.ID, + }) + // The report should not already have a shadow copy linked. + assert.False(t, report.ShadowCopyID.Valid) + + // Keep a copy of old field values before updating them. + oldUserData := user_model.UserData{ + FullName: abuser.FullName, + Location: abuser.Location, + Website: abuser.Website, + Pronouns: abuser.Pronouns, + Description: abuser.Description, + } + + // The abusive user is updating their profile. + opts := &UpdateOptions{ + FullName: optional.Some(newDummyValue), + Location: optional.Some(newDummyValue), + Website: optional.Some(newDummyValue), + Pronouns: optional.Some(newDummyValue), + Description: optional.Some(newDummyValue), + } + require.NoError(t, UpdateUser(t.Context(), abuser, opts)) + + // Reload the report. + report = unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReport{ID: report.ID}) + // A shadow copy should have been created and linked to our report. + assert.True(t, report.ShadowCopyID.Valid) + // Retrieve the newly created shadow copy and unmarshal the stored JSON so that we can check the values. + shadowCopy := unittest.AssertExistsAndLoadBean(t, &moderation.AbuseReportShadowCopy{ID: report.ShadowCopyID.Int64}) + shadowCopyUserData := new(user_model.UserData) + require.NoError(t, json.Unmarshal([]byte(shadowCopy.RawValue), &shadowCopyUserData)) + // Check to see if the initial field values of the user were stored within the shadow copy. + assert.Equal(t, oldUserData.FullName, shadowCopyUserData.FullName) + assert.Equal(t, oldUserData.Location, shadowCopyUserData.Location) + assert.Equal(t, oldUserData.Website, shadowCopyUserData.Website) + assert.Equal(t, oldUserData.Pronouns, shadowCopyUserData.Pronouns) + assert.Equal(t, oldUserData.Description, shadowCopyUserData.Description) +} From 4819d4a29ac53ea19edef14dc2a495bc15010a0d Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Tue, 22 Jul 2025 18:41:03 +0200 Subject: [PATCH 25/80] [v12.0/forgejo] fix: follow symlinks for local assets (#8610) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8596 - This reverts behavior that was partially unintentionally introduced in forgejo/forgejo#8143, symbolic links were no longer followed (if they escaped the asset folder) for local assets. - Having symbolic links for user-added files is, to my understanding, a ,common usecase for NixOS and would thus have symbolic links in the asset folders. Avoiding symbolic links is not easy. - The previous code used `http.Dir`, we cannot use that as it's not of the same type. The equivalent is `os.DirFS`. - Unit test to prevent this regression from happening again. Reported-by: bloxx12 (Matrix). Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8610 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/assetfs/layered.go | 11 ++--------- modules/assetfs/layered_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index 48c6728f43..2041f28bb1 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -56,14 +56,7 @@ func Local(name, base string, sub ...string) *Layer { panic(fmt.Sprintf("Unable to get absolute path for %q: %v", base, err)) } root := util.FilePathJoinAbs(base, sub...) - fsRoot, err := os.OpenRoot(root) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - return nil - } - panic(fmt.Sprintf("Unable to open layer %q", err)) - } - return &Layer{name: name, fs: fsRoot.FS(), localPath: root} + return &Layer{name: name, fs: os.DirFS(root), localPath: root} } // Bindata returns a new Layer with the given name, it serves files from the given bindata asset. @@ -80,7 +73,7 @@ type LayeredFS struct { // Layered returns a new LayeredFS with the given layers. The first layer is the top layer. func Layered(layers ...*Layer) *LayeredFS { - return &LayeredFS{layers: slices.DeleteFunc(layers, func(layer *Layer) bool { return layer == nil })} + return &LayeredFS{layers: layers} } // Open opens the named file. The caller is responsible for closing the file. diff --git a/modules/assetfs/layered_test.go b/modules/assetfs/layered_test.go index 87d1f92b00..76eeb61d83 100644 --- a/modules/assetfs/layered_test.go +++ b/modules/assetfs/layered_test.go @@ -1,4 +1,5 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package assetfs @@ -108,3 +109,30 @@ func TestLayered(t *testing.T) { assert.Equal(t, "l1", assets.GetFileLayerName("f1")) assert.Equal(t, "l2", assets.GetFileLayerName("f2")) } + +// Allow layers to read symlink outside the layer root. +func TestLayeredSymlink(t *testing.T) { + dir := t.TempDir() + dirl1 := filepath.Join(dir, "l1") + require.NoError(t, os.MkdirAll(dirl1, 0o755)) + + // Open layer in dir/l1 + layer := Local("l1", dirl1) + + // Create a file in dir/outside + fileContents := []byte("I am outside the layer") + require.NoError(t, os.WriteFile(filepath.Join(dir, "outside"), fileContents, 0o600)) + // Symlink dir/l1/outside to dir/outside + require.NoError(t, os.Symlink(filepath.Join(dir, "outside"), filepath.Join(dirl1, "outside"))) + + // Open dir/l1/outside. + f, err := layer.Open("outside") + require.NoError(t, err) + defer f.Close() + + // Confirm it contains the output of dir/outside + contents, err := io.ReadAll(f) + require.NoError(t, err) + + assert.Equal(t, fileContents, contents) +} From 5dc9f86f09f133c3f8fa145eae5a777d218016e5 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Tue, 22 Jul 2025 19:24:22 +0200 Subject: [PATCH 26/80] [v12.0/forgejo] fix: upgrade fails or hang at migration[32]: Migrate maven package name concatenation (#8613) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8609 - Some SQL queries were not being run in the transaction of v32, which could lead to the migration failing or hanging indefinitely. - Use `db.WithTx` to get a `context.Context` that will make sure to run SQL queries in the transaction. - Using `db.DefaultContext` is fine to be used as parent context for starting the transaction, in all cases of starting the migration `x` and `db.DefaultContext` will point to the same engine. - Resolves forgejo/forgejo#8580 ## Testing 1. Have a v11 Forgejo database with a maven package. 2. Run this migration. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8613 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/forgejo_migrations/v32.go | 87 +++++++++++++++----------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/models/forgejo_migrations/v32.go b/models/forgejo_migrations/v32.go index bed335ab6b..2460003597 100644 --- a/models/forgejo_migrations/v32.go +++ b/models/forgejo_migrations/v32.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "forgejo.org/models/db" "forgejo.org/models/packages" "forgejo.org/modules/json" "forgejo.org/modules/log" @@ -52,55 +53,50 @@ type mavenPackageResult struct { // ChangeMavenArtifactConcatenation resolves old dash-concatenated Maven coordinates and regenerates metadata. // Note: runs per-owner in a single transaction; failures roll back all owners. func ChangeMavenArtifactConcatenation(x *xorm.Engine) error { - sess := x.NewSession() - defer sess.Close() - - if err := sess.Begin(); err != nil { - return err - } - - // get unique owner IDs of Maven packages - var ownerIDs []*int64 - if err := sess. - Table("package"). - Select("package.owner_id"). - Where("package.type = 'maven'"). - GroupBy("package.owner_id"). - OrderBy("package.owner_id DESC"). - Find(&ownerIDs); err != nil { - return err - } - - for _, id := range ownerIDs { - if err := fixMavenArtifactPerOwner(sess, id); err != nil { - log.Error("owner %d migration failed: %v", id, err) - return err // rollback all + return db.WithTx(db.DefaultContext, func(ctx context.Context) error { + // get unique owner IDs of Maven packages + var ownerIDs []*int64 + if err := db.GetEngine(ctx). + Table("package"). + Select("package.owner_id"). + Where("package.type = 'maven'"). + GroupBy("package.owner_id"). + OrderBy("package.owner_id DESC"). + Find(&ownerIDs); err != nil { + return err } - } - return sess.Commit() + for _, id := range ownerIDs { + if err := fixMavenArtifactPerOwner(ctx, id); err != nil { + log.Error("owner %d migration failed: %v", id, err) + return err // rollback all + } + } + + return nil + }) } -func fixMavenArtifactPerOwner(sess *xorm.Session, ownerID *int64) error { - results, err := getMavenPackageResultsToUpdate(sess, ownerID) +func fixMavenArtifactPerOwner(ctx context.Context, ownerID *int64) error { + results, err := getMavenPackageResultsToUpdate(ctx, ownerID) if err != nil { return err } - if err = resolvePackageCollisions(results, sess); err != nil { + if err = resolvePackageCollisions(ctx, results); err != nil { return err } - if err = processPackageVersions(results, sess); err != nil { + if err = processPackageVersions(ctx, results); err != nil { return err } - return processPackageFiles(results, sess) + return processPackageFiles(ctx, results) } // processPackageFiles updates Maven package files and versions in the database // Returns an error if any database or processing operation fails. -func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) error { +func processPackageFiles(ctx context.Context, results []*mavenPackageResult) error { processedVersion := make(map[string][]*mavenPackageResult) for _, r := range results { @@ -113,7 +109,7 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro if r.PackageVersion.ID != r.PackageFile.VersionID { pattern := strings.TrimSuffix(r.PackageFile.Name, ".pom") + "%" // Per routers/api/packages/maven/maven.go:338, POM files already have the `IsLead`, so no update needed for this prop - if _, err := sess.Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { + if _, err := db.GetEngine(ctx).Exec("UPDATE package_file SET version_id = ? WHERE version_id = ? and name like ?", r.PackageVersion.ID, r.PackageFile.VersionID, pattern); err != nil { return err } } @@ -128,14 +124,14 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro rs := packageResults[0] - pf, md, err := parseMetadata(sess, rs) + pf, md, err := parseMetadata(ctx, rs) if err != nil { return err } if pf != nil && md != nil && md.GroupID == rs.GroupID && md.ArtifactID == rs.ArtifactID { if pf.VersionID != rs.PackageFile.VersionID { - if _, err := sess.ID(pf.ID).Cols("version_id").Update(pf); err != nil { + if _, err := db.GetEngine(ctx).ID(pf.ID).Cols("version_id").Update(pf); err != nil { return err } } @@ -150,11 +146,9 @@ func processPackageFiles(results []*mavenPackageResult, sess *xorm.Session) erro // parseMetadata retrieves metadata for a Maven package file from the database and decodes it into a Metadata object. // Returns the associated PackageFile, Metadata, and any error encountered during processing. -func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { - ctx := context.Background() - +func parseMetadata(ctx context.Context, snapshot *mavenPackageResult) (*packages.PackageFile, *Metadata, error) { var pf packages.PackageFile - found, err := sess.Table(pf). + found, err := db.GetEngine(ctx).Table(pf). Where("version_id = ?", snapshot.PackageFile.VersionID). // still the old id And("lower_name = ?", "maven-metadata.xml"). Get(&pf) @@ -183,7 +177,7 @@ func parseMetadata(sess *xorm.Session, snapshot *mavenPackageResult) (*packages. // processPackageVersions processes Maven package versions by updating metadata or inserting new records as necessary. // It avoids redundant updates by tracking already processed versions using a map. Returns an error on failure. -func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) error { +func processPackageVersions(ctx context.Context, results []*mavenPackageResult) error { processedVersion := make(map[string]int64) for _, r := range results { @@ -196,14 +190,14 @@ func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) e // for non collisions, just update the metadata if r.PackageVersion.PackageID == r.Package.ID { - if _, err := sess.ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { + if _, err := db.GetEngine(ctx).ID(r.PackageVersion.ID).Cols("metadata_json").Update(r.PackageVersion); err != nil { return err } } else { log.Info("Create new maven package version for %s:%s", r.PackageName, r.PackageVersion.Version) r.PackageVersion.ID = 0 r.PackageVersion.PackageID = r.Package.ID - if _, err := sess.Insert(r.PackageVersion); err != nil { + if _, err := db.GetEngine(ctx).Insert(r.PackageVersion); err != nil { return err } } @@ -216,10 +210,9 @@ func processPackageVersions(results []*mavenPackageResult, sess *xorm.Session) e // getMavenPackageResultsToUpdate retrieves Maven package results that need updates based on the owner ID. // It processes POM metadata, fixes package inconsistencies, and filters corrupted package versions. -func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mavenPackageResult, error) { - ctx := context.Background() +func getMavenPackageResultsToUpdate(ctx context.Context, ownerID *int64) ([]*mavenPackageResult, error) { var candidates []*mavenPackageResult - if err := sess. + if err := db.GetEngine(ctx). Table("package_file"). Select("package_file.*, package_version.*, package.*"). Join("INNER", "package_version", "package_version.id = package_file.version_id"). @@ -265,7 +258,7 @@ func getMavenPackageResultsToUpdate(sess *xorm.Session, ownerID *int64) ([]*mave // resolvePackageCollisions handles name collisions by keeping the first existing record and inserting new Package records for subsequent collisions. // Returns a map from PackageName to its resolved Package.ID. -func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) error { +func resolvePackageCollisions(ctx context.Context, results []*mavenPackageResult) error { // Group new names by lowerName collisions := make(map[string][]string) for _, r := range results { @@ -292,7 +285,7 @@ func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) } else if list[0] == r.PackageName { pkgIDByName[r.PackageName] = r.Package.ID - if _, err = sess.ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { + if _, err = db.GetEngine(ctx).ID(r.Package.ID).Cols("name", "lower_name").Update(r.Package); err != nil { return err } // create a new entry @@ -300,7 +293,7 @@ func resolvePackageCollisions(results []*mavenPackageResult, sess *xorm.Session) log.Info("Create new maven package for %s", r.Package.Name) r.Package.ID = 0 - if _, err = sess.Insert(r.Package); err != nil { + if _, err = db.GetEngine(ctx).Insert(r.Package); err != nil { return err } From 927dfb4f5045ed12797e7882931ea3be7944ca6f Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Tue, 22 Jul 2025 21:08:03 +0200 Subject: [PATCH 27/80] [v12.0/forgejo] chore: disable E2E test for webkit (#8616) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8611 As far as I can see and tell, the newest webkit version contains a regression that makes this specific test fail. The screenshots that are uploaded upon failure do not seem to suggest that this test should fail. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8616 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- tests/e2e/markup.test.e2e.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/e2e/markup.test.e2e.ts b/tests/e2e/markup.test.e2e.ts index 398a0a6300..b26e83661b 100644 --- a/tests/e2e/markup.test.e2e.ts +++ b/tests/e2e/markup.test.e2e.ts @@ -5,7 +5,8 @@ import {expect} from '@playwright/test'; import {save_visual, test} from './utils_e2e.ts'; -test('markup with #xyz-mode-only', async ({page}) => { +test('markup with #xyz-mode-only', async ({page}, workerInfo) => { + test.skip(['webkit', 'Mobile Safari'].includes(workerInfo.project.name), 'Newest version contains a regression'); const response = await page.goto('/user2/repo1/issues/1'); expect(response?.status()).toBe(200); From 06cb8dfcca8ef42c02d2511c79c8bcf63b8c649c Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 23 Jul 2025 00:18:50 +0200 Subject: [PATCH 28/80] [v12.0/forgejo] fix: make the action feed resilient to database inconsistencies (#8618) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8617 This reverts commit 7380eac5a2a2c04e6e8948f74d1b71dee2ffb61e. Resolves forgejo/forgejo#8612 It is possible for the action feed to reference deleted repositories the `INNER JOIN` will make sure that these are filtered out. We cannot filter these out after the fact, because the value of `count` will still be incorrect. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8617): make the action feed resilient to database inconsistencies Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8618 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/activities/action.go | 5 ++++- models/activities/action_test.go | 18 ++++++++++++++++++ models/fixtures/action.yml | 8 ++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/models/activities/action.go b/models/activities/action.go index 8592f81414..f928ad6784 100644 --- a/models/activities/action.go +++ b/models/activities/action.go @@ -473,8 +473,11 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err return nil, 0, err } + sess := db.GetEngine(ctx).Where(cond). + Select("`action`.*"). // this line will avoid select other joined table's columns + Join("INNER", "repository", "`repository`.id = `action`.repo_id") + opts.SetDefaultValues() - sess := db.GetEngine(ctx).Where(cond) sess = db.SetSessionPagination(sess, &opts) actions := make([]*Action, 0, opts.PageSize) diff --git a/models/activities/action_test.go b/models/activities/action_test.go index 47dbd8ac2d..161d05bbfa 100644 --- a/models/activities/action_test.go +++ b/models/activities/action_test.go @@ -227,6 +227,24 @@ func TestNotifyWatchers(t *testing.T) { }) } +func TestGetFeedsCorrupted(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + unittest.AssertExistsAndLoadBean(t, &activities_model.Action{ + ID: 8, + RepoID: 1700, + }) + + actions, count, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{ + RequestedUser: user, + Actor: user, + IncludePrivate: true, + }) + require.NoError(t, err) + assert.Empty(t, actions) + assert.Equal(t, int64(0), count) +} + func TestConsistencyUpdateAction(t *testing.T) { if !setting.Database.Type.IsSQLite3() { t.Skip("Test is only for SQLite database.") diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml index a97e94fbf4..f1592d4569 100644 --- a/models/fixtures/action.yml +++ b/models/fixtures/action.yml @@ -59,6 +59,14 @@ created_unix: 1603011540 # grouped with id:7 - id: 8 + user_id: 1 + op_type: 12 # close issue + act_user_id: 1 + repo_id: 1700 # dangling intentional + is_private: false + created_unix: 1603011541 + +- id: 9 user_id: 34 op_type: 12 # close issue act_user_id: 34 From 43305dff0383d782b13aa7544198098f7f8a427b Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 23 Jul 2025 10:07:21 +0200 Subject: [PATCH 29/80] [v12.0/forgejo] fix(ci): pull stylus from github:stylus/stylus#0.57.0 (#8627) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8625 npm error 404 Not Found - GET https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz - Not found Workaround to be reverted when the issue is fixed. Refs https://github.com/stylus/stylus/issues/2938 Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8627 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- package-lock.json | 3 +-- package.json | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9de06a8055..9108095bd7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14044,8 +14044,7 @@ }, "node_modules/stylus": { "version": "0.57.0", - "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz", - "integrity": "sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==", + "resolved": "git+ssh://git@github.com/stylus/stylus.git#bc1404aa1f6c03341bd76529c8cf4beb4f3d99f7", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index f7df1b3f38..fb5cf71355 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,9 @@ "vite-string-plugin": "1.3.4", "vitest": "3.2.3" }, + "overrides": { + "stylus": "github:stylus/stylus#0.57.0" + }, "browserslist": [ "defaults" ] From ac0d653925ea3668bacb543d9bba71461cfb860e Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 23 Jul 2025 10:55:17 +0200 Subject: [PATCH 30/80] [v12.0/forgejo] fix: rebase and fast forward merge breaks commit signatures (#8624) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8622 - Do not try to rebase a pull request when it is zero commits behind. We can trust this number as before merging a repository the status of the pull request is mergeable and thus not in a conflict checking stage (where this would be updated). - This resolves a issue where `git-replay` would rebase a pull request when this is not needed and causes to lose the signature of Git commits and commit IDs as shown in the pullrequest commits timeline. - Resolves forgejo/forgejo#8619 - Add a simple integration test that simply checks that after merging a up-to-date pull request via the rebase style that the commit ID didn't change. This demonstrates that it didn't do needlessly rebasing. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8624 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- services/pull/merge_prepare.go | 5 +++++ tests/integration/pull_merge_test.go | 29 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/services/pull/merge_prepare.go b/services/pull/merge_prepare.go index fc70da10a4..4598d57b7a 100644 --- a/services/pull/merge_prepare.go +++ b/services/pull/merge_prepare.go @@ -249,6 +249,11 @@ func rebaseTrackingOnToBase(ctx *mergeContext, mergeStyle repo_model.MergeStyle) ctx.outbuf.Reset() ctx.errbuf.Reset() + // If the pull request is zero commits behind, then no rebasing needs to be done. + if ctx.pr.CommitsBehind == 0 { + return nil + } + // Check git version for availability of git-replay. If it is available, we use // it for performance and to preserve unknown commit headers like the // "change-id" header used by Jujutsu and GitButler to track changes across diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index cca2381fd4..ab3d1604de 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -1135,3 +1135,32 @@ func TestPullDeleteBranchPerms(t *testing.T) { user4Session.MakeRequest(t, req, http.StatusOK) }) } + +// Test that rebasing only happens when its necessary. +func TestRebaseWhenNecessary(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n") + + resp := testPullCreate(t, session, "user1", "repo1", false, "master", "master", "This is a pull title") + pullLink := test.RedirectURL(resp) + + resp = session.MakeRequest(t, NewRequest(t, "GET", test.RedirectURL(resp)+"/commits"), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + commitLinkBefore, ok := htmlDoc.Find("a.sha").Attr("href") + assert.True(t, ok) + commitBefore := commitLinkBefore[strings.LastIndexByte(commitLinkBefore, '/'):] + + elem := strings.Split(pullLink, "/") + testPullMerge(t, session, elem[1], elem[2], elem[4], repo_model.MergeStyleRebase, false) + + resp = session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1"), http.StatusOK) + htmlDoc = NewHTMLParser(t, resp.Body) + commitLinkAfter, ok := htmlDoc.Find(".latest-commit a.sha").Attr("href") + assert.True(t, ok) + commitAfter := commitLinkAfter[strings.LastIndexByte(commitLinkAfter, '/'):] + + assert.Equal(t, commitBefore, commitAfter) + }) +} From 5e5dac84ed36a6eb70802c250087759240f438fc Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 23 Jul 2025 23:21:00 +0200 Subject: [PATCH 31/80] [v12.0/forgejo] Revert "fix(ci): pull stylus from github:stylus/stylus#0.57.0 (#8625)" (#8641) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8638 This reverts commit 4d06d62515a5e8d63f8714c67272159a7cc08ad2. https://www.npmjs.com/package/stylus?activeTab=versions is back. Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8641 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- package-lock.json | 3 ++- package.json | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9108095bd7..9de06a8055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14044,7 +14044,8 @@ }, "node_modules/stylus": { "version": "0.57.0", - "resolved": "git+ssh://git@github.com/stylus/stylus.git#bc1404aa1f6c03341bd76529c8cf4beb4f3d99f7", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz", + "integrity": "sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index fb5cf71355..f7df1b3f38 100644 --- a/package.json +++ b/package.json @@ -101,9 +101,6 @@ "vite-string-plugin": "1.3.4", "vitest": "3.2.3" }, - "overrides": { - "stylus": "github:stylus/stylus#0.57.0" - }, "browserslist": [ "defaults" ] From 8b06eb1bea5c7e83dd7758f79d8416cc083ddbbd Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 24 Jul 2025 07:57:37 +0200 Subject: [PATCH 32/80] [v12.0/forgejo] fix(ui): update i18n usage in comments (#8646) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8644 Fix regression of https://codeberg.org/forgejo/forgejo/pulls/8214 (regressing v11 feature https://codeberg.org/forgejo/forgejo/pulls/6523) Reporeted by @Andre601. ## Preview ![bug](/attachments/0e0c4703-537f-4adc-95f7-4047710522b4) ![fixed](/attachments/07bc5824-87ae-43da-92a2-8e6e9b9cf567) ## Testing * go to https://v13.next.forgejo.org/, log in * create repo, add some issue labels (on `./labels`) * create issue * add some labels to it and then close it * observe that what you see looks more like the 2nd screenshot than the 1st screenshot ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8644): fix(ui): update i18n usage in comments Co-authored-by: 0ko <0ko@noreply.codeberg.org> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8646 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- templates/repo/issue/view_content/comments.tmpl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 3bc4cd0773..d7701bc468 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -724,18 +724,18 @@
  • {{svg "octicon-dot-fill"}} {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.pulls.reopened_at" "" ""}} + {{ctx.Locale.Tr "repo.pulls.reopened_at" ""}} {{else}} - {{ctx.Locale.Tr "repo.issues.reopened_at" "" ""}} + {{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" "" ""}} + {{ctx.Locale.Tr "repo.pulls.closed_at" ""}} {{else}} - {{ctx.Locale.Tr "repo.issues.closed_at" "" ""}} + {{ctx.Locale.Tr "repo.issues.closed_at" ""}} {{end}}
  • {{end}} From bcd0821f3ef01c92c29a53dd4d2fb4472e4cc162 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 24 Jul 2025 17:53:11 +0200 Subject: [PATCH 33/80] [v12.0/forgejo] Revert "feat: remove API authentication methods that uses the URL query (#7924)" (#8653) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8633 This reverts commit b2a3966e648fc72eddf1bbc9383b069b0c8d59e8. weblate etc. are using this method and need to be updated before the change is enforced. Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8653 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/setting/security.go | 11 +++++++++++ routers/api/shared/middleware.go | 8 ++++++++ routers/api/v1/api.go | 12 ++++++++++++ services/auth/oauth2.go | 12 ++++++++++++ templates/swagger/v1_json.tmpl | 18 ++++++++++++++++++ tests/mysql.ini.tmpl | 1 + tests/pgsql.ini.tmpl | 1 + tests/sqlite.ini.tmpl | 1 + 8 files changed, 64 insertions(+) diff --git a/modules/setting/security.go b/modules/setting/security.go index c38d8dae79..f3480d1056 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -35,6 +35,7 @@ var ( PasswordHashAlgo string PasswordCheckPwn bool SuccessfulTokensCacheSize int + DisableQueryAuthToken bool CSRFCookieName = "_csrf" CSRFCookieHTTPOnly = true ) @@ -159,4 +160,14 @@ func loadSecurityFrom(rootCfg ConfigProvider) { PasswordComplexity = append(PasswordComplexity, name) } } + + sectionHasDisableQueryAuthToken := sec.HasKey("DISABLE_QUERY_AUTH_TOKEN") + + // TODO: default value should be true in future releases + DisableQueryAuthToken = sec.Key("DISABLE_QUERY_AUTH_TOKEN").MustBool(false) + + // warn if the setting is set to false explicitly + if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken { + log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.") + } } diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go index 7d537f1ef9..f56acbe1bf 100644 --- a/routers/api/shared/middleware.go +++ b/routers/api/shared/middleware.go @@ -30,6 +30,7 @@ func Middlewares() (stack []any) { return append(stack, context.APIContexter(), + checkDeprecatedAuthMethods, // Get user from session if logged in. apiAuth(buildAuthGroup()), verifyAuthWithOptions(&common.VerifyOptions{ @@ -126,6 +127,13 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC } } +// check for and warn against deprecated authentication options +func checkDeprecatedAuthMethods(ctx *context.APIContext) { + if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { + ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") + } +} + func securityHeaders() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index bf08bdd249..95c3ee4791 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -22,6 +22,8 @@ // // Security: // - BasicAuth : +// - Token : +// - AccessToken : // - AuthorizationHeaderToken : // - SudoParam : // - SudoHeader : @@ -30,6 +32,16 @@ // SecurityDefinitions: // BasicAuth: // type: basic +// Token: +// type: apiKey +// name: token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. +// AccessToken: +// type: apiKey +// name: access_token +// in: query +// description: This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. // AuthorizationHeaderToken: // type: apiKey // name: Authorization diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 4fdd15d7ec..fa13c20a7f 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -122,6 +122,18 @@ func (o *OAuth2) Name() string { // representing whether the token exists or not func parseToken(req *http.Request) (string, bool) { _ = req.ParseForm() + if !setting.DisableQueryAuthToken { + // Check token. + if token := req.Form.Get("token"); token != "" { + return token, true + } + // Check access token. + if token := req.Form.Get("access_token"); token != "" { + return token, true + } + } else if req.Form.Get("token") != "" || req.Form.Get("access_token") != "" { + log.Warn("API token sent in query string but DISABLE_QUERY_AUTH_TOKEN=true") + } // check header token if auHead := req.Header.Get("Authorization"); auHead != "" { diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 59c13cd9e6..9b4d79f0df 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -30058,6 +30058,12 @@ } }, "securityDefinitions": { + "AccessToken": { + "description": "This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead.", + "type": "apiKey", + "name": "access_token", + "in": "query" + }, "AuthorizationHeaderToken": { "description": "API tokens must be prepended with \"token\" followed by a space.", "type": "apiKey", @@ -30084,12 +30090,24 @@ "type": "apiKey", "name": "X-FORGEJO-OTP", "in": "header" + }, + "Token": { + "description": "This authentication option is deprecated for removal in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead.", + "type": "apiKey", + "name": "token", + "in": "query" } }, "security": [ { "BasicAuth": [] }, + { + "Token": [] + }, + { + "AccessToken": [] + }, { "AuthorizationHeaderToken": [] }, diff --git a/tests/mysql.ini.tmpl b/tests/mysql.ini.tmpl index f44aff7594..3315d85a3f 100644 --- a/tests/mysql.ini.tmpl +++ b/tests/mysql.ini.tmpl @@ -92,6 +92,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ +DISABLE_QUERY_AUTH_TOKEN = true [lfs] PATH = tests/{{TEST_TYPE}}/gitea-{{TEST_TYPE}}-mysql/data/lfs diff --git a/tests/pgsql.ini.tmpl b/tests/pgsql.ini.tmpl index 829fdc5b75..1e9b981800 100644 --- a/tests/pgsql.ini.tmpl +++ b/tests/pgsql.ini.tmpl @@ -97,6 +97,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTU1NTE2MTh9.hhSVGOANkaKk3vfCd2jDOIww4pUk0xtg9JRde5UogyQ +DISABLE_QUERY_AUTH_TOKEN = true [lfs] MINIO_BASE_PATH = lfs/ diff --git a/tests/sqlite.ini.tmpl b/tests/sqlite.ini.tmpl index d36388405b..df6cea44ca 100644 --- a/tests/sqlite.ini.tmpl +++ b/tests/sqlite.ini.tmpl @@ -94,6 +94,7 @@ DISABLE_GIT_HOOKS = false INSTALL_LOCK = true SECRET_KEY = 9pCviYTWSb INTERNAL_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE0OTI3OTU5ODN9.OQkH5UmzID2XBdwQ9TAI6Jj2t1X-wElVTjbE7aoN4I8 +DISABLE_QUERY_AUTH_TOKEN = true [oauth2] JWT_SECRET = KZb_QLUd4fYVyxetjxC4eZkrBgWM2SndOOWDNtgUUko From 267f314aeff9f0b9c554bd842133daf04fa8dcc5 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 26 Jul 2025 00:45:07 +0200 Subject: [PATCH 34/80] [v12.0/forgejo] fix: query token auth version mismatch (#8670) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8666 It's now scheduled for Forgejo v13 see #8633 for more context I used Github Copilot for some auto completion of code. Co-authored-by: Michael Kriese Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8670 Reviewed-by: Michael Kriese Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/setting/security.go | 2 +- routers/api/shared/middleware.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/setting/security.go b/modules/setting/security.go index f3480d1056..1f38857af6 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -168,6 +168,6 @@ func loadSecurityFrom(rootCfg ConfigProvider) { // warn if the setting is set to false explicitly if sectionHasDisableQueryAuthToken && !DisableQueryAuthToken { - log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will default to true in gitea 1.23 and will be removed in gitea 1.24.") + log.Warn("Enabling Query API Auth tokens is not recommended. DISABLE_QUERY_AUTH_TOKEN will be removed in Forgejo v13.0.0.") } } diff --git a/routers/api/shared/middleware.go b/routers/api/shared/middleware.go index f56acbe1bf..b57fabac0e 100644 --- a/routers/api/shared/middleware.go +++ b/routers/api/shared/middleware.go @@ -130,7 +130,7 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.APIC // check for and warn against deprecated authentication options func checkDeprecatedAuthMethods(ctx *context.APIContext) { if ctx.FormString("token") != "" || ctx.FormString("access_token") != "" { - ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") + ctx.Resp.Header().Set("Warning", "token and access_token API authentication is deprecated and will be removed in Forgejo v13.0.0. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization.") } } From 89a84a51e82258603246cd8214946e453b02f0c4 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 26 Jul 2025 15:17:04 +0200 Subject: [PATCH 35/80] [v12.0/forgejo] fix: store code challenge correctly in session (#8682) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8678 - Even though the test file contains some good extensive testing, it didn't bother to actually call `/login/oauth/access_token` to see if the received code actually resulted into a access token. - The fix itself is... well yeah self-explanatory. - Resolves forgejo/forgejo#8669 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8682 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/auth/oauth.go | 2 +- tests/integration/oauth_test.go | 61 +++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index e8e5d2c54b..1b75004623 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -489,7 +489,7 @@ func AuthorizeOAuth(ctx *context.Context) { }, form.RedirectURI) return } - if err := ctx.Session.Set("CodeChallengeMethod", form.CodeChallenge); err != nil { + if err := ctx.Session.Set("CodeChallenge", form.CodeChallenge); err != nil { handleAuthorizeError(ctx, AuthorizeError{ ErrorCode: ErrorCodeServerError, ErrorDescription: "cannot set code challenge", diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index 2b44863ec2..68d168bde5 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -1535,3 +1535,64 @@ func TestSignUpViaOAuth2FA(t *testing.T) { // Make sure user has to go through 2FA. assert.Equal(t, "/user/webauthn", test.RedirectURL(resp)) } + +func TestAccessTokenWithPKCE(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + var u *url.URL + t.Run("Grant", func(t *testing.T) { + session := loginUser(t, "user4") + req := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{ + "_csrf": GetCSRF(t, session, "/login/oauth/authorize?client_id=ce5a1322-42a7-11ed-b878-0242ac120002&redirect_uri=b&response_type=code&code_challenge_method=plain&code_challenge=CODE&state=thestate"), + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "redirect_uri": "b", + "state": "thestate", + "granted": "true", + }) + resp := session.MakeRequest(t, req, http.StatusSeeOther) + + var err error + u, err = url.Parse(test.RedirectURL(resp)) + require.NoError(t, err) + }) + + t.Run("Incorrect code verfifier", func(t *testing.T) { + req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{ + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "code": u.Query().Get("code"), + "code_verifier": "just a guess", + "grant_type": "authorization_code", + "redirect_uri": "b", + }) + resp := MakeRequest(t, req, http.StatusBadRequest) + + var respBody map[string]any + DecodeJSON(t, resp, &respBody) + + if assert.Len(t, respBody, 2) { + assert.Equal(t, "unauthorized_client", respBody["error"]) + assert.Equal(t, "failed PKCE code challenge", respBody["error_description"]) + } + }) + + t.Run("Get access token", func(t *testing.T) { + req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{ + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "code": u.Query().Get("code"), + "code_verifier": "CODE", + "grant_type": "authorization_code", + "redirect_uri": "b", + }) + resp := MakeRequest(t, req, http.StatusOK) + + var respBody map[string]any + DecodeJSON(t, resp, &respBody) + + if assert.Len(t, respBody, 4) { + assert.NotEmpty(t, respBody["access_token"]) + assert.NotEmpty(t, respBody["token_type"]) + assert.NotEmpty(t, respBody["expires_in"]) + assert.NotEmpty(t, respBody["refresh_token"]) + } + }) +} From 1efd54b94f53278c38d57060c40f8ba78a8f7de1 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 26 Jul 2025 16:07:56 +0200 Subject: [PATCH 36/80] [v12.0/forgejo] fix: show mergebox when only manual merge is allowed (#8683) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8681 - If a repository only has the 'manual merge' strategy allowed, the mergebox should still be shown. - The condition that checks if all merge strategies are disabled didn't check for the manual merge strategy. - Add a integration test that demonstrates this fix is effective. Reported-by: apteryx Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8683 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- templates/repo/issue/view_content/pull.tmpl | 2 +- tests/integration/pull_test.go | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 58d85dbf1b..bc578a6f4c 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -199,7 +199,7 @@ {{if .AllowMerge}} {{/* user is allowed to merge */}} {{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}} - {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}} + {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly $prUnit.PullRequestsConfig.AllowManualMerge}} {{$hasPendingPullRequestMergeTip := ""}} {{if .HasPendingPullRequestMerge}} {{$createdPRMergeStr := DateUtils.TimeSince .PendingPullRequestMerge.CreatedUnix}} diff --git a/tests/integration/pull_test.go b/tests/integration/pull_test.go index fd9dbf8888..8ff715a1b5 100644 --- a/tests/integration/pull_test.go +++ b/tests/integration/pull_test.go @@ -136,3 +136,24 @@ func TestPullCombinedReviewRequest(t *testing.T) { helper(t, "detach", "9", "removed review request for user11") helper(t, "detach", "2", "removed review requests for user11, user2") } + +func TestShowMergeForManualMerge(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // Only allow manual merge strategy for this repository. + pullRepoUnit := unittest.AssertExistsAndLoadBean(t, &repo_model.RepoUnit{ID: 5, RepoID: 1, Type: unit.TypePullRequests}) + pullRepoUnit.Config = &repo_model.PullRequestsConfig{ + AllowManualMerge: true, + DefaultMergeStyle: repo_model.MergeStyleManuallyMerged, + } + repo_model.UpdateRepoUnit(t.Context(), pullRepoUnit) + + session := loginUser(t, "user2") + + req := NewRequest(t, "GET", "/user2/repo1/pulls/5") + resp := session.MakeRequest(t, req, http.StatusOK) + + // Assert that the mergebox is shown. + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, "#pull-request-merge-form", true) +} From 4214fea8b1ebe30516fd8eae9afab01e964ed8a9 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sun, 27 Jul 2025 12:58:35 +0200 Subject: [PATCH 37/80] [v12.0/forgejo] fix: return error when user is not repo writer (#8696) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8690 - If the doer isn't a issue/pull writer, return a error. - Fixes a panic (NPE), because the callers of `prepareForReplaceOrAdd` simply checked if there was a error returned to see if the user was allowed. It didn't check if a statuscode was written. This is specifically a issue when the automatic token by Forgejo actions is used. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8696 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/api/v1/repo/issue_label.go | 2 +- tests/integration/api_issue_label_test.go | 25 +++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 3b2935305c..f2e79ea417 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -384,7 +384,7 @@ func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { ctx.Status(http.StatusForbidden) - return nil, nil, nil + return nil, nil, errors.New("not issue/pull writer") } err = issue_service.SetIssueUpdateDate(ctx, issue, form.Updated, ctx.Doer) diff --git a/tests/integration/api_issue_label_test.go b/tests/integration/api_issue_label_test.go index 160774a7e5..665e4b2f2a 100644 --- a/tests/integration/api_issue_label_test.go +++ b/tests/integration/api_issue_label_test.go @@ -336,3 +336,28 @@ func TestAPIModifyOrgLabels(t *testing.T) { AddTokenAuth(token) MakeRequest(t, req, http.StatusNoContent) } + +func TestAPIReplaceIssueLabelsActionsToken(t *testing.T) { + require.NoError(t, unittest.LoadFixtures()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}) + label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 47}) + task.RepoID = repo.ID + task.OwnerID = owner.ID + task.IsForkPullRequest = true // Read permission. + require.NoError(t, task.GenerateToken()) + + // Explicitly need "is_fork_pull_request". + require.NoError(t, actions_model.UpdateTask(t.Context(), task, "repo_id", "owner_id", "is_fork_pull_request", "token", "token_salt", "token_hash", "token_last_eight")) + + urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/labels", + owner.Name, repo.Name, issue.Index) + req := NewRequestWithJSON(t, "PUT", urlStr, &api.IssueLabelsOption{ + Labels: []any{label.Name}, + }).AddTokenAuth(task.Token) + MakeRequest(t, req, http.StatusForbidden) +} From 51870086bc611ec4f535dc08697922063a5847a2 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Tue, 29 Jul 2025 10:48:51 +0200 Subject: [PATCH 38/80] [v12.0/forgejo] fix: allow admins to always rename users (#8719) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8715 Do not apply the rename restriction of non-local users if the doer is an admin (changes via the admin interface). This is a conscious choice and the admin knows better if they make such changes. Regression of c59a057297 Resolves forgejo/forgejo#3657 ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8715): allow admins to always rename users Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8719 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- services/user/user.go | 3 ++- services/user/user_test.go | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/services/user/user.go b/services/user/user.go index d90fbac978..d682d5a434 100644 --- a/services/user/user.go +++ b/services/user/user.go @@ -47,7 +47,8 @@ func renameUser(ctx context.Context, u *user_model.User, newUserName string, doe } // Non-local users are not allowed to change their username. - if !u.IsOrganization() && !u.IsLocal() { + // If the doer is an admin, then allow the rename - they know better. + if !doerIsAdmin && !u.IsOrganization() && !u.IsLocal() { return user_model.ErrUserIsNotLocal{ UID: u.ID, Name: u.Name, diff --git a/services/user/user_test.go b/services/user/user_test.go index 36f2776ad8..f1cab60a6d 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -145,10 +145,16 @@ func TestRenameUser(t *testing.T) { t.Run("Non-Local", func(t *testing.T) { u := &user_model.User{ + ID: 2, + Name: "old-name", Type: user_model.UserTypeIndividual, LoginType: auth.OAuth2, } - require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename"), user_model.ErrUserIsNotLocal{}) + require.ErrorIs(t, RenameUser(db.DefaultContext, u, "user_rename2"), user_model.ErrUserIsNotLocal{UID: 2, Name: "old-name"}) + + t.Run("Admin", func(t *testing.T) { + require.NoError(t, AdminRenameUser(t.Context(), u, "user_rename2")) + }) }) t.Run("Same username", func(t *testing.T) { From 36f108041ca1326bdcb99258219f90fd3b86b925 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 30 Jul 2025 20:31:39 +0200 Subject: [PATCH 39/80] [v12.0/forgejo] fix: allow double digit epoch for Debian packages (#8733) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8671 Debian packages were capped for a single digit epoch, relax that requirement to a double digit epoch. This is allowed by Debian. Resolves forgejo/forgejo#8649 Co-authored-by: pkpkpkpk Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8733 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/packages/debian/metadata.go | 2 +- modules/packages/debian/metadata_test.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/packages/debian/metadata.go b/modules/packages/debian/metadata.go index e44801654b..1729a2206e 100644 --- a/modules/packages/debian/metadata.go +++ b/modules/packages/debian/metadata.go @@ -46,7 +46,7 @@ var ( // https://www.debian.org/doc/debian-policy/ch-controlfields.html#source namePattern = regexp.MustCompile(`\A[a-z0-9][a-z0-9+-.]+\z`) // https://www.debian.org/doc/debian-policy/ch-controlfields.html#version - versionPattern = regexp.MustCompile(`\A(?:[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`) + versionPattern = regexp.MustCompile(`\A(?:[1-9]?[0-9]:)?[a-zA-Z0-9.+~]+(?:-[a-zA-Z0-9.+-~]+)?\z`) ) type Package struct { diff --git a/modules/packages/debian/metadata_test.go b/modules/packages/debian/metadata_test.go index cfcbc57ee0..079b9c19c8 100644 --- a/modules/packages/debian/metadata_test.go +++ b/modules/packages/debian/metadata_test.go @@ -167,6 +167,14 @@ func TestParseControlFile(t *testing.T) { require.ErrorIs(t, err, ErrInvalidArchitecture) }) + t.Run("ValidVersionEpoch", func(t *testing.T) { + for _, version := range []string{"0:1.2.3-test", "1:1.2.3-test", "9:1.2.3-test", "10:1.2.3-test", "37:1.2.3-test", "99:1.2.3-test"} { + p, err := ParseControlFile(buildContent(packageName, version, packageArchitecture)) + require.NoError(t, err) + assert.NotNil(t, p) + } + }) + t.Run("Valid", func(t *testing.T) { content := buildContent(packageName, packageVersion, packageArchitecture) full := content.String() From 3740bcc8374316f36283d1d85f923c72cbbb691a Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 31 Jul 2025 14:37:47 +0200 Subject: [PATCH 40/80] [v12.0/forgejo] chore(ci): send mail when daily integration tests fail (#8730) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8727 ## Testing - 24h after this is merged - push a commit to https://codeberg.org/forgejo-integration/forgejo/src/branch/forgejo with an error - cancel all workflows except for https://codeberg.org/forgejo-integration/forgejo/actions?workflow=testing-integration.yml&actor=0&status=0 - verify a notification was sent to forgejo-integration-actions@forgejo.org about the error - update the user research discussion at https://codeberg.org/forgejo/user-research/issues/64 Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8730 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- .forgejo/workflows/testing-integration.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.forgejo/workflows/testing-integration.yml b/.forgejo/workflows/testing-integration.yml index 9e5cfb92ed..bfb5952116 100644 --- a/.forgejo/workflows/testing-integration.yml +++ b/.forgejo/workflows/testing-integration.yml @@ -1,7 +1,8 @@ # # Additional integration tests designed to run once a day when # `mirror.yml` pushes to https://codeberg.org/forgejo-integration/forgejo -# and send a notification via email should they fail. +# and send a notification via email to the contact email of the +# organization should they fail. # # For debug purposes: # @@ -22,6 +23,8 @@ on: - 'forgejo' - 'v*/forgejo' +enable-email-notifications: true + jobs: test-unit: # if: vars.ROLE == 'forgejo-coding' From 1ef2c321becd38f5e968bbe1d5a4ff7f31a0e02d Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 2 Aug 2025 13:46:34 +0200 Subject: [PATCH 41/80] [v12.0/forgejo] fix: correctly get stats for API commits (#8758) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8756 - Instead of generating a patch and parsing its contents, use a faster and simple way to get it via `--shortstat`. - Resolves forgejo/forgejo#8725 - Regression of forgejo/forgejo#7682 - Adds unit test. - Adds integration test. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8758 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/git/repo_compare.go | 11 +++ modules/git/repo_compare_test.go | 81 +++++++++++++++++++ services/convert/git_commit.go | 11 +-- .../integration/api_repo_git_commits_test.go | 19 +++++ 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go index 373b5befb5..94f1911c4a 100644 --- a/modules/git/repo_compare.go +++ b/modules/git/repo_compare.go @@ -183,6 +183,17 @@ func (repo *Repository) GetDiffShortStat(base, head string) (numFiles, totalAddi return numFiles, totalAdditions, totalDeletions, err } +// GetCommitStat returns the number of files, total additions and total deletions the commit has. +func (repo *Repository) GetCommitShortStat(commitID string) (numFiles, totalAdditions, totalDeletions int, err error) { + cmd := NewCommand(repo.Ctx, "diff-tree", "--shortstat", "--no-commit-id", "--root").AddDynamicArguments(commitID) + stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repo.Path}) + if err != nil { + return 0, 0, 0, err + } + + return parseDiffStat(stdout) +} + // GetDiffShortStat counts number of changed files, number of additions and deletions func GetDiffShortStat(ctx context.Context, repoPath string, trustedArgs TrustedCmdArgs, dynamicArgs ...string) (numFiles, totalAdditions, totalDeletions int, err error) { // Now if we call: diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go index 86bd6855a7..b1ebdf6177 100644 --- a/modules/git/repo_compare_test.go +++ b/modules/git/repo_compare_test.go @@ -1,4 +1,5 @@ // Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package git @@ -162,3 +163,83 @@ func TestGetCommitFilesChanged(t *testing.T) { assert.ElementsMatch(t, tc.files, changedFiles) } } + +func TestGetCommitShortStat(t *testing.T) { + t.Run("repo1_bare", func(t *testing.T) { + repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo1_bare")) + if err != nil { + require.NoError(t, err) + return + } + defer repo.Close() + + numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("ce064814f4a0d337b333e646ece456cd39fab612") + require.NoError(t, err) + assert.Equal(t, 0, numFiles) + assert.Equal(t, 0, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("feaf4ba6bc635fec442f46ddd4512416ec43c2c2") + require.NoError(t, err) + assert.Equal(t, 0, numFiles) + assert.Equal(t, 0, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("37991dec2c8e592043f47155ce4808d4580f9123") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("6fbd69e9823458e6c4a2fc5c0f6bc022b2f2acd1") + require.NoError(t, err) + assert.Equal(t, 2, numFiles) + assert.Equal(t, 2, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8006ff9adbf0cb94da7dad9e537e53817f9fa5c0") + require.NoError(t, err) + assert.Equal(t, 2, numFiles) + assert.Equal(t, 2, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("8d92fc957a4d7cfd98bc375f0b7bb189a0d6c9f2") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("95bb4d39648ee7e325106df01a621c530863a653") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + }) + + t.Run("repo6_blame_sha256", func(t *testing.T) { + repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo6_blame_sha256")) + if err != nil { + require.NoError(t, err) + return + } + defer repo.Close() + + numFiles, totalAddition, totalDeletions, err := repo.GetCommitShortStat("e2f5660e15159082902960af0ed74fc144921d2b0c80e069361853b3ece29ba3") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 0, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("9347b0198cd1f25017579b79d0938fa89dba34ad2514f0dd92f6bc975ed1a2fe") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 1, totalAddition) + assert.Equal(t, 1, totalDeletions) + + numFiles, totalAddition, totalDeletions, err = repo.GetCommitShortStat("ab2b57a4fa476fb2edb74dafa577caf918561abbaa8fba0c8dc63c412e17a7cc") + require.NoError(t, err) + assert.Equal(t, 1, numFiles) + assert.Equal(t, 6, totalAddition) + assert.Equal(t, 0, totalDeletions) + }) +} diff --git a/services/convert/git_commit.go b/services/convert/git_commit.go index 4603cfac4d..6a691966b8 100644 --- a/services/convert/git_commit.go +++ b/services/convert/git_commit.go @@ -15,7 +15,6 @@ import ( api "forgejo.org/modules/structs" "forgejo.org/modules/util" ctx "forgejo.org/services/context" - "forgejo.org/services/gitdiff" ) // ToCommitUser convert a git.Signature to an api.CommitUser @@ -210,17 +209,15 @@ func ToCommit(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep // Get diff stats for commit if opts.Stat { - diff, _, err := gitdiff.GetDiffSimple(ctx, gitRepo, &gitdiff.DiffOptions{ - AfterCommitID: commit.ID.String(), - }) + _, totalAdditions, totalDeletions, err := gitRepo.GetCommitShortStat(commit.ID.String()) if err != nil { return nil, err } res.Stats = &api.CommitStats{ - Total: diff.TotalAddition + diff.TotalDeletion, - Additions: diff.TotalAddition, - Deletions: diff.TotalDeletion, + Total: totalAdditions + totalDeletions, + Additions: totalAdditions, + Deletions: totalDeletions, } } diff --git a/tests/integration/api_repo_git_commits_test.go b/tests/integration/api_repo_git_commits_test.go index 7a93029d4c..db73307653 100644 --- a/tests/integration/api_repo_git_commits_test.go +++ b/tests/integration/api_repo_git_commits_test.go @@ -231,3 +231,22 @@ func TestGetFileHistoryNotOnMaster(t *testing.T) { assert.Equal(t, "1", resp.Header().Get("X-Total")) } + +func TestAPIReposGitCommit(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository) + + req := NewRequest(t, "GET", "/api/v1/repos/user2/repo16/git/commits/f27c2b2b03dcab38beaf89b0ab4ff61f6de63441"). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiData api.Commit + DecodeJSON(t, resp, &apiData) + + assert.Equal(t, "f27c2b2b03dcab38beaf89b0ab4ff61f6de63441", apiData.CommitMeta.SHA) + assert.Equal(t, 1, apiData.Stats.Total) + assert.Equal(t, 1, apiData.Stats.Additions) + assert.Equal(t, 0, apiData.Stats.Deletions) +} From 514229544f9a6a95e8ce8a38b3b7a33e75d6021c Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 6 Aug 2025 16:17:03 +0200 Subject: [PATCH 42/80] [v12.0/forgejo] fix: trim trailing slash in WebFinger OIDC issuer link (#8800) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8794 As stated in a comment: https://codeberg.org/forgejo/forgejo/issues/8634#issuecomment-6136933 > `routers/web/webfinger.go` was left unchanged, so it still includes the trailing slash, no longer matching the issuer specified in other endpoints. > > ... > > From the [OpenID Connect Discovery specification](https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery): > > > The Issuer location MUST be returned in the WebFinger response as the value of the href member of a links array element with rel member value http://openid.net/specs/connect/1.0/issuer. > > This sounds to me like the `href` should be the issuer location exactly. > > Using Forgejo for OIDC for auth with Tailscale is one instance of this change breaking something - signing up to Tailscale with OIDC now gives an error. Unsure what happens for existing accounts. In summary, since !8028, trailing slashes have been removed from the OIDC issuer locations specified by Forgejo everywhere except in WebFinger responses at `/.well-known/webfinger`, which still includes a trailing slash and so no longer matches the issuer as specified elsewhere (such as at `/.well-known/openid-configuration`). ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [ ] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [ ] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8794): trim trailing slash in WebFinger OIDC issuer link Co-authored-by: hazycora Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8800 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/webfinger.go | 2 +- tests/integration/webfinger_test.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index be3c2925fe..7636fd2d0c 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -153,7 +153,7 @@ func WebfingerQuery(ctx *context.Context) { }, { Rel: "http://openid.net/specs/connect/1.0/issuer", - Href: appURL.String(), + Href: strings.TrimSuffix(appURL.String(), "/"), }, } diff --git a/tests/integration/webfinger_test.go b/tests/integration/webfinger_test.go index 078be6fa54..4f07e4eb7f 100644 --- a/tests/integration/webfinger_test.go +++ b/tests/integration/webfinger_test.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "net/url" + "strings" "testing" "forgejo.org/models/unittest" @@ -43,6 +44,8 @@ func TestWebfinger(t *testing.T) { session := loginUser(t, "user1") + ctx := t.Context() + req := NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:%s@%s", user.LowerName, appURL.Host)) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "application/jrd+json", resp.Header().Get("Content-Type")) @@ -50,6 +53,26 @@ func TestWebfinger(t *testing.T) { var jrd webfingerJRD DecodeJSON(t, resp, &jrd) assert.Equal(t, "acct:user2@"+appURL.Host, jrd.Subject) + assert.ElementsMatch(t, []*webfingerLink{ + { + Rel: "http://webfinger.net/rel/profile-page", + Type: "text/html", + Href: user.HTMLURL(), + }, + { + Rel: "http://webfinger.net/rel/avatar", + Href: user.AvatarLink(ctx), + }, + { + Rel: "self", + Type: "application/activity+json", + Href: appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(user.ID), + }, + { + Rel: "http://openid.net/specs/connect/1.0/issuer", + Href: strings.TrimSuffix(appURL.String(), "/"), + }, + }, jrd.Links) assert.ElementsMatch(t, []string{user.HTMLURL(), appURL.String() + "api/v1/activitypub/user-id/" + fmt.Sprint(user.ID)}, jrd.Aliases) req = NewRequest(t, "GET", fmt.Sprintf("/.well-known/webfinger?resource=acct:%s@%s", user.LowerName, "unknown.host")) From f77d4995459302c4ba14e84c595f3a7f2699dd31 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 6 Aug 2025 18:32:17 +0200 Subject: [PATCH 43/80] [v12.0/forgejo] fix: correct release link in feed (#8805) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8802 Resolves forgejo/forgejo#8793 ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8802): correct release link in feed Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8805 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/feed/release.go | 2 +- tests/integration/release_feed_test.go | 41 +++++++++++++++----------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go index 646241c021..d24fa6ecc7 100644 --- a/routers/web/feed/release.go +++ b/routers/web/feed/release.go @@ -29,7 +29,7 @@ func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleas if isReleasesOnly { title = ctx.Locale.TrString("repo.release.releases_for", repo.FullName()) - link = &feeds.Link{Href: repo.HTMLURL() + "/release"} + link = &feeds.Link{Href: repo.HTMLURL() + "/releases"} } else { title = ctx.Locale.TrString("repo.release.tags_for", repo.FullName()) link = &feeds.Link{Href: repo.HTMLURL() + "/tags"} diff --git a/tests/integration/release_feed_test.go b/tests/integration/release_feed_test.go index e1781e343e..eded0459c8 100644 --- a/tests/integration/release_feed_test.go +++ b/tests/integration/release_feed_test.go @@ -4,6 +4,7 @@ package integration import ( + "fmt" "net/http" "regexp" "testing" @@ -27,50 +28,56 @@ func TestReleaseFeed(t *testing.T) { t.Run("RSS feed", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/releases.rss"), http.StatusOK) - assert.Equal(t, ` + releasesPath := "/user2/repo1/releases" + MakeRequest(t, NewRequest(t, "GET", releasesPath), http.StatusOK) + + resp := MakeRequest(t, NewRequest(t, "GET", releasesPath+".rss"), http.StatusOK) + assert.Equal(t, fmt.Sprintf(` Releases for user2/repo1 - http://localhost/user2/repo1/release + http://localhost%[1]s pre-release - http://localhost/user2/repo1/releases/tag/v1.0 + http://localhost%[1]s/tag/v1.0 some text for a pre release

    ]]>
    user2 - 5: http://localhost/user2/repo1/releases/tag/v1.0 + 5: http://localhost%[1]s/tag/v1.0
    testing-release - http://localhost/user2/repo1/releases/tag/v1.1 + http://localhost%[1]s/tag/v1.1 user2 - 1: http://localhost/user2/repo1/releases/tag/v1.1 + 1: http://localhost%[1]s/tag/v1.1
    -
    `, normalize(resp.Body.String())) +
    `, releasesPath), normalize(resp.Body.String())) }) t.Run("Atom feed", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/releases.atom"), http.StatusOK) - assert.Equal(t, ` + releasesPath := "/user2/repo1/releases" + MakeRequest(t, NewRequest(t, "GET", releasesPath), http.StatusOK) + + resp := MakeRequest(t, NewRequest(t, "GET", releasesPath+".atom"), http.StatusOK) + assert.Equal(t, fmt.Sprintf(` Releases for user2/repo1 - http://localhost/user2/repo1/release + http://localhost%[1]s - + pre-release - 5: http://localhost/user2/repo1/releases/tag/v1.0 + 5: http://localhost%[1]s/tag/v1.0 <p dir="auto">some text for a pre release</p> - + user2 user2@noreply.example.org @@ -79,13 +86,13 @@ func TestReleaseFeed(t *testing.T) { testing-release - 1: http://localhost/user2/repo1/releases/tag/v1.1 - + 1: http://localhost%[1]s/tag/v1.1 + user2 user2@noreply.example.org -`, normalize(resp.Body.String())) +`, releasesPath), normalize(resp.Body.String())) }) } From 2d3f44d03b4c5b597cbaac802e2e48b6e46dd220 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Thu, 7 Aug 2025 14:29:03 +0200 Subject: [PATCH 44/80] [v12.0/forgejo] fix: add .forgejo/CODEOWNERS support (#8746) (#8790) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8773 Currently, the docs mention that a CODEOWNERS file can be located in .forgejo for code owner PR review assignment, but this does not work. Add support for this location. This fixes #8746. ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [x] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8790): fix: add .forgejo/CODEOWNERS support (#8746) Co-authored-by: John Moon Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8790 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/repo/view.go | 2 +- services/issue/pull.go | 2 +- tests/integration/codeowner_test.go | 297 +++++++++++++++------------- 3 files changed, 159 insertions(+), 142 deletions(-) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index bb3e1388a8..91a8661097 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -438,7 +438,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { if workFlowErr != nil { ctx.Data["FileError"] = ctx.Locale.Tr("actions.runs.invalid_workflow_helper", workFlowErr.Error()) } - } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"}, ctx.Repo.TreePath) { + } else if slices.Contains([]string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"}, ctx.Repo.TreePath) { if rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize); err == nil { _, warnings := issue_model.GetCodeOwnersFromReader(ctx, rc, size > setting.UI.MaxDisplayFileSize) if len(warnings) > 0 { diff --git a/services/issue/pull.go b/services/issue/pull.go index 2eef1fbfa8..6245344ccb 100644 --- a/services/issue/pull.go +++ b/services/issue/pull.go @@ -71,7 +71,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue, } var rules []*issues_model.CodeOwnerRule - for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS"} { + for _, file := range []string{"CODEOWNERS", "docs/CODEOWNERS", ".gitea/CODEOWNERS", ".forgejo/CODEOWNERS"} { if blob, err := commit.GetBlobByPath(file); err == nil { rc, size, err := blob.NewTruncatedReader(setting.UI.MaxDisplayFileSize) if err == nil { diff --git a/tests/integration/codeowner_test.go b/tests/integration/codeowner_test.go index e2eeb843d8..b85a5f213d 100644 --- a/tests/integration/codeowner_test.go +++ b/tests/integration/codeowner_test.go @@ -26,175 +26,192 @@ import ( "github.com/stretchr/testify/require" ) -func TestCodeOwner(t *testing.T) { - onGiteaRun(t, func(t *testing.T, u *url.URL) { - user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) +func CodeOwnerTestCommon(t *testing.T, u *url.URL, codeownerTest CodeownerTest) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - // Create the repo. - repo, _, f := tests.CreateDeclarativeRepo(t, user2, "", - []unit_model.Type{unit_model.TypePullRequests}, nil, - []*files_service.ChangeRepoFile{ - { - Operation: "create", - TreePath: "CODEOWNERS", - ContentReader: strings.NewReader("README.md @user5\ntest-file @user4"), - }, + // Create the repo. + repo, _, f := tests.CreateDeclarativeRepo(t, user2, codeownerTest.Name, + []unit_model.Type{unit_model.TypePullRequests}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: codeownerTest.Path, + ContentReader: strings.NewReader("README.md @user5\ntest-file @user4"), }, - ) - defer f() + }, + ) + defer f() - dstPath := t.TempDir() - 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(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + dstPath := t.TempDir() + 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(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) - t.Run("Normal", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - err := os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o666) - require.NoError(t, err) + err := os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o666) + require.NoError(t, err) - err = git.AddChanges(dstPath, true) - require.NoError(t, err) + err = git.AddChanges(dstPath, true) + require.NoError(t, err) - err = git.CommitChanges(dstPath, git.CommitChangesOptions{ - Committer: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Author: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Message: "Add README.", - }) - require.NoError(t, err) - - err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-normal").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) - - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-normal"}) - unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add README.", }) + require.NoError(t, err) - t.Run("Forked repository", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-normal").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - session := loginUser(t, "user1") - testRepoFork(t, session, user2.Name, repo.Name, "user1", "repo1") + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-normal"}) + unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) - repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"}) + t.Run("Forked repository", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - r := fmt.Sprintf("%suser1/repo1.git", u.String()) - remoteURL, _ := url.Parse(r) - remoteURL.User = url.UserPassword("user2", userPassword) - doGitAddRemote(dstPath, "forked", remoteURL)(t) + session := loginUser(t, "user1") + testRepoFork(t, session, user2.Name, repo.Name, "user1", codeownerTest.Name) - err := git.NewCommand(git.DefaultContext, "push", "forked", "HEAD:refs/for/main", "-o", "topic=codeowner-forked").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: codeownerTest.Name}) - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-forked"}) - unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + r := fmt.Sprintf("%suser1/%s.git", u.String(), codeownerTest.Name) + remoteURL, _ := url.Parse(r) + remoteURL.User = url.UserPassword("user2", userPassword) + doGitAddRemote(dstPath, "forked", remoteURL)(t) + + err := git.NewCommand(git.DefaultContext, "push", "forked", "HEAD:refs/for/main", "-o", "topic=codeowner-forked").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-forked"}) + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) + + t.Run("Out of date", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Push the changes made from the previous subtest. + require.NoError(t, git.NewCommand(git.DefaultContext, "push", "origin").Run(&git.RunOpts{Dir: dstPath})) + + // Reset the tree to the previous commit. + require.NoError(t, git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})) + + err := os.WriteFile(path.Join(dstPath, "test-file"), []byte("## test content"), 0o666) + require.NoError(t, err) + + err = git.AddChanges(dstPath, true) + require.NoError(t, err) + + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add test-file.", }) + require.NoError(t, err) - t.Run("Out of date", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-out-of-date").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - // Push the changes made from the previous subtest. - require.NoError(t, git.NewCommand(git.DefaultContext, "push", "origin").Run(&git.RunOpts{Dir: dstPath})) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-out-of-date"}) + unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) + t.Run("From a forked repository", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - // Reset the tree to the previous commit. - require.NoError(t, git.NewCommand(git.DefaultContext, "reset", "--hard", "HEAD~1").Run(&git.RunOpts{Dir: dstPath})) + session := loginUser(t, "user1") - err := os.WriteFile(path.Join(dstPath, "test-file"), []byte("## test content"), 0o666) - require.NoError(t, err) + r := fmt.Sprintf("%suser1/%s.git", u.String(), codeownerTest.Name) + remoteURL, _ := url.Parse(r) + remoteURL.User = url.UserPassword("user1", userPassword) + doGitAddRemote(dstPath, "forked-2", remoteURL)(t) - err = git.AddChanges(dstPath, true) - require.NoError(t, err) + err := git.NewCommand(git.DefaultContext, "push", "forked-2", "HEAD:branch").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - err = git.CommitChanges(dstPath, git.CommitChangesOptions{ - Committer: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Author: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Message: "Add test-file.", - }) - require.NoError(t, err) - - err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-out-of-date").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) - - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-out-of-date"}) - unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) - unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + req := NewRequestWithValues(t, "POST", repo.FullName()+"/compare/main...user1/"+codeownerTest.Name+":branch", map[string]string{ + "_csrf": GetCSRF(t, session, repo.FullName()+"/compare/main...user1/"+codeownerTest.Name+":branch"), + "title": "pull request", }) - t.Run("From a forked repository", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + session.MakeRequest(t, req, http.StatusOK) - session := loginUser(t, "user1") + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "branch"}) + unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) + }) - r := fmt.Sprintf("%suser1/repo1.git", u.String()) - remoteURL, _ := url.Parse(r) - remoteURL.User = url.UserPassword("user1", userPassword) - doGitAddRemote(dstPath, "forked-2", remoteURL)(t) + t.Run("Codeowner user with no permission", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - err := git.NewCommand(git.DefaultContext, "push", "forked-2", "HEAD:branch").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) + // Make repository private, only user2 (owner of repository) has now access to this repository. + repo.IsPrivate = true + _, err := db.GetEngine(db.DefaultContext).Cols("is_private").Update(repo) + require.NoError(t, err) - req := NewRequestWithValues(t, "POST", repo.FullName()+"/compare/main...user1/repo1:branch", map[string]string{ - "_csrf": GetCSRF(t, session, repo.FullName()+"/compare/main...user1/repo1:branch"), - "title": "pull request", - }) - session.MakeRequest(t, req, http.StatusOK) + err = os.WriteFile(path.Join(dstPath, "README.md"), []byte("## very sensitive info"), 0o666) + require.NoError(t, err) - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "branch"}) - unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 4}) + err = git.AddChanges(dstPath, true) + require.NoError(t, err) + + err = git.CommitChanges(dstPath, git.CommitChangesOptions{ + Committer: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Author: &git.Signature{ + Email: "user2@example.com", + Name: "user2", + When: time.Now(), + }, + Message: "Add secrets to the README.", }) + require.NoError(t, err) - t.Run("Codeowner user with no permission", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-private").Run(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) - // Make repository private, only user2 (owner of repository) has now access to this repository. - repo.IsPrivate = true - _, err := db.GetEngine(db.DefaultContext).Cols("is_private").Update(repo) - require.NoError(t, err) - - err = os.WriteFile(path.Join(dstPath, "README.md"), []byte("## very sensitive info"), 0o666) - require.NoError(t, err) - - err = git.AddChanges(dstPath, true) - require.NoError(t, err) - - err = git.CommitChanges(dstPath, git.CommitChangesOptions{ - Committer: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Author: &git.Signature{ - Email: "user2@example.com", - Name: "user2", - When: time.Now(), - }, - Message: "Add secrets to the README.", - }) - require.NoError(t, err) - - err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-private").Run(&git.RunOpts{Dir: dstPath}) - require.NoError(t, err) - - // In CODEOWNERS file the codeowner for README.md is user5, but does not have access to this private repository. - pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-private"}) - unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) - }) + // In CODEOWNERS file the codeowner for README.md is user5, but does not have access to this private repository. + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "user2/codeowner-private"}) + unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) + }) +} + +type CodeownerTest struct { + Name string + Path string +} + +func TestCodeOwner(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + tests := []CodeownerTest{ + {Name: "root", Path: "CODEOWNERS"}, + {Name: "docs", Path: "docs/CODEOWNERS"}, + {Name: "gitea", Path: ".gitea/CODEOWNERS"}, + {Name: "forgejo", Path: ".forgejo/CODEOWNERS"}, + } + for _, test := range tests { + CodeOwnerTestCommon(t, u, test) + } }) } From e6469c5db03095b43a59bb80a76b9556b0d8ee04 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 7 Aug 2025 14:49:08 +0200 Subject: [PATCH 45/80] Update dependency go to v1.24.6 (v12.0/forgejo) (#8812) 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 | |---|---|---|---| | [go](https://go.dev/) ([source](https://github.com/golang/go)) | toolchain | patch | `1.24.4` -> `1.24.6` | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - Between 12:00 AM and 03:59 AM ( * 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/8812 Reviewed-by: Earl Warren 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 bb2be827eb..caf6b8f00a 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module forgejo.org go 1.24 -toolchain go1.24.4 +toolchain go1.24.6 require ( code.forgejo.org/f3/gof3/v3 v3.11.0 From 978ff860e6939d3e94b30a815e47640a68b86df9 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 9 Aug 2025 20:20:40 +0200 Subject: [PATCH 46/80] [v12.0/forgejo] fix: wrap items in gitignore dropdown (#8841) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8830 - Resolves forgejo/forgejo#2639 - Simple E2E test to show it doesn't overflow Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8841 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- templates/repo/create_init.tmpl | 2 +- tests/e2e/repo-new.test.e2e.ts | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/templates/repo/create_init.tmpl b/templates/repo/create_init.tmpl index 729b44c8e6..468d9a9ae5 100644 --- a/templates/repo/create_init.tmpl +++ b/templates/repo/create_init.tmpl @@ -7,7 +7,7 @@
    {{template "base/footer" .}} diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl index 621fc44bf5..09af3e4229 100644 --- a/templates/repo/commits_table.tmpl +++ b/templates/repo/commits_table.tmpl @@ -37,4 +37,10 @@ {{template "repo/commits_list" .}} {{end}} +{{if .OldFilename}} +
    + {{ctx.Locale.Tr "repo.commits.renamed_from" .OldFilename}} ({{ctx.Locale.Tr "repo.commits.browse_further"}}) +
    +{{end}} + {{template "base/paginate" .}} diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/info/commit-graph b/tests/gitea-repositories-meta/user2/repo59.git/objects/info/commit-graph index d151dc87e63b6d2485029740163c1cd02c7d41d0..f85243ea3fd2d2614f8e9743fcf16407815cadcb 100644 GIT binary patch delta 188 zcmeC-I>9wTTXKrKi=z<(7_h29Xb{P2veD%{vmpzRNfMsy!eS%x`LXCdgOwbCM>o|s zdD-~=u~{|KadHDoj>k;a)0s!SQeU|q-!FHh;Na$Pwe`DzHZT@|tYl!|$(Ux=2Vo+} ZZMHXJCHfd-zkT^;zAfWw<*i4?nlB0l-WMEj~-O{G< Tc)K`PiF1+U^e-||Q6V(|xv43sgH8Fdxtz#A1G+T6$qYpj{+k*0mY0Lwp(@O>!!Jxl_Wh zEvR~`k(g(>4f}7te!~TqdYrpi`BO~~9y2ZRIJzc8d!MGc9oF-fMmF zXJ){U^ZGN6REE}POEgx6g#B9ll&M9H!DGvks`ZjBYwae@jE!ku$@R_WSiRCUt|_{4 zzutGB%x>6W6Vs${c$FpFgT_xxi{(OQ@9Yxj$a7L^=K){#UH_+h-I&RGI`fEE>MPgd`{j-l z9NZkPwtm-wGwa`4a^!tg*q70E(=u+80Iy-)>PIW@{;dzu|1tZZWx`6$J3T9MW}a=I zzwLZ<^*;VRR(xN4+NS<}F)QrGSKHsl-_Cpyn`1JaJFcWu@L>1r&j;U_9eAD){nW4U zlY{B0Wu@D9IevaDde2}bN8r&-^-W$jet&FM&2-Ec2nhK7EB8ev@8PFMC64=_>Z(8C zymi^GMXN)fuyJJ<;q6NE`8vuUw+glEbYgk_`2+A?}dQH zLmDutp8zHuVA|o+0kS^;F)(hKt%3X}K=D!_Ed$ItbwKfSAUhR^n}J!i49K?yrUM?C u<1yRS9-axgEW>czaiZ>uUCUgS99VOBL6G&b`9jCfn_S=c#zKVIeH#E6W%8H+ literal 0 HcmV?d00001 diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-1cc75cb726e1cc54d31c00c741912da8baa644a4.pack b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-1cc75cb726e1cc54d31c00c741912da8baa644a4.pack new file mode 100644 index 0000000000000000000000000000000000000000..73622aa52c0750c3d647d1d39e235077b0df6fab GIT binary patch literal 3999 zcmZvbWmFRm7l%hnjusFEQDAfm13@}Rw{$pCKtf3=C8S}5)aX%?BP3)XHF8RbbT^Z3 zgxCKY-`;!9{r22*p5Hn5rz@`r0RRBFf0rs-(Jv=$yoI7t1rV_eFa5Nj!SFWecl&x?p>PPkP+l z#A1mtv{a7+R)Y!IHI+G6MUoM=eQZ5yN&ULTBurz`D!BckU>bstazhs69yS8waoz{^ z%!n4n@wmm1bkx@Qe$yQeD9qkJjE?ahMN(uEIuIaPRJH<^NoUjfBa2Im($19Wpr;up5`u?) zT8;nRn#Y@@Pvk^bNqpuEJJXq~bG)3;O_4{yuaNPE%ftBgE6mGr%y>QFHT!M&8g ziDP#*9`RYfgr_K%rejp1KI5!S4cZF^DcCUk^-VYB-bmAI&n$Nwz08xCAL+%lCVh^> z)Yizps&pB@;A36FfM5JB)o}*mW-ih-Mll^(rMDHh9lpWCm)l%FCt2nOZIvAf0~oqX z%rz!*a5mn!7oFC)0R8BUkWXGIdX7$Cc@-h(+arW6iq?*|0icyQFS2HEyX;%Kik4!PPW z?M+$U=d!_C#=VSR9lZ&c^m0z`=O?#wRAo5UzfOP&7FGPnH&$6NiIfjj_OZ(NL$Um` z9WmWfF^W7P0ha&t5oKu#1P2OT;9-;$3xz?NGp$n1v#rEp32A8@a!YQ?epn$F_r+u7 zGwaz?VM}FNa8pUtm4<{{s`+sc64A6=x+;b2dQ%53FyE_8*T4s{T5ihSWgcNF)EZR9 z2N{FrzLYMFsb0cL?JlIYN9LEuf#I4}*p1s0y4CiS?vG?9xad-shx-OWjz4_92Uw*a zs>)HymfJ~TynR16Dk8B8vJsJ@oU`yiS>gj^893^ql<`mD9~0pokqxK5{3_8o9fpZ_ z!jvI6!Z9YzISPO40#mQ4RH6NFenWBw@)Q;4UzZ`k>$Avae9%2gK4Q~Nfah>^>PZbf z7cXDuGRjR!aM>d+c%d#wdBhzmNvJgHe?}pqtxaInCx_qi*Y232 zSqr*XPIfN}9QIyXo}o?zGnj&wk5hsd{ob#86?i3+1TVS!;`l-*GVBkG?;KG%u-OOd%eRxKZIGGS@?|f^iR&9iLth+H2OuAVC>082QPfSa1M;VxvyGxmK?;FHfA-bBo-N+D*o((m1 zyBARXVR&v?dD>hDE}I&pLZMw2zW0OsG4`6?NLaaa2YbaH#qZlrNi<1=N%R#aZ996c zD=t6<_%_~WYCaO?<(~0mDX`{v(#}$F*2jlU`8?wAq$ktD;-Te-X6hABO_~CXG6!v` z7rf$R#Z%!IxYpsJp9Svg(5^KJHfg7?N89;V>NEtZs#)$&!=|`RJ@riXahw><1+>RD zu}?KfDu)hS@w@NS>Khw*^{o2eb3xtum}W6KwzOkP*||b4y`LD#E~-PC)iysbChtz~Sm&@2 z*Zbx85R=eo^aUKr_&{O;y;Osrf55;ssev>8JqFS!T-)zRlJiK4Vtx(r852 z`<(_5g$}Q6Fph!Qg9QJql(7cQ+!r#6J%Lm%{21Fl6+Gy=yx%+!rjxYZ4IVtZIHhiy zn%vXwtz{Va&Knl;PQJ*(FEcpfW2{w{NCj|c&*FDAX+bb3lXw9|AD;TU;&-G{NRg@o zL~~WXGZY>C9NPL7Th@{4TA>O#-+R**y+w*Ctz2S5>)3j)jweQOVvL@^=E z2YX3KfY^@+_lz|@pq4hIi!M8{kEp5*oB@!7!0>!pTgl}0Ps}c7%|nwR zGZ~+G`6R?Cu7v&_?A-k!?i7mb`fcso8)<>d=l&x3H_%JG(BIk@rnC<#5X!b#!Y5O zQSTsT{6c8Gm6#U5;-h}4<0?{i@5zqGZ5gu9d>+0hWcAy1m>i)Y%sV!5Q^EwfqS3i} zOLKYGhF_a~p+M@ij{$nm#M|rE{hg;Us*}k_PT^Jg9!Fl}xv3F+3!)VWSH3H~53~#h zS)!ez=kxV>pvB+8)N7c*p(iW*qh-G9C?-W(H{tu9TES)}!#%9u*A$=q(}y(Jz*vO} zK;>LodB3Xo0Zv54oVjytCRCRy_$}E;e!eFA(a9yu@U1V*!@r_qUUFQHn-ckCmdmco zTUxMII<{WZ4rVw=nOF2Ki#DQR6K3%;Wy(;7=zwXvGh8-Bnsp0YA2P<_+!lEYmzm8A zomV$c$iOEKBcEaK2^w#Lb6$mnKur?f5C>leiu@oW^2;I`joes*yk@_9O(>Lq7H`yN zYAiH5nU&O}%4l9vHS3vV3FNpD8QU}PDyA};vm)ug`KIB2ez!`-MlLP&JXLJ>#4FA; z7N3sLJH@UeZLi0q)~hROg=GdveWp$4Q*_vLV)GUD5oq8f+!@z|oEYN0?{?YgrEc(+ zlqbNlkXjRNmfG0k^^GDHZiA3VE%c~qoxuh3XNxU}GI44k#{%(|V-&DhnL~F8~*UiS4b2@#V z7mv+7ys6;XEs;hQ^^0!i5>4@a9s9I{x5;G!LyD;o7bOXY+k*JQuTIb zR@XbC4+KUgJ55Xe_65=8S3dlbI9Km547e+orRT2RiI<;z9>3IQwruMa^vvw$XiXqz z`C~z^!23VZuOC$%8Rkh}wJNmg?2T{Fmi0659Oeb_#`6@Wh-49mJXdM zY35V0Hj>m-_ty;@x*s5==si@Vv&lj|=Y!f?b`>)rEu z<8%0Ca!AYe_11#xyF*8Z&cx#@(FDWDpNm_6>z4Xej7y zSWE#cWl99y)to+p0YR+IRNr*VKxBt2HgSy%usjL{(GB|D61=$(L}m%hA&((?gjD}0 zhSxo;$dd1A3JP%SxF1gMWxEDF>^5@ubS z@jAq(;n5_g;+H!m4BLXLry7Yw@3bh^P7CGT=PJLCyJX2r^^lKZp5->|zxnzN7hLLb z?q=mrH9dIDp?k0O#h;l0KhEpVI8qr}pDoc?6%zJq@l&Q2H3p9@ORCmOwyd?AG&44) zeI?g7pJVk(*SMzW#{GKVeKNaYhfPeA!r@hxY!4bgF)dmiW_O~p!%nl{ZKnC@ZN`Ex z9oO!x{jkm|sPmShlc&wf&DW9@7c-SjT=qEAS+QqLRll=Kpd-&oshtOW*?0Y)?sa3z z){=*9A_nY|rc-k_tj+IZ`MzT3?9&@1I_wNg%64-KT|0ErZ=PcE;{|8dzqRDZ`>L=n zqwS_;+$I5D!@31$WhcDQ-+Jo{Z&GE)mY-AX4Z3O;t$QeOsNH3$wQk+RLfb;;fROw< zs~@er`?o$s|HtfumI*60@ARz5nR%4w?APQj#nq;J zp5Td}556%w@H`>Oj{w5~;Pn@?d+qGzQ z=#$(_u|4p16j!cPuI0Wu*(=7fk+Mr)dWTGX;HzJL)F&)0 zuZ%(S$D#N-VDW4Y%!)u-ES-Ttd>RnD0onb)^aISF5`I8j2^4DsCZ~(Qthp2@_Y}x4 z0v22DK>9qeNC*MaZ-Lq1Bv36-pV;o)%gg==_sf<)^wG=i%W-_VPDShf{Yw4SqES8S R@AzV-R-~x4va}Sh1OP;7B9#CD diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.pack b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.pack deleted file mode 100644 index ddb8c16caf1f0600bf22af94b7f41c3799679617..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6316 zcmZu$RZtvYlUzK&;vRwqcXtWy?yz`Zad!w7++iVPaa|mO2Z!LYkl;>m4-UaE|6ScZ z-NW}VU-eW?Pu0|P*Jw(~r~m)}#D9+{N7^qZ1EN${B~KV}_k?lFXAlW|LxZ~U02~_Md>gA>-Ih>#u8Yz9iy<`p*MwOlPzs2TJS4S}$`heU< zcWE^S!=GCVycD1Q&FJ#UV!9BWJ0Fq|pd81!(w**g!N6-!ljzzt#IcSXrq&Uq zf(El>-5gwa-6T}|l>v4Mlv}EbOQT>hbv2{5QCStD=&=^8SsY|gUkG1tyf5G*`IKxW z$DX$yl2de(b#cL1G-Y1@&Fr&06S-9hC*hvN8B;6cdFIfmlGX)Y*%d@kdd}bZG_Ba8{?TVbDcsYs3?X;#Opo|`K7?;xlRTrynb<0*;BCT zX=z{I+9bzYrW0uY=C7N}zBg?f9c?$1YHuaj8u!!L^C^*c#+`qDHMw1yHAeCxbwYYJ*%rN$?ku7X!1I-Zv)X-4SeBDL6UQf zbja_Mo@OL(30U8_I7Fz+%E%p55UqfY*J&dfz`V8ic1KiZ%Y0Bp=?u$WCQ0AP^_RWN zCDBPRgY~ucI|E^0_olk~v7AK9i2YV zMzi~N;-bwFOeOnrV|HA+X$o-Q0e;L38JfVuJX|vAeh1cF%~_dpu6TJ-%iTp@Tn|7& zAWGASR{Sk@Rf57PIeNk}kG#t3EqCcfp>MX7ejr4*(7qi(YB9U6%fo^PB?1%$g)A5T zumxsAcP)c61UoVl4)y8%Q+{QPs_}D2qwZpj{vRy(EG3;bZsYn-4XjH3l45jnw0%Bl8?;^aj5iUvwHJd8RoGJ!jNC%+9o-6CA$9j2!H#(SNg9hW<4vf79H4#EWm5BcO{=LC(S(rOfAN)o-`ja&*tNars8&a%btD?Fs)f+kzEMe2$bz0t??pUdya(Ck05p#e!RaW(zdPKw0 zb9*K^nqf-c?%t*4P|I4CvT0|VzY7Uq(;a2rry+)S%z%xM$g-Ymp`tjdCfPj(W=cfCbsL=g0#)`2@{~{@vsI2)_cFj z0Q!)@JD-G)nu{~rv}faLpcb%;e$@-eX)>Le0~2;iTr;-l&|wQ>ZWic_>_QC@OZdUH zK$>lsK-`=8NACAVdY`P_=*`mJnAqt+>pd2_V1s5@j3iiFBr?5TPtV4j~X5T7m%nxI)XmO(hl2#TUxF~;Ft{9?t>n@l&gZ)Fs-5$`*l z=zQTQ;MIc=PNWz|Op1WUq`oC|sV4fpcJTGpE{Pr`?eD%p;v6FD`FK7=SFJUySh#nF z-{ksp#{-E10I{GdKaEMbfoYGVymBo9ESu}on))L~r{Y^#o^5`dw#f^vPZ&%6XC{Nh zM^1&%WsE$X_$@=Moq*g;h1CNck(NOJ3)<81`jb1+pR9znr{pu-y3Cr8cfjFD?caX2 z(V^5{(bppFY?hsOZ;3$37kU~d5`isZMVt+IP$eAmtKTTF(*0b^@}IRC!}gDH02Ois z%DRMn&ZnWCJ@v>=NNz3B_>7baE=KwNVTSlbQdzAoNS8kO)B3_}<7Cw5s$nWP5`seA zdS+dmU$o)72J9=KHbj^sjwlD+zVuvf>?KBR^mJTi2A;)OX~C<#^g~vaE?^&5Q;qo{ zAQTA}X^DjP30zP#kZ)<>?scIiA3`iE`8I@Ib2(0QwpWjhqP!<lk8R^VV!y#7)qYXvJoHRx3u4#Jq zEc8EODZAy777*7+&l`Zc`V`lBTH|BqWZzTHd>=a9phw3{{m#B2uzz>6bgcT^UYEv_ z5OEJPd|fHg@za2DQ8Zo8@N3?0rd>S)W6LiIp3_`ig8YvhV*v|J$&J|Jh-N!ckOwS$ zmR*{V*PxaN1|g%ymOti__y~RWOMp2RiwzbZjnlu-Bq7iFVV6M zmk-i@ZB?XOQ8pm|#)crR7Y7POAC{< z-mro+rT(z~Rzxz?9p|0A3PYiGtA0i@aL~M_aqF5B3}9L63-EN)K{pKau`9_w*>soB zKTUxLPMRf8>9C9pc^CcsoRgTi{N#tqe7O1cOF9CQD>mLWRco!;q>1Ol^)Ml$gAPZ2 zkWOuKiPW^NG7j>t3;K;q`LXJ-`$7t8oGOj`N6-Xe7UtC$s z;g4UY=%_yE=XhBvjE~bB-_5b~ER)tvA(^YdKyb`&7Co}PWt>(0*~wQ6ue^UN3Dh-| zV-shS9OrBuIJMLlNqUC|j6>@P-ko3MwNNeo7~lYw9Wy9x7SccF3X3mY7VxLAWc?;{ z+~L)ZK78V!ufDAGCpLf)@3hX8jScv1lQ3pkPu%ZXs7f5^iW2N2GU%^aq5-Nr7M@kxEMa` z?-mlgNs(TNcDtBBVDW^la8occJC+Q^*z77808z3g*{rF-V`jtX!0fd*iQF z`}S2b387T{;u#Y>co>vTql{HUZQyO(^`V2f#g#a*GpG1USis|gM|x?zTWP4d>P&>0 zq)eK0r1jf)x=Ii)lo{qWm6Xmw13m1PWD}d^!_NDvQ_UN79v)F@|Cq=#g|CWr0QZdp zare&u+3SLf`Ka?Ab-0m5$5t(XmnN)@`TG-2@J-3aHxy0dz~+r!9LjWQMlCMQ3fM^R zfw$l`hxxZ^tKID&=ikAm-|UU zUgAtr17$zjR+c$45E{22zZTQ+?I=;S2=t(=^OaKt1YTu}2x@TGP01xgLP}9rzNQ^P zUrL?fwMwAkyt4dyG?<8C~W+V%89FXP@#*j?b*KN>oGg z4Qyp;bapH*TFY%6mNb9sgT57A93W^eY-WY-3^5jwq4?5L`u4t&$=Cd&c-uhDB885n z?cqBvqw`O$b013=bguP_ZfJle{RDWGFn@#5BRrG2G>mN_mqy3-f}_6oEWSFhbL^fNWWWL-T3<}`Bdl~DmF78 zvgv4rMvQpyki<$CjHSaNX*3>5TXIs0P;El8_7ZfOO(Jnd{8z#Unx&xPz=)Z zPhcgRUc4uNkVF`bC4|RRD~2LT;UVA31%786ME2t2l*0~A>hHWBHOLP(*fj~|hHDyi+y2-W%ApPX^HA?#^m*#<0>F`R+)1dx% zrMSHlTz%dF{inM0MWrAQiWve5+LiN<$<`lE-3r&-#Q10_Z`%t+4~yv(_S}HX)?l8% zj9Q-Agfeg3Ybd4n=H>(${2z4m00PiBN`J1+S?T4QW_7T zvLrLplLkxv8!}W-()K$zRth8M0sgF_L@GNJS>aZ<#@2fe^61oCa9ZckTNfDPMKCzD zxw9cWuHTP})V9E!!D3|OWZdFZ<$v*ZjoDFTOqow_PI3!R+%Nl@q5n?WEIB zoGU>CRRYy+&M0+ku^F7_XuE~lE(AjrgYExy(qD4;?pyUkQp-_+q|HT$(x!*&95diJ z{CPX!i1Kxc6ezKY2*D15qLZ9(V(Q33|KmXZiLU8Nd4T+#nA}-S2{}S!<*JGOH>iy! zUa$`kUQnP)dwG2i(e?3#xcXOiuL;k5V8(5vTVb^7^AzL!CKlhIY6a09!_6=LltmcX zv=0G&Ok2~Ie*c&dHCPsB2)DSXFZ{HF1k|(~BAG)Swl9Y*z9)`6{I}kL}lE?56 z5@KVJSMS;wuv}VOU#T1AB ziD&vm$D;&KZ`BNA`00nT|K000kQz%&L?WH<_}U}EFdmr*)iceiJL6=);G0KZ^cKZ3 z4*sn=kyr6W%e6%pBo;@ZQ@P?;W3=&w#|Un2j^tiw;-Jz0F;Ixk$J*>^$@A~B8JMsb zAxRA2HJAS@6f7%PiHzDAKWMVAcnXhErMRnKNvvS7tDYTq>S1JcDu0)%W1x5#p67i) zl^g(Ew|}mwrceu%65L;7_m;r=-=c<&_M(cu-u1$+GQSQ4kytydov=V_3_0 zwa}}GUDS>ii844L`>0@%V`uEPJK4gmI?lq$DO$nP{+*K6C2=55RI1&Q@E>0$f`Tf_ z@5!qTwiAHY3Q1z-+M`6NIf=yIL&lqy9zmkUPnSC!Ih(PCgB+iqW87$JE_L(89@?eb zHBM#@SIURq9bauP;S_psa7tV7gS=83btS7Dn|@!|;}Wc;pIQjxY@{+RJpU+j*C<1TYS6jsvFSW}C3hoG%0TWcpm7CzFG+g-{)3&xi*uAv zv^i1#Y;r&)1U1OOKx4w3Q&1Ye0Mjprmye5!ms{(Nhqu>-_uROerQ5-}$It$Xd4<>b+M z*T@>OOns$@0J;hlqGNoNUQ?xa^Q#70QcWYpJXZCN=?9N2^|8$`7^r|&CxpsZHXE^RGUSnmphtoC^0f6;| ze)S9?%=^FTjSCg(($)!O6#oAd``!Ry_Te=D3-#Ipq66^ykfLnD`Tz6xRh)(#fGUSb z+VhW^j{X~E_7ToWwo%SXU8X6vQB6f%#Zgri6?SDftJcWmgbK6%h=z{h_=FPs{Pq?i zAY&WXqzU1_6Gs*GnAFucl-aeI)Y;V3^>z>e(tD`jPE@}finwvCP-$0&HRn z3PZqFxhzV5ErSushMp=!1xvBqxe7MsH!@gRb6jnLS=j(5kT`BIg86L2kGA_0)XH=1A8GzrtsZV P<#0@LlG?4MW7+sGiQYRB diff --git a/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.rev b/tests/gitea-repositories-meta/user2/repo59.git/objects/pack/pack-6dd3a6fe138f1d77e14c2e6b8e6c41e5ae242adf.rev deleted file mode 100644 index 81554dba7405b86a5e8db6c52a81b9c6590b0f0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 136 zcmWIYbctYKU|@t|HXscGd_XJ!#2|5QAQl2*E+A$CVpbp)0b(8?W&vVeAm#*OW+3JV zVnHBg2VxE&7S6rA?4NMIZ23bUz3jdm$EWL5wC;O}2sbdA%~jT1wJNl|W?lK0SNV4U DT$U8_ diff --git a/tests/gitea-repositories-meta/user2/repo59.git/packed-refs b/tests/gitea-repositories-meta/user2/repo59.git/packed-refs index 77fedbf67d..b3e7aa4eca 100644 --- a/tests/gitea-repositories-meta/user2/repo59.git/packed-refs +++ b/tests/gitea-repositories-meta/user2/repo59.git/packed-refs @@ -1,4 +1,4 @@ # pack-refs with: peeled fully-peeled sorted d8f53dfb33f6ccf4169c34970b5e747511c18beb refs/heads/cake-recipe -80b83c5c8220c3aa3906e081f202a2a7563ec879 refs/heads/master +f3e315de30a90851c5b27f824a3c4efc3caa9941 refs/heads/master d8f53dfb33f6ccf4169c34970b5e747511c18beb refs/tags/v1.0 diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index 7370b63dcd..3d268e68cd 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -587,6 +587,26 @@ func TestRenamedFileHistory(t *testing.T) { assert.Equal(t, "/user2/repo59/commits/commit/80b83c5c8220c3aa3906e081f202a2a7563ec879/licnse", oldFileHistoryLink) }) + t.Run("Renamed file, pagination", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer test.MockVariableValue(&setting.Git.CommitsRangeSize, 1)() // Limit commits displayed on the page to one + + resp := MakeRequest(t, NewRequest(t, "GET", "/user2/repo59/commits/branch/master/license"), http.StatusOK) + page1 := NewHTMLParser(t, resp.Body) + + resp = MakeRequest(t, NewRequest(t, "GET", "/user2/repo59/commits/branch/master/license?page=2"), http.StatusOK) + page2 := NewHTMLParser(t, resp.Body) + + // Browse further is only shown on 2nd page + browseFurtherSel := ".ui.bottom.attached.header a[href='/user2/repo59/commits/commit/80b83c5c8220c3aa3906e081f202a2a7563ec879/licnse']" + page1.AssertElement(t, browseFurtherSel, false) + page2.AssertElement(t, browseFurtherSel, true) + + // Pagination goes after Browser further + afterBrowseFurther := page2.Find(browseFurtherSel).Parent().Parent().NextAll() + assert.Equal(t, 1, afterBrowseFurther.Find(".pagination.menu").Length()) + }) + t.Run("Non renamed file", func(t *testing.T) { req := NewRequest(t, "GET", "/user2/repo59/commits/branch/master/README.md") resp := MakeRequest(t, req, http.StatusOK) From 97a27bb096d9eb3fdc0fe0d1c06d95daf2215622 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Mon, 11 Aug 2025 14:06:50 +0200 Subject: [PATCH 50/80] [v12.0/forgejo] fix: make ssh key verification command more robust (#8860) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8821 This is a follow-up to a13414341b7e462ef5dabc17fad8ff6efda7c370. There are two situations where the previous verification command could fail: * The user has an SSH key in a normal file, but no running SSH agent. * The user uses a special SSH agent, but it's not specified via the SSH_AUTH_SOCK variable. To fix that, we provide two separate commands to copy-paste. One for file-based keys and one for agent-based keys. People using file-based keys with a path other than the standard `~/.ssh/id_ed25519` should notice themselves what to change. People using an SSH agent get a little hint to make sure the SSH_AUTH_SOCK variable is set. See also: https://codeberg.org/Codeberg/Community/issues/2066 (no test or documentation changes) ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. ## Testing * `pr=8821 ; git fetch forgejo +refs/pull/$pr/head:refs/heads/wip-pr-$pr` * `git checkout wip-pr-$pr` * `make TAGS='sqlite sqlite_unlock_notify' watch` * login * visit /user/settings/keys and add an ssh key * click verify * see a CLI sample is displayed ![image](/attachments/7350cbe5-4a78-47a7-821f-575dd0a43e0e) * run the command matching your setup (file-based SSH key or agent-provided one), copy paste the output and submit it * check that the verification is successful ![image](attachments/20074f32-e06f-42fd-9732-32171016c47e) ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/8821): make ssh key verification command more robust Co-authored-by: Remo Senekowitsch Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8860 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- options/locale/locale_en-US.ini | 1 + templates/user/settings/keys_ssh.tmpl | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 5fd2ebd163..cb35fbecdf 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -895,6 +895,7 @@ ssh_invalid_token_signature = The provided SSH key, signature or token do not ma ssh_token_required = You must provide a signature for the below token ssh_token = Token ssh_token_help = You can generate a signature using: +ssh_token_help_ssh_agent = or, if you're using an SSH agent (with the SSH_AUTH_SOCK variable set): ssh_token_signature = Armored SSH signature key_signature_ssh_placeholder = Begins with "-----BEGIN SSH SIGNATURE-----" verify_ssh_key_success = SSH key "%s" has been verified. diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl index 420a25cb1d..d7cc074db8 100644 --- a/templates/user/settings/keys_ssh.tmpl +++ b/templates/user/settings/keys_ssh.tmpl @@ -77,7 +77,11 @@
    +

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

    +
    echo -n '{{$.TokenToSign}}' | ssh-keygen -Y sign -n gitea -f ~/.ssh/id_ed25519 # or the path to the private key if it is different.
    +
    +

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

    bash -c "echo -n '{{$.TokenToSign}}' | ssh-keygen -Y sign -n gitea -f <(echo '{{.OmitEmail}}')"

    From 53c4c6bda826ae058c9cb32d9e7c1e943868bc3c Mon Sep 17 00:00:00 2001 From: BtbN Date: Mon, 11 Aug 2025 23:08:46 +0200 Subject: [PATCH 51/80] [v12.0/forgejo] fix: prevent pull requests from being merged multiple times (#8862) Backport of https://codeberg.org/forgejo/forgejo/pulls/8842 Contains a partial cherry-pick of 184e068f376ce8c5f5bfe74ec17f3188d8ba9189, for the parts the PR depends on. The whole commit is way too involved to cherry-pick as a whole. Co-authored-by: Danko Aleksejevs Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8862 Reviewed-by: Earl Warren Co-authored-by: BtbN Co-committed-by: BtbN --- options/locale_next/locale_en-US.json | 1 + routers/api/v1/repo/pull.go | 3 +++ routers/web/repo/pull.go | 4 ++++ services/pull/merge.go | 28 ++++++++++++++++++---- services/pull/merge_test.go | 34 +++++++++++++++++++++++++++ tests/test_utils.go | 26 ++++++++++++++++++++ 6 files changed, 92 insertions(+), 4 deletions(-) diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 4f1c3904e2..3262b49f6c 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -44,6 +44,7 @@ "relativetime.2months": "two months ago", "relativetime.1year": "last year", "relativetime.2years": "two years ago", + "repo.pulls.already_merged": "Merge failed: This pull request has already been merged.", "repo.pulls.merged_title_desc": { "one": "merged %[1]d commit from %[2]s into %[3]s %[4]s", "other": "merged %[1]d commits from %[2]s into %[3]s %[4]s" diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 9360ff1335..d978d24e8b 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -1016,6 +1016,9 @@ func MergePullRequest(ctx *context.APIContext) { } else if models.IsErrMergeUnrelatedHistories(err) { conflictError := err.(models.ErrMergeUnrelatedHistories) ctx.JSON(http.StatusConflict, conflictError) + } else if models.IsErrPullRequestHasMerged(err) { + conflictError := err.(models.ErrPullRequestHasMerged) + ctx.JSON(http.StatusConflict, conflictError) } else if git.IsErrPushOutOfDate(err) { ctx.Error(http.StatusConflict, "Merge", "merge push out of date") } else if models.IsErrSHADoesNotMatch(err) { diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 4e365f24ea..1e3eda9b61 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1445,6 +1445,10 @@ func MergePullRequest(ctx *context.Context) { log.Debug("MergeUnrelatedHistories error: %v", err) ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories")) ctx.JSONRedirect(issue.Link()) + } else if models.IsErrPullRequestHasMerged(err) { + log.Debug("MergePullRequestHasMerged error: %v", err) + ctx.Flash.Error(ctx.Tr("repo.pulls.already_merged")) + ctx.JSONRedirect(issue.Link()) } else if git.IsErrPushOutOfDate(err) { log.Debug("MergePushOutOfDate error: %v", err) ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date")) diff --git a/services/pull/merge.go b/services/pull/merge.go index f69f8a87b4..c279147399 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -208,7 +208,30 @@ func AddCommitMessageTrailer(message, tailerKey, tailerValue string) string { // Merge merges pull request to base repository. // Caller should check PR is ready to be merged (review and status checks) func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, baseGitRepo *git.Repository, mergeStyle repo_model.MergeStyle, expectedHeadCommitID, message string, wasAutoMerged bool) error { - if err := pr.LoadBaseRepo(ctx); err != nil { + pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) + defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) + + pr, err := issues_model.GetPullRequestByID(ctx, pr.ID) + if err != nil { + log.Error("Unable to load pull request itself: %v", err) + return fmt.Errorf("unable to load pull request itself: %w", err) + } + + if pr.HasMerged { + return models.ErrPullRequestHasMerged{ + ID: pr.ID, + IssueID: pr.IssueID, + HeadRepoID: pr.HeadRepoID, + BaseRepoID: pr.BaseRepoID, + HeadBranch: pr.HeadBranch, + BaseBranch: pr.BaseBranch, + } + } + + if err := pr.LoadIssue(ctx); err != nil { + log.Error("Unable to load issue: %v", err) + return fmt.Errorf("unable to load issue: %w", err) + } else if err := pr.LoadBaseRepo(ctx); err != nil { log.Error("Unable to load base repo: %v", err) return fmt.Errorf("unable to load base repo: %w", err) } else if err := pr.LoadHeadRepo(ctx); err != nil { @@ -216,9 +239,6 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U return fmt.Errorf("unable to load head repo: %w", err) } - pullWorkingPool.CheckIn(fmt.Sprint(pr.ID)) - defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) - prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) if err != nil { log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err) diff --git a/services/pull/merge_test.go b/services/pull/merge_test.go index 2a26759956..7d3b7538d6 100644 --- a/services/pull/merge_test.go +++ b/services/pull/merge_test.go @@ -6,7 +6,15 @@ package pull import ( "testing" + "forgejo.org/models" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/gitrepo" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_expandDefaultMergeMessage(t *testing.T) { @@ -90,3 +98,29 @@ func TestAddCommitMessageTailer(t *testing.T) { assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1", "Test-tailer", "v2")) assert.Equal(t, "title\n\nTest-tailer: v1\nTest-tailer: v2", AddCommitMessageTrailer("title\n\nTest-tailer: v1\n", "Test-tailer", "v2")) } + +func TestMergeMergedPR(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + require.NoError(t, pr.LoadBaseRepo(t.Context())) + + gitRepo, err := gitrepo.OpenRepository(t.Context(), pr.BaseRepo) + require.NoError(t, err) + defer gitRepo.Close() + + assert.True(t, pr.HasMerged) + pr.HasMerged = false + + err = Merge(t.Context(), pr, doer, gitRepo, repo_model.MergeStyleRebase, "", "I should not exist", false) + require.Error(t, err) + assert.True(t, models.IsErrPullRequestHasMerged(err)) + + if mergeErr, ok := err.(models.ErrPullRequestHasMerged); ok { + assert.Equal(t, pr.ID, mergeErr.ID) + assert.Equal(t, pr.IssueID, mergeErr.IssueID) + assert.Equal(t, pr.HeadBranch, mergeErr.HeadBranch) + assert.Equal(t, pr.BaseBranch, mergeErr.BaseBranch) + } +} diff --git a/tests/test_utils.go b/tests/test_utils.go index 75d1f98914..7bbab2751a 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -42,6 +42,7 @@ import ( "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "xorm.io/xorm/convert" ) func exitf(format string, args ...any) { @@ -342,6 +343,7 @@ type DeclarativeRepoOptions struct { Name optional.Option[string] EnabledUnits optional.Option[[]unit_model.Type] DisabledUnits optional.Option[[]unit_model.Type] + UnitConfig optional.Option[map[unit_model.Type]convert.Conversion] Files optional.Option[[]*files_service.ChangeRepoFile] WikiBranch optional.Option[string] AutoInit optional.Option[bool] @@ -390,9 +392,14 @@ func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts enabledUnits = make([]repo_model.RepoUnit, len(units)) for i, unitType := range units { + var config convert.Conversion + if cfg, ok := opts.UnitConfig.Value()[unitType]; ok { + config = cfg + } enabledUnits[i] = repo_model.RepoUnit{ RepoID: repo.ID, Type: unitType, + Config: config, } } } @@ -470,6 +477,25 @@ func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, en } if enabledUnits != nil { opts.EnabledUnits = optional.Some(enabledUnits) + + for _, unitType := range enabledUnits { + if unitType == unit_model.TypePullRequests { + opts.UnitConfig = optional.Some(map[unit_model.Type]convert.Conversion{ + unit_model.TypePullRequests: &repo_model.PullRequestsConfig{ + AllowMerge: true, + AllowRebase: true, + AllowRebaseMerge: true, + AllowSquash: true, + AllowFastForwardOnly: true, + AllowManualMerge: true, + AllowRebaseUpdate: true, + DefaultMergeStyle: repo_model.MergeStyleMerge, + DefaultUpdateStyle: repo_model.UpdateStyleMerge, + }, + }) + break + } + } } if disabledUnits != nil { opts.DisabledUnits = optional.Some(disabledUnits) From aca70e89b648af84726a001cce26213c06b0afd5 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 13 Aug 2025 07:59:10 +0200 Subject: [PATCH 52/80] [12.0/forgejo] fix: de-duplicate Forgejo Actions job names when needed (#8883) **Backport: https://codeberg.org/forgejo/forgejo/pulls/8864** The status of two jobs by the same name shadow each other, they need to be distinct. If two jobs by the same name are found, they are made distinct by adding a - suffix. Resolves forgejo/forgejo#8648 (cherry picked from commit 6bc1803c70280b09af3e62bfc4000755faa36b42) ``` Conflicts: services/actions/notifier_helper.go services/actions/schedule_tasks.go services/actions/workflows.go trivial context conflicts services/actions/job_parser.go use "github.com/nektos/act/pkg/jobparser" ``` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8883 Reviewed-by: Gusted Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- services/actions/job_parser.go | 31 ++++ services/actions/job_parser_test.go | 212 ++++++++++++++++++++++++++++ services/actions/notifier_helper.go | 2 +- services/actions/schedule_tasks.go | 2 +- services/actions/workflows.go | 2 +- 5 files changed, 246 insertions(+), 3 deletions(-) create mode 100644 services/actions/job_parser.go create mode 100644 services/actions/job_parser_test.go diff --git a/services/actions/job_parser.go b/services/actions/job_parser.go new file mode 100644 index 0000000000..f55edcddad --- /dev/null +++ b/services/actions/job_parser.go @@ -0,0 +1,31 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package actions + +import ( + "fmt" + + "github.com/nektos/act/pkg/jobparser" +) + +func jobParser(workflow []byte, options ...jobparser.ParseOption) ([]*jobparser.SingleWorkflow, error) { + singleWorkflows, err := jobparser.Parse(workflow, options...) + if err != nil { + return nil, err + } + nameToSingleWorkflows := make(map[string][]*jobparser.SingleWorkflow, len(singleWorkflows)) + duplicates := make(map[string]int, len(singleWorkflows)) + for _, singleWorkflow := range singleWorkflows { + id, job := singleWorkflow.Job() + nameToSingleWorkflows[job.Name] = append(nameToSingleWorkflows[job.Name], singleWorkflow) + if len(nameToSingleWorkflows[job.Name]) > 1 { + duplicates[job.Name]++ + job.Name = fmt.Sprintf("%s-%d", job.Name, duplicates[job.Name]) + if err := singleWorkflow.SetJob(id, job); err != nil { + return nil, err + } + } + } + return singleWorkflows, nil +} diff --git a/services/actions/job_parser_test.go b/services/actions/job_parser_test.go new file mode 100644 index 0000000000..9c1361d74e --- /dev/null +++ b/services/actions/job_parser_test.go @@ -0,0 +1,212 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +package actions + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServiceActions_jobParser(t *testing.T) { + for _, testCase := range []struct { + name string + workflow string + singleWorkflows []string + }{ + { + name: "OneJobNoDuplicate", + workflow: ` +jobs: + job1: + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `jobs: + job1: + name: job1 + runs-on: docker + steps: + - run: echo OK +`, + }, + }, + { + name: "MatrixTwoJobsWithSameJobName", + workflow: ` +name: test +jobs: + job1: + name: shadowdefaultmatrixgeneratednames + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: shadowdefaultmatrixgeneratednames + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: shadowdefaultmatrixgeneratednames-1 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + { + name: "MatrixTwoJobsWithMatrixGeneratedNames", + workflow: ` +name: test +jobs: + job1: + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: job1 (1.17) + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: job1 (1.19) + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + { + name: "MatrixTwoJobsWithDistinctInterpolatedNames", + workflow: ` +name: test +jobs: + job1: + name: myname-${{ matrix.version }} + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: myname-1.17 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: myname-1.19 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + { + name: "MatrixTwoJobsWithIdenticalInterpolatedNames", + workflow: ` +name: test +jobs: + job1: + name: myname-${{ matrix.typo }} + strategy: + matrix: + version: [1.17, 1.19] + runs-on: docker + steps: + - run: echo OK +`, + singleWorkflows: []string{ + `name: test +jobs: + job1: + name: myname- + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.17 +`, + `name: test +jobs: + job1: + name: myname--1 + runs-on: docker + steps: + - run: echo OK + strategy: + matrix: + version: + - 1.19 +`, + }, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + sw, err := jobParser([]byte(testCase.workflow)) + require.NoError(t, err) + for i, sw := range sw { + actual, err := sw.Marshal() + require.NoError(t, err) + assert.Equal(t, testCase.singleWorkflows[i], string(actual)) + } + }) + } +} diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index e240c996b5..c94223bc3b 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -372,7 +372,7 @@ func handleWorkflows( continue } - jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars)) + jobs, err := jobParser(dwf.Content, jobparser.WithVars(vars)) if err != nil { run.Status = actions_model.StatusFailure log.Info("jobparser.Parse: invalid workflow, setting job status to failed: %v", err) diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index cf8b29ead7..b422057d6f 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -153,7 +153,7 @@ func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) run.NotifyEmail = notifications // Parse the workflow specification from the cron schedule - workflows, err := jobparser.Parse(cron.Content, jobparser.WithVars(vars)) + workflows, err := jobParser(cron.Content, jobparser.WithVars(vars)) if err != nil { return err } diff --git a/services/actions/workflows.go b/services/actions/workflows.go index fbba3fd667..3a13d90716 100644 --- a/services/actions/workflows.go +++ b/services/actions/workflows.go @@ -138,7 +138,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette return nil, nil, err } - jobs, err := jobparser.Parse(content, jobparser.WithVars(vars)) + jobs, err := jobParser(content, jobparser.WithVars(vars)) if err != nil { return nil, nil, err } From 428edf37fba479f245a996f6a2d417d03af74317 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Wed, 13 Aug 2025 23:06:30 +0200 Subject: [PATCH 53/80] [v12.0/forgejo] fix: compare week as numbers and not as strings (#8887) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8882 The repository contributors graph received the stats for each author for each week, these weeks are stored as unix milis values, `Object.entries` converted these values to strings and `sort()` would thus sort them as strings - this worked without a problem for most repository. If a repository has commits from before 'Sun Sep 9 03:46:40 AM CEST 2001', it meant that the weeks when those commits were made would be sorted towards the end because "1000000000" > "999999999" (when compared as strings) and would thus be silently cut from the data. This edge-case was seen by the curl repository (https://mastodon.social/@bagder/115018271785548165) Sort them as numbers to avoid this problem, it being stored as strings is otherwise not a problem. Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8887 Reviewed-by: Gusted Reviewed-by: Beowulf Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- .../js/components/RepoContributors.test.js | 76 +++++++++++++++++++ web_src/js/components/RepoContributors.vue | 2 +- web_src/js/vitest.setup.js | 4 + 3 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 web_src/js/components/RepoContributors.test.js diff --git a/web_src/js/components/RepoContributors.test.js b/web_src/js/components/RepoContributors.test.js new file mode 100644 index 0000000000..d97a862d2d --- /dev/null +++ b/web_src/js/components/RepoContributors.test.js @@ -0,0 +1,76 @@ +import {flushPromises, mount} from '@vue/test-utils'; +import RepoContributors from './RepoContributors.vue'; + +test('has commits from before 2001', async () => { + vi.spyOn(global, 'fetch').mockResolvedValue({ + json: vi.fn().mockResolvedValue({ + 'daniel@haxx.se': { + name: 'Daniel Stenberg', + total_commits: 13, + weeks: { + 1754179200000: { + week: 1754179200000, + additions: 4330, + deletions: 47, + commits: 10, + }, + 946166400000: { + week: 946166400000, + additions: 37273, + deletions: 0, + commits: 1, + }, + }, + }, + total: { + name: 'Total', + total_commits: 11, + weeks: { + 1754179200000: { + week: 1754179200000, + additions: 4330, + deletions: 47, + commits: 10, + }, + 946166400000: { + week: 946166400000, + additions: 37273, + deletions: 0, + commits: 1, + }, + }, + }, + }), + ok: true, + }); + + const repoContributorsGraph = mount(RepoContributors, { + global: { + stubs: { + 'relative-time': { + template: 'relative time', + }, + }, + }, + props: { + repoLink: '', + repoDefaultBranchName: '', + locale: { + filterLabel: '', + contributionType: { + commits: '', + additions: '', + deletions: '', + }, + + loadingTitle: '', + loadingTitleFailed: '', + loadingInfo: '', + }, + }, + }); + await flushPromises(); + + expect(repoContributorsGraph.componentVM.xAxisStart).toBe(946166400000); + expect(repoContributorsGraph.componentVM.contributorsStats['daniel@haxx.se'].weeks[0].week).toBe(946166400000); +}); diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue index 5e03019ef1..4b15c3714b 100644 --- a/web_src/js/components/RepoContributors.vue +++ b/web_src/js/components/RepoContributors.vue @@ -125,7 +125,7 @@ export default { const data = await response.json(); const {total, ...rest} = data; // below line might be deleted if we are sure go produces map always sorted by keys - total.weeks = Object.fromEntries(Object.entries(total.weeks).sort()); + total.weeks = Object.fromEntries(Object.entries(total.weeks).map((x) => [parseInt(x[0]), x[1]]).sort((a, b) => a[0] - b[0])); const weekValues = Object.values(total.weeks); this.xAxisStart = weekValues[0].week; diff --git a/web_src/js/vitest.setup.js b/web_src/js/vitest.setup.js index 5366958fb5..a99b852097 100644 --- a/web_src/js/vitest.setup.js +++ b/web_src/js/vitest.setup.js @@ -1,3 +1,7 @@ +import $ from 'jquery'; + +$.fn.dropdown = () => undefined; + window.__webpack_public_path__ = ''; window.config = { From 14a7e6a5adb063740618834db9eb5e432790420f Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Fri, 15 Aug 2025 11:26:07 +0200 Subject: [PATCH 54/80] [v12.0/forgejo] fix: migrate new Github release assets (#8899) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8771 - It seems that mid-May (according to some system notices in Codeberg) Github started (or converted all) release asssets to be made available under `https://release-assets.githubusercontent.com/`. - Update the migration code to allow this baseURL for Github release assets. - Resolves Codeberg/Community#2061 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8899 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- services/migrations/github.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/migrations/github.go b/services/migrations/github.go index 9721c86180..317eb568b5 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -364,7 +364,8 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease) // Prevent open redirect if !hasBaseURL(redirectURL, g.baseURL) && - !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") { + !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") && + !hasBaseURL(redirectURL, "https://release-assets.githubusercontent.com/") { WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL) return io.NopCloser(strings.NewReader(redirectURL)), nil From 2941adfd119a8b8fb15603f30be26f411c9143f6 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 16 Aug 2025 06:43:42 +0200 Subject: [PATCH 55/80] [v12.0/forgejo] fix: minio initialization can freeze indefinitely if misconfigured (#8914) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8897 Fixes #8893 by using a 30 second initialization timeout on the minio storage. Manually tested by configuring `MINIO_ENDPOINT=100.64.123.123`... ``` 2025/08/14 11:29:29 ...s/storage/storage.go:157:initAttachments() [I] Initialising Attachment storage with type: minio 2025/08/14 11:29:29 ...les/storage/minio.go:100:NewMinioStorage() [I] Creating Minio storage at 100.64.123.123:mfenniak-forgejo with base path attachments/ 2025/08/14 11:29:59 routers/init.go:63:mustInit() [F] forgejo.org/modules/storage.Init failed: Get "http://100.64.123.123/mfenniak-forgejo/?versioning=": context deadline exceeded ``` ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [ ] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. Co-authored-by: Mathieu Fenniak Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8914 Reviewed-by: Mathieu Fenniak Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/storage/minio.go | 11 +++++++--- modules/storage/minio_test.go | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/modules/storage/minio.go b/modules/storage/minio.go index bf51a1642a..8d4f9d6627 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -76,8 +76,13 @@ var getBucketVersioning = func(ctx context.Context, minioClient *minio.Client, b return err } +var initializationTimeout = 30 * time.Second + // NewMinioStorage returns a minio storage func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, error) { + initCtx, cancel := context.WithTimeout(ctx, initializationTimeout) + defer cancel() + config := cfg.MinioConfig if config.ChecksumAlgorithm != "" && config.ChecksumAlgorithm != "default" && config.ChecksumAlgorithm != "md5" { return nil, fmt.Errorf("invalid minio checksum algorithm: %s", config.ChecksumAlgorithm) @@ -112,7 +117,7 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, // Otherwise even if the request itself fails (403, 404, etc), the code should still continue because the parameters seem "good" enough. // Keep in mind that GetBucketVersioning requires "owner" to really succeed, so it can't be used to check the existence. // Not using "BucketExists (HeadBucket)" because it doesn't include detailed failure reasons. - err = getBucketVersioning(ctx, minioClient, config.Bucket) + err = getBucketVersioning(initCtx, minioClient, config.Bucket) if err != nil { errResp, ok := err.(minio.ErrorResponse) if !ok { @@ -125,13 +130,13 @@ func NewMinioStorage(ctx context.Context, cfg *setting.Storage) (ObjectStorage, } // Check to see if we already own this bucket - exists, err := minioClient.BucketExists(ctx, config.Bucket) + exists, err := minioClient.BucketExists(initCtx, config.Bucket) if err != nil { return nil, convertMinioErr(err) } if !exists { - if err := minioClient.MakeBucket(ctx, config.Bucket, minio.MakeBucketOptions{ + if err := minioClient.MakeBucket(initCtx, config.Bucket, minio.MakeBucketOptions{ Region: config.Location, }); err != nil { return nil, convertMinioErr(err) diff --git a/modules/storage/minio_test.go b/modules/storage/minio_test.go index ec1b2fc77a..18fe91edfb 100644 --- a/modules/storage/minio_test.go +++ b/modules/storage/minio_test.go @@ -9,8 +9,10 @@ import ( "net/http/httptest" "os" "testing" + "time" "forgejo.org/modules/setting" + "forgejo.org/modules/test" "github.com/minio/minio-go/v7" "github.com/stretchr/testify/assert" @@ -217,3 +219,41 @@ func TestMinioCredentials(t *testing.T) { }) }) } + +func TestNewMinioStorageInitializationTimeout(t *testing.T) { + defer test.MockVariableValue(&getBucketVersioning, func(ctx context.Context, minioClient *minio.Client, bucket string) error { + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(1 * time.Millisecond): + return minio.ErrorResponse{ + StatusCode: http.StatusBadRequest, + Code: "TestError", + Message: "Mocked error for testing", + } + } + })() + + settings := &setting.Storage{ + MinioConfig: setting.MinioStorageConfig{ + Endpoint: "localhost", + AccessKeyID: "123456", + SecretAccessKey: "12345678", + Bucket: "bucket", + Location: "us-east-1", + }, + } + + // Verify that we reach `getBucketVersioning` and return the error from our mock. + storage, err := NewMinioStorage(t.Context(), settings) + require.ErrorContains(t, err, "Mocked error for testing") + assert.Nil(t, storage) + + defer test.MockVariableValue(&initializationTimeout, 1*time.Nanosecond)() + + // Now that the timeout is super low, verify that we get a context deadline exceeded error from our mock. + storage, err = NewMinioStorage(t.Context(), settings) + require.Error(t, err) + require.ErrorIs(t, err, context.DeadlineExceeded, "err must be a context deadline exceeded error, but was %v", err) + assert.Nil(t, storage) +} From 100ddf45a74e079f51f43f17b98f3904d4d8d04a Mon Sep 17 00:00:00 2001 From: zokki Date: Sun, 17 Aug 2025 12:18:09 +0200 Subject: [PATCH 56/80] [v12.0/forgejo] fix: redirect from /{username}/{reponame}/pulls/{index} to issue if index is a issue (#8876) **Backport:** !8874 conflict resolved by accepting the incomming hunk and removing TestIssueTimelineLabels-func Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8876 Reviewed-by: Earl Warren Reviewed-by: floss4good Co-authored-by: zokki Co-committed-by: zokki --- routers/web/repo/pull.go | 2 +- tests/integration/issue_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 1e3eda9b61..87a7d2d5cd 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -360,7 +360,7 @@ func getPullInfo(ctx *context.Context) (issue *issues_model.Issue, ok bool) { ctx.Data["Issue"] = issue if !issue.IsPull { - ctx.NotFound("ViewPullCommits", nil) + ctx.Redirect(issue.Link()) return nil, false } diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 2b50be48a4..368897f461 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -1526,3 +1526,35 @@ func TestIssuePostersSearch(t *testing.T) { assert.EqualValues(t, 1, data.Results[0].UserID) }) } + +func TestIssueAndPullRedirect(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + req := NewRequest(t, "GET", "/user2/repo1/issues/1") + MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", "/user2/repo1/pulls/2") + MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", "/user2/repo1/pulls/1") + resp := MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/user2/repo1/issues/1", resp.Header().Get("Location")) + + req = NewRequest(t, "GET", "/user2/repo1/pulls/1/commits") + resp = MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/user2/repo1/issues/1", resp.Header().Get("Location")) + + req = NewRequest(t, "GET", "/user2/repo1/pulls/1/files") + resp = MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/user2/repo1/issues/1", resp.Header().Get("Location")) + + req = NewRequest(t, "GET", "/user2/repo1/issues/2") + resp = MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/user2/repo1/pulls/2", resp.Header().Get("Location")) + + req = NewRequest(t, "GET", "/user2/repo1/issues/9999999") + MakeRequest(t, req, http.StatusNotFound) + + req = NewRequest(t, "GET", "/user2/repo1/pulls/9999999") + MakeRequest(t, req, http.StatusNotFound) +} From cd35473212985d3f6b61b606d739510c60a8a7dc Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Tue, 19 Aug 2025 01:43:43 +0200 Subject: [PATCH 57/80] [v12.0/forgejo] a corrupted Forgejo Actions scheduled workflow is disabled (#8944) **Backport: https://codeberg.org/forgejo/forgejo/pulls/8942** The following errors are specific to the scheduled workflow being handled. They do not imply the remaining scheduled workflows cannot be scheduled successfully. - Failure to create a scheduled task which is most likely caused by an invalid YAML file. - Failure to parse the scheduling specs which can be caused by a number of formating errors. Instead of returning on error, the corrupted workflow is disabled. Also display more informative error messages so that the failed workflow can be identified from the logged error. (cherry picked from commit ab3cf7ddcf91907071d94f45d9704ad87deff3e4) ``` Conflicts: services/actions/schedule_tasks.go trivial context conflict ``` Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8944 Reviewed-by: Otto Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- models/repo/repo_unit.go | 5 +- .../action_schedule.yml | 41 ++++++++++++++ .../action_schedule_spec.yml | 15 ++++++ services/actions/schedule_tasks.go | 30 ++++++++--- services/actions/schedule_tasks_test.go | 54 +++++++++++++++++++ 5 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 services/actions/TestServiceActions_startTask/action_schedule.yml create mode 100644 services/actions/TestServiceActions_startTask/action_schedule_spec.yml diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index e50f79e945..aa6f2fa0ae 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -336,5 +336,8 @@ func getUnitsByRepoID(ctx context.Context, repoID int64) (units []*RepoUnit, err // UpdateRepoUnit updates the provided repo unit func UpdateRepoUnit(ctx context.Context, unit *RepoUnit) error { _, err := db.GetEngine(ctx).ID(unit.ID).Update(unit) - return err + if err != nil { + return fmt.Errorf("UpdateRepoUnit: %v", err) + } + return nil } diff --git a/services/actions/TestServiceActions_startTask/action_schedule.yml b/services/actions/TestServiceActions_startTask/action_schedule.yml new file mode 100644 index 0000000000..d0e7234475 --- /dev/null +++ b/services/actions/TestServiceActions_startTask/action_schedule.yml @@ -0,0 +1,41 @@ +# A corrupted cron spec with a valid schedule workflow +- + id: 1 + title: schedule_title1 + specs: + - '* * * * *' + repo_id: 4 + owner_id: 2 + workflow_id: 'workflow1.yml' + trigger_user_id: 2 + ref: main + commit_sha: shashasha + event: "schedule" + event_payload: "fakepayload" + content: | + jobs: + job2: + runs-on: ubuntu-latest + steps: + - run: true + +# A valid cron spec with a corrupted schedule workflow +- + id: 2 + title: schedule_title2 + specs: + - '* * * * *' + repo_id: 4 + owner_id: 2 + workflow_id: 'workflow2.yml' + trigger_user_id: 2 + ref: main + commit_sha: shashasha + event: "schedule" + event_payload: "fakepayload" + content: | + jobs: + job2: { invalid yaml + runs-on: ubuntu-latest + steps: + - run: true diff --git a/services/actions/TestServiceActions_startTask/action_schedule_spec.yml b/services/actions/TestServiceActions_startTask/action_schedule_spec.yml new file mode 100644 index 0000000000..7bcc78f010 --- /dev/null +++ b/services/actions/TestServiceActions_startTask/action_schedule_spec.yml @@ -0,0 +1,15 @@ +# A corrupted cron spec with a valid schedule workflow +- + id: 1 + repo_id: 4 + schedule_id: 1 + next: 1 + spec: 'corrupted * *' + +# A valid cron spec with a corrupted schedule workflow +- + id: 2 + repo_id: 4 + schedule_id: 2 + next: 1 + spec: '* * * * *' diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go index b422057d6f..ec38f5eb8a 100644 --- a/services/actions/schedule_tasks.go +++ b/services/actions/schedule_tasks.go @@ -20,6 +20,7 @@ import ( "github.com/nektos/act/pkg/jobparser" act_model "github.com/nektos/act/pkg/model" + "github.com/robfig/cron/v3" "xorm.io/builder" ) @@ -83,20 +84,33 @@ func startTasks(ctx context.Context) error { } return fmt.Errorf("GetUnit: %w", err) } - if cfg.ActionsConfig().IsWorkflowDisabled(row.Schedule.WorkflowID) { + actionConfig := cfg.ActionsConfig() + if actionConfig.IsWorkflowDisabled(row.Schedule.WorkflowID) { continue } - if err := CreateScheduleTask(ctx, row.Schedule); err != nil { - log.Error("CreateScheduleTask: %v", err) - return err + createAndSchedule := func(row *actions_model.ActionScheduleSpec) (cron.Schedule, error) { + if err := CreateScheduleTask(ctx, row.Schedule); err != nil { + return nil, fmt.Errorf("CreateScheduleTask: %v", err) + } + + // Parse the spec + schedule, err := row.Parse() + if err != nil { + return nil, fmt.Errorf("Parse(Spec=%v): %v", row.Spec, err) + } + return schedule, nil } - // Parse the spec - schedule, err := row.Parse() + schedule, err := createAndSchedule(row) if err != nil { - log.Error("Parse: %v", err) - return err + log.Error("RepoID=%v WorkflowID=%v: %v", row.Schedule.RepoID, row.Schedule.WorkflowID, err) + actionConfig.DisableWorkflow(row.Schedule.WorkflowID) + if err := repo_model.UpdateRepoUnit(ctx, cfg); err != nil { + log.Error("RepoID=%v WorkflowID=%v: CreateScheduleTask: %v", row.Schedule.RepoID, row.Schedule.WorkflowID, err) + return err + } + continue } // Update the spec's next run time and previous run time diff --git a/services/actions/schedule_tasks_test.go b/services/actions/schedule_tasks_test.go index 7073985252..31ed5ec813 100644 --- a/services/actions/schedule_tasks_test.go +++ b/services/actions/schedule_tasks_test.go @@ -7,7 +7,9 @@ import ( "testing" actions_model "forgejo.org/models/actions" + "forgejo.org/models/db" repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" "forgejo.org/models/unittest" webhook_module "forgejo.org/modules/webhook" @@ -15,6 +17,58 @@ import ( "github.com/stretchr/testify/require" ) +func TestServiceActions_startTask(t *testing.T) { + defer unittest.OverrideFixtures("services/actions/TestServiceActions_startTask")() + require.NoError(t, unittest.PrepareTestDatabase()) + + // Load fixtures that are corrupted and create one valid scheduled workflow + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + + workflowID := "some.yml" + schedules := []*actions_model.ActionSchedule{ + { + Title: "scheduletitle1", + RepoID: repo.ID, + OwnerID: repo.OwnerID, + WorkflowID: workflowID, + TriggerUserID: repo.OwnerID, + Ref: "branch", + CommitSHA: "fakeSHA", + Event: webhook_module.HookEventSchedule, + EventPayload: "fakepayload", + Specs: []string{"* * * * *"}, + Content: []byte( + ` +jobs: + job2: + runs-on: ubuntu-latest + steps: + - run: true +`), + }, + } + + require.Equal(t, 2, unittest.GetCount(t, actions_model.ActionScheduleSpec{})) + require.NoError(t, actions_model.CreateScheduleTask(t.Context(), schedules)) + require.Equal(t, 3, unittest.GetCount(t, actions_model.ActionScheduleSpec{})) + _, err := db.GetEngine(db.DefaultContext).Exec("UPDATE `action_schedule_spec` SET next = 1") + require.NoError(t, err) + + // After running startTasks an ActionRun row is created for the valid scheduled workflow + require.Empty(t, unittest.GetCount(t, actions_model.ActionRun{WorkflowID: workflowID})) + require.NoError(t, startTasks(t.Context())) + require.NotEmpty(t, unittest.GetCount(t, actions_model.ActionRun{WorkflowID: workflowID})) + + // The invalid workflows loaded from the fixtures are disabled + repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + actionUnit, err := repo.GetUnit(t.Context(), unit.TypeActions) + require.NoError(t, err) + actionConfig := actionUnit.ActionsConfig() + assert.True(t, actionConfig.IsWorkflowDisabled("workflow2.yml")) + assert.True(t, actionConfig.IsWorkflowDisabled("workflow1.yml")) + assert.False(t, actionConfig.IsWorkflowDisabled("some.yml")) +} + func TestCreateScheduleTask(t *testing.T) { require.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2, OwnerID: 2}) From 306fc24036cad459f7f83112ba632ac60bbf5e08 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Fri, 22 Aug 2025 15:07:54 +0500 Subject: [PATCH 58/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate Translation updates that were relevant to v12 branch were picked from this commit: e8acd8afd3a06726ad5c29a33d5d6ef7ae6b2dab Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Juno Takano Co-authored-by: Lzebulon Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: Zalexanninev15 Co-authored-by: adf19 Co-authored-by: darkswordreams Co-authored-by: earl-warren Co-authored-by: justbispo Co-authored-by: oatbiscuits Co-authored-by: xtex --- options/locale/locale_ar.ini | 351 +++++++++++++++++++++++--- options/locale/locale_cs-CZ.ini | 4 +- options/locale/locale_uk-UA.ini | 96 +++++-- options/locale_next/locale_cs-CZ.json | 24 +- options/locale_next/locale_hi.json | 60 ++++- 5 files changed, 459 insertions(+), 76 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index ca74a477ce..dab6787a53 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -312,31 +312,31 @@ blocked_since = محظور منذ %s comment_type_group_milestone = الأهداف ui = السمة email_notifications.disable = عطّل إشعارات البريد الإلكتروني -passcode_invalid = رمز الدخول خطأ. حاول مرة أخرى. +passcode_invalid = رمز الدخول خطأ. حاول مجدداً. openid_deletion = أزل عنوان OpenID activate_email = أرسل التفعيل uploaded_avatar_not_a_image = الملف المرفوع ليس صورة. theme_update_error = السمة المختارة غير موجودة. -twofa_disabled = عُطِّل الاستيثاق الثنائي. +twofa_disabled = تم تعطيل المصادقة الثنائية. theme_desc = ستكون هذه السمة المبدئية لك عبر الموقع. new_password = كلمة المرور الجديدة -twofa_disable_desc = تعطيل الاستيثاق الثنائي سيجعل حسابك أقل أمانًا. أتريد الاستمرار؟ +twofa_disable_desc = تعطيل المصادقة الثنائية سيجعل حسابك أقل أماناً. الاستمرار؟ manage_themes = الموضوع الافتراضي -delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عنها بعد ذلك. -cancel = ألغ -repos_none = ليس لديك أي مستودع. +delete_prompt = هذه العملية ستحذف حسابك إلى الأبد. لا يمكن التراجع عن ذلك. +cancel = إلغاء +repos_none = لا تملك أية مستودع. twofa_desc = لحماية حسابك من سرقة كلمة المرور، يمكنك استخدام هاتف ذكي أو جهاز آخر لاستلام كلمة مرور مؤقتة ذات استخدام واحد ("TOTP"). -email_notifications.submit = اضبط تفضيلات البريد الإلكتروني +email_notifications.submit = ضبط تفضيلات البريد الإلكتروني update_user_avatar_success = حُدِّثت صورة المستخدم الرمزية. activations_pending = في انتظار التفعيل -language = اللغة +language = اللّغة primary = الأساسي update_avatar_success = حُدِّثت صورتك الرمزية. keep_email_private = أخفِ عنوان البريد delete_current_avatar = احذف الصورة الرمزية الحالية avatar = صورة رمزية email_preference_set_success = حُدِّثت تفضيلات البريد الإلكتروني. -scan_this_image = امسح هذه الصورة بتطبيق الاستيثاق الذي تستخدمه: +scan_this_image = امسح هذه الصورة بتطبيق المصادقة الذي تستخدمه: orgs_none = لست عضوًا في أي منظمة. delete_email = أزله theme_update_success = حُدِّثت السمة. @@ -363,14 +363,14 @@ openid_deletion_success = أزيل عنوان OpenID. add_email_success = أضيف عنوان البريد الجديد. enable_custom_avatar = استخدم صورة رمزية مخصصة update_avatar = حدّث الصورة الرمزية -twofa_disable = تعطيل الاستيثاق الثنائي +twofa_disable = تعطيل المصادقة الثنائية retype_new_password = تأكيد كلمة المرور الجديدة manage_emails = أدر عناوين البريد الإلكتروني then_enter_passcode = وأدخل رمز الدخول الظاهر في التطبيق: update_password = حدّث كلمة المرور continue = استمر emails = عناوين البريد الإلكتروني -confirm_delete_account = أكُد الحذف +confirm_delete_account = تأكيد الحذف change_password_success = حُدِّثت كلمة مرورك. سجّل الدخول بكلمة مرورك الجديدة من الآن فصاعدا. email_deletion_desc = سيُزال عنوان البريد هذا مع كل المعلومات المرتطبة به من حسابك. لكن ستبقى إيداعات Git المودعة به بلا تغيير. أتريد الاستمرار؟ or_enter_secret = أو أدخل السر: %s @@ -389,14 +389,14 @@ saved_successfully = حُدِّثت إعداداتك بنجاح. update_theme = حدِّث السمة access_token_deletion_confirm_action = احذف website = الموقع الإلكتروني -delete_token = احذف +delete_token = حذف hidden_comment_types.ref_tooltip = التعليقات التي تقول أن هذه المسألة قد أشير إليها في مسألة أخرى أو إيداع أو غير ذلك… update_language_success = تم تحديث اللغة. privacy = الخصوصية comment_type_group_label = التصنيفات account_link = الحسابات المرتبطة comment_type_group_assignee = المكلفون -update_language = حدِّث اللغة +update_language = تغيير اللغة organization = المنظمات update_language_not_found = اللغة "%s" غير متاحة. update_profile_success = تم تحديث ملفك الشخصي. @@ -424,7 +424,7 @@ can_write_info = كتابة delete = احذف الحساب oauth2_application_name = اسم التطبيق key_state_desc = هذا المفتاح أستُعمل خلال آخر 7 أيام -webauthn_delete_key = أزِل مفتاح الأمان +webauthn_delete_key = إزالة مفتاح الأمان valid_forever = صالح للأبد can_read_info = قراءة create_oauth2_application_button = أنشئ تطبيقا @@ -436,14 +436,14 @@ select_permissions = أختر التصاريح added_on = مُضاف في %s show_openid = أظهر على الملف الشخصي hide_openid = أخفي من الملف الشخصي -webauthn_delete_key_desc = إذا أزلت مفتاح الأمان، فلن تتمكن من تسجيل الدخول باستخدامه. اكمل؟ +webauthn_delete_key_desc = إذا أزلت مفتاح الأمان، فلن تتمكن من تسجيل الدخول باستخدامه. المتابعة؟ permissions_list = التصاريح: webauthn_key_loss_warning = إذا فقدت مفاتيح الأمان الخاصة بك، فسوف تفقد الوصول إلى حسابك. -hooks.desc = أضف خطاطيف ويب تُطلق لكل مستودعاتك. +hooks.desc = إضافة خطاطيف ويب سيتم تشغيلها لـ جميع المستودعات التي تمتلكها. keep_activity_private_popup = يجعل النشاط مرأياً لك وللمديرين فقط keep_email_private_popup = سيؤدي هذا إلى إخفاء عنوان بريدك الإلكتروني من ملفك الشخصي، وكذلك عند تقديم طلب سحب أو تحرير ملف باستخدام واجهة الويب. لن يتم تعديل الالتزامات المدفوعة. استخدم %s في الإيداعات لربطها بحسابك. ssh_key_name_used = هناك مفتاح SSH بنفس الاسم موجود بالفعل على حسابك. -authorized_oauth2_applications = تطبيقات OAuth2 المأذونة +authorized_oauth2_applications = تطبيقات OAuth2 المأذون لها uid = المعرّف الرمزي manage_openid = عناوين OpenID webauthn = استيثاق ثنائي (مفاتيح الأمان) @@ -468,11 +468,11 @@ key_content_gpg_placeholder = يبدأ بـ '-----BEGIN PGP PUBLIC KEY BLOCK---- add_email_confirmation_sent = بريد تفعيل جديد تم إرساله إلى "%s". يُرجى التحقق من البريد الوارد خلال %s لتأكيد عنوان البريد الإلكتروني. ssh_desc = مفاتيح SSH العمومية هذه مرتبطة بحسابك. وتسمح المفاتيح الخصوصية المرافقة بالوصول الكامل إلى مستودعاتك. ويمكن استعمال مفاتيح SSH الموثَّقة لتوثيق إيداعات جت الموقَّعة بمفاتيح SSH. ssh_gpg_keys = مفاتيح SSH / GPG -authorized_oauth2_applications_description = لقد منحتَ إمكانية الوصول إلى حسابك الشخصي على فورجيو لهذه التطبيقات من تطبيقات خارجية. الرجاء إلغاء وصول التطبيقات التي لم تعد بحاجة إليها. +authorized_oauth2_applications_description = لقد منحت حق الوصول إلى حسابك الشخصي في Forgejo لهذه التطبيقات الخارجية. يرجى إلغاء الوصول للتطبيقات التي لم تعد قيد الاستخدام. ssh_key_been_used = هذا المفتاح الـSSH تم إضافته بالفعل إلى هذا الخادم. password_change_disabled = المستخدمين غير المحليين لا يمكنهم تغيير كلمة مرورهم عن طريق واجهة ويب فورجيو. token_state_desc = هذا الرمز استخدم خلال آخر 7 أيام -delete_key = أزله +delete_key = إزالة ssh_invalid_token_signature = مفتاح SSH المزود، والتوقيع والرمز لا يتطابقوا أو الرمز قديم. ssh_token_help = يمكنك توليد توقيع باستخدام: gpg_key_verify = تحقق @@ -486,10 +486,10 @@ unbind = الغ الربط verify_ssh_key_success = تم التحقق من مفتاح SSH "%s". gpg_token_required = يجب أن تقدم توقيعاً للرمز التالي ssh_key_verified_long = تم التحقق من المفتاح مع رمز ويمكن استخدامه للتحقق من الإيداعات المتطابقة مع أي عناوين البريد المفعلة لهذا المستخدم. -gpg_key_deletion = أزل مفتاح GPG +gpg_key_deletion = إزالة مفتاح GPG gpg_token_help = يمكنك توليد توقيع باستخدام: -ssh_key_deletion = أزل مفتاح SSH -ssh_token = رمز +ssh_key_deletion = إزالة مفتاح SSH +ssh_token = رمز فريد ssh_disabled = SSH مُعطل gpg_key_verified_long = تم التحقق من المفتاح بواسطة رمز ويمكن استخدامه للتحقق من إيداعات متطابقة لعناوين البريد المفعلة لهذا المستخدم بالإضافة إلى أي هويات متطابقة لهذا المفتاح. change_username_redirect_prompt = اسم المستخدم القديم سوف يعاد توجيهه حتى يطالب به شخص آخر. @@ -497,7 +497,7 @@ add_key_success = تم إضافة مفتاح SSH "%s". key_name = اسم المفتاح comment_type_group_time_tracking = تتبع الوقت gpg_invalid_token_signature = مفتاح GPG المزود، والتوقيع والرمز لا يتطابقوا أو الرمز قديم. -ssh_key_verified = مفتاح مُتحقق منه +ssh_key_verified = مفتاح تم التحقق منه ssh_key_deletion_success = تم إزالة مفتاح SSH. key_signature_ssh_placeholder = يبدأ بـ'-----BEGIN SSH SIGNATURE-----' gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig @@ -511,7 +511,7 @@ gpg_key_deletion_desc = إزالة مفتاح GPG يُلغي تحقق الإيد permission_read = القراءة ssh_token_signature = توقيع SSH مصفح ssh_key_deletion_desc = إزالة مفتاح SSH يلغي وصوله إلى حسابك. أكمل؟ -revoke_key = أسحب +revoke_key = سحب gpg_token = رمز gpg_key_matched_identities_long = الهويات المدمجة في هذا المفتاح تطابق عناوين البريد الإلكتروني المفعلة التالية لهذا المستخدم. ويمكن التحقق من صحة الإيداعات المطابقة لهذه العناوين البريدية مع هذا المفتاح. key_content = المحتوى @@ -525,10 +525,122 @@ remove_oauth2_application_success = أُزيل التطبيق. update_oauth2_application_success = لقد حدّثت بنجاح تطبيق OAuth2. oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع كل رابط في سطر وحده. remove_account_link = أزل الحساب المربوط -remove_account_link_success = أُزيل الحساب المربوط. +remove_account_link_success = أُزيل الحساب المرتبط. quota = كوتا +storage_overview = نظرة عامة على التخزين +pronouns = الضمائر +pronouns_unspecified = غير محدد +change_username_redirect_prompt.with_cooldown.one = سيصبح اسم المستخدم القديم متاحًا للجميع بعد فترة تباطؤ تبلغ %[1]d يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +change_username_redirect_prompt.with_cooldown.few = سيصبح اسم المستخدم القديم متاحًا للجميع بعد فترة تباطؤ تبلغ يومًا. لا يزال بإمكانك استعادة اسم المستخدم القديم خلال فترة التهدئة. +language.title = اللغة الافتراضية +language.description = سيتم حفظ هذه اللغة في حسابك واستخدامها كلغة افتراضية بعد تسجيل الدخول. +language.localization_project = "ساعدنا في ترجمة Forgejo إلى لغتك! المزيد من المعلومات. +hints = تلميحات +additional_repo_units_hint = اقتراح تفعيل وحدات المستودعات الإضافية +additional_repo_units_hint_description = اعرض تلميح "تفعيل المزيد" للمستودعات التي لم يتم تفعيل جميع الوحدات المتاحة بها. +update_hints = حدِّث التلميحات +update_hints_success = تم تحديث التلميحات. +keep_activity_private.description = سيكون نشاطك العام مرئيًا لك ولمشرفي المثيل فقط. +change_password = غيّر كلمة المرور +keep_pronouns_private = إظهار الضمائر للمستخدمين الذين تمت مصادقتهم فقط +keep_pronouns_private.description = سيؤدي ذلك إلى إخفاء ضمائرك عن الزوار الذين لم يقوموا بتسجيل الدخول. +manage_ssh_principals = إدارة مدراء شهادات SSH الرئيسية +principal_desc = هذه الهُويات الرئيسية لشهادات SSH مرتبطة بحسابك وتتيح وصولاً كاملاً إلى مستودعاتك. +ssh_helper = هل تحتاج مساعدة؟ اطلع على الدليل لـ إنشاء مفاتيح SSH الخاصة بك أو لحل المشكلات الشائعة التي قد تواجهها عند استخدام SSH. +gpg_helper = تحتاج لمساعدة؟ اطّلع على الدليل حول GPG. +add_new_principal = إضافة هوية رئيسية +ssh_principal_been_used = تمت إضافة هذه الهوية الرئيسية إلى الخادم مسبقاً. +gpg_key_matched_identities = الهويات المتطابقة: +subkeys = المفاتيح الفرعية +add_principal_success = تمت إضافة الهوية الرئيسية لشهادة "SSH "%s. +ssh_principal_deletion = إزالة الهوية الرئيسية لشهادة SSH +ssh_principal_deletion_desc = إزالة هوية رئيسية لشهادة SSH ستسحب صلاحية وصولها إلى حسابك. تريد المتابعة؟ +ssh_principal_deletion_success = تم إزالة الهوية. +no_activity = لا يوجد نشاط حديث +principal_state_desc = استخدمت هذه الهوية في آخر 7 أيام +ssh_signonly = SSH معطّل حاليًا، لذا تُستخدم هذه المفاتيح فقط للتحقق من توقيع الإيداع. +ssh_externally_managed = يتم إدارة مفتاح SSH هذا خارجيًا لهذا المستخدم +manage_access_token = رموز الوصول الفريدة +generate_new_token = توليد رمز جديد +tokens_desc = تمنح هذه الرموز الفريدة إمكانية الوصول إلى حسابك باستخدام واجهة برمجة تطبيقات Forgejo. +token_name = اسم الرمز الفريد +generate_token = توليد رمز فريد +generate_token_success = تم إنشاء الرمز الفريد الجديد الخاص بك. انسخه الآن لأنه لن يظهر مرة أخرى. +generate_token_name_duplicate = اسم التطبيق %s مُستخدم مسبقًا. يُرجى استخدام اسم جديد. +access_token_deletion = حذف رمز الوصول الفريد +access_token_deletion_desc = حذف الرمز الفريد سيسحب صلاحية الوصول إلى حسابك من التطبيقات التي تستخدمه. لا يمكن التراجع عن هذا الإجراء. تريد المتابعة؟ +delete_token_success = تم حذف الرمز الفريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك. +regenerate_token = إعادة التوليد +access_token_regeneration = إعادة توليد رمز وصول فريد +access_token_regeneration_desc = سيؤدي إعادة إنشاء رمز فريد إلى إبطال الوصول إلى حسابك للتطبيقات التي تستخدمه. لا يمكن التراجع عن ذلك. المتابعة؟ +regenerate_token_success = تم إعادة إنشاء الرمز الغريد. لم يعد بإمكان التطبيقات التي تستخدمه الوصول إلى حسابك ويجب تحديثها بالرمز الجديد. +repo_and_org_access = الوصول إلى المستودع والمنظمة +permission_no_access = لا وصول +permission_write = قراءة وكتابة +access_token_desc = تقتصر صلاحيات الرمز المحددة على مسارات واجهة البرمجة (API) المقابلة فقط. اطلع على الوثائق لمزيد من المعلومات. +at_least_one_permission = يجب عليك تحديد صلاحية واحدة على الأقل لإنشاء رمز فريد +oauth2_applications_desc = تمكّن تطبيقات OAuth2 تطبيقات الطرف الثالث من مصادقة المستخدمين بأمان في مثيل Forgejo هذا. +remove_oauth2_application_desc = ستؤدي إزالة تطبيق OAuth2 إلى إبطال الوصول إلى جميع رموز الوصول الفريدة الموقعة. المتابعة؟ +oauth2_confidential_client = العميل السري. حدد للتطبيقات التي تحافظ على السرية، مثل تطبيقات الويب. لا تحدد للتطبيقات الأصلية بما في ذلك تطبيقات سطح المكتب وتطبيقات الأجهزة المحمولة. +oauth2_client_id = معرف العميل +oauth2_client_secret = سر العميل +oauth2_regenerate_secret = تجديد السر +oauth2_regenerate_secret_hint = فقدت سرك؟ +oauth2_client_secret_hint = لن يظهر السر مرة أخرى بعد مغادرة هذه الصفحة أو تحديثها. يرجى التأكد من أنك قمت بحفظه. +oauth2_application_create_description = ‪تمنح تطبيقات OAuth2 تطبيقات الطرف الثالث حق الوصول إلى حسابات المستخدمين على هذا المثيل. +oauth2_application_remove_description = ستؤدي إزالة تطبيق OAuth2 إلى منعه من الوصول إلى حسابات المستخدمين المصرح لهم على هذا المثيل. المتابعة؟ +oauth2_application_locked = يقوم Forgejo بالتسجيل المسبق لبعض تطبيقات OAuth2 عند بدء التشغيل إذا تم تمكينها في التكوين. لمنع السلوك غير المتوقع، لا يمكن تحريرها أو إزالتها. يرجى الرجوع إلى وثائق OAuth2 لمزيد من المعلومات. +revoke_oauth2_grant = سحب صلاحية الوصول +revoke_oauth2_grant_description = سيؤدي إبطال الوصول لهذا التطبيق التابع لجهة خارجية إلى منع هذا التطبيق من الوصول إلى بياناتك. أنت متأكد؟ +revoke_oauth2_grant_success = تم سحب صلاحية الوصول بنجاح. +twofa_recovery_tip = إذا فقدت جهازك، ستتمكن من استخدام مفتاح الاسترداد للاستخدام مرة واحدة لاستعادة الوصول إلى حسابك. +twofa_is_enrolled = حسابك مسجّل حاليًا في المصادقة الثنائية. +twofa_not_enrolled = حسابك غير مسجّل حالياً في المصادقة الثنائية. +twofa_scratch_token_regenerate = إعادة إنشاء مفتاح الاسترداد للاستخدام مرة واحدة +twofa_scratch_token_regenerated = مفتاح الاسترداد للاستخدام مرة واحدة هو %s الآن. قم بتخزينه في مكان آمن، فلن يتم عرضه مجدداً. +twofa_enroll = التسجيل في المصادقة الثنائية +regenerate_scratch_token_desc = إذا فقدت مفتاح الاسترداد الخاص بك في غير محله أو استخدمته بالفعل لتسجيل الدخول، يمكنك إعادة تعيينه هنا. +twofa_enrolled = تم تسجيل حسابك بنجاح. قم بتخزين مفتاح الاسترداد للاستخدام لمرة واحدة (%s) في مكان آمن، فلن يتم عرضه مجدداً. +twofa_failed_get_secret = إخفاق في الحصول على سر. +webauthn_desc = مفاتيح الأمان هي أجهزة فعلية تحوي على مفاتيح تشفير. يمكن استخدامها للتحقق بخطوتين. يجب أن تدعم مفاتيح الأمان معيار WebAuthn Authenticator. +webauthn_register_key = إضافة مفتاح تشفير +webauthn_nickname = الاسم المستعار +webauthn_alternative_tip = قد ترغب في تكوين أسلوب مصادقة إضافي. +manage_account_links = الحسابات المرتبطة +manage_account_links_desc = هذه الحسابات الخارجية مرتبطة بحسابك في Forgejo. +remove_account_link_desc = ستؤدي إزالة حساب مرتبط إلى إلغاء وصوله إلى حساب Forgejo الخاص بك. المتابعة؟ +delete_with_all_comments = حسابك أصغر من %s. لتجنب التعليقات الوهمية، سيتم حذف جميع تعليقات المشكلة/المسؤولية الشخصية معها. +delete_account_desc = هل أنت متأكد من رغبتك في حذف حساب المستخدم هذا نهائيًا؟ +email_notifications.onmention = البريد الإلكتروني فقط عند الإشارة +email_notifications.andyourown = والإشعارات الخاصة بك +visibility = رؤية المستخدم +visibility.public = عام +visibility.public_tooltip = مرئي للجميع +visibility.limited = محدود +visibility.limited_tooltip = مرئية فقط للمستخدمين الذين قاموا بتسجيل الدخول +visibility.private = خاص +visibility.private_tooltip = مرئي فقط لأعضاء المؤسسات التي انضممت إليها +user_block_yourself = لا يمكنك حظر نفسك. +quota.applies_to_user = تنطبق قواعد الحصص التالية على حسابك +quota.rule.exceeded = تم تجاوزه +quota.rule.exceeded.helper = لقد تجاوز الحجم الإجمالي للكائنات لهذه القاعدة الحصة النسبية. +quota.rule.no_limit = غير محدود +quota.sizes.all = الكل +quota.sizes.repos.all = المستودعات +quota.sizes.repos.public = مستودعات عامة +quota.sizes.repos.private = مستودعات خاصة +quota.sizes.git.all = محتوى Git +quota.sizes.git.lfs = Git LFS +quota.sizes.assets.all = الأصول +quota.sizes.assets.attachments.all = المرفقات +quota.sizes.assets.attachments.issues = إصدار المرفقات +quota.sizes.assets.attachments.releases = تحرير المرفقات +quota.sizes.assets.artifacts = التحف الفنية +quota.sizes.assets.packages.all = الحزم +quota.sizes.wiki = الموسوعة + [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. settings.delete_prompt = ستزال المنظمة إلى الأبد. لا يمكن التراجع عنها بعد ذلك! @@ -636,7 +748,7 @@ pulls.blocked_by_user = لا يمكنك أن ترسل طلب سحب في هذا migrate.migrating_milestones = ترحيل الأهداف migrate_items_milestones = أهداف repo_size = حجم المستودع -object_format = صيغة الكائنات +object_format = تنسيق الكائنات use_template = استخدم هذا القالب migrate_items_merge_requests = طلبات الدمج repo_name = اسم المستودع @@ -644,11 +756,11 @@ template = القالب projects.modify = عدّل المشروع tree_path_not_found_commit = المسار %[1]s غير موجود في الإيداع %[2]s repo_lang = اللغة -fork_repo = اشتق المستودع +fork_repo = اشتقاق المستودع fork_no_valid_owners = لا يمكن اشتقاق هذا المستودع لعدم وجود مالك صالح. license = الترخيص fork_branch = الفرع الذي سيُستنسخ إلى الاشتقاق -template_helper = اجعل المستودع قالبا +template_helper = اجعل المستودع قالباً owner = المالك projects.deletion_success = تم حذف المشروع. projects.deletion = احذف المشروع @@ -658,14 +770,14 @@ projects.edit = عدّل المشروع template.avatar = الصورة الرمزية migrate_items_wiki = الموسوعة repo_desc = الوصف -template_select = اختر قالبا. +template_select = اختر قالبا repo_name_helper = الأسماء الحسنة للمستودعات تستخدم كلمات مفتاحية قصيرة وسهلة التذكر وفريدة. -default_branch = الفرع المبدئي +default_branch = الفرع الافتراضي all_branches = كل الفروع -migrate_items_issues = المسائل +migrate_items_issues = البلاغات projects.deletion_desc = حذف مشروع يحذف كل المسائل المرتبطة به. أتريد الاستمرار؟ -repo_desc_helper = أدخل وصفا قصيرا (اختياريا) -create_repo = أنشئ مستودعا +repo_desc_helper = أدخل وصفاً قصيراً (اختياري) +create_repo = إنشاء مستودع migrate_items_releases = الإصدارات already_forked = لقد اشتققت %s بالفعل license_helper = اختر ملف ترخيص. @@ -686,7 +798,7 @@ issues.remove_milestone_at = `أزال هذه المسألة من الهدف issues.filter_assginee_no_assignee = بلا مكلف issues.new.no_milestone = بلا هدف issues.new.projects = المشروعات -delete_preexisting_label = احذف +delete_preexisting_label = حذف issues.context.edit = عدّل branch.rename = غيّر اسم الفرع "%s" issue_labels = تصنيفات المسائل @@ -782,7 +894,7 @@ milestones.deletion_desc = حذف هدف يحذفه من كل المسائل ا issues.desc = نظّم إبلاغات العلل، والمهام، والأهداف. issues.choose.ignore_invalid_templates = أُهمِلت القوالب التالفة branch.renamed = غُيّر اسم الفرع %s إلى %s. -delete_preexisting = احذف الملفات الموجودة سابقا +delete_preexisting = حذف الملفات الموجودة مسبقاً branch.included_desc = هذا الفرع جزء من الفرع المبدئي trust_model_helper_collaborator_committer = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع issues.reopened_at = `أعاد فتح هذه المسألة %s` @@ -856,7 +968,7 @@ issues.reopen_comment_issue = علّق وأعد فتحها issues.dependency.add = أضف اعتمادية… issues.label_deletion_desc = حذف تصنيف يحذفه من كل المسائل، أتريد الاستمرار؟ labels = التصنيفات -delete_preexisting_content = احذف الملفات في %s +delete_preexisting_content = حذف الملفات في %s milestones.deletion = احذف الهدف issues.comment_pull_merged_at = دمج الإيداع %[1]s إلى %[2]s %[3]s issues.new.closed_milestone = الأهداف التامة @@ -944,7 +1056,7 @@ issues.filter_assignee = مكلف issues.open_title = حالية download_file = نزّل الملف issues.attachment.download = `انقر لتنزيل "%s"` -download_archive = نزّل المستودع +download_archive = تنزيل المستودع download_tar = نزّل TAR.GZ download_zip = نزّل ZIP releases.desc = تتبع إصدارات المشروع وتنزيلاته. @@ -1354,6 +1466,171 @@ pulls.merge_conflict = تعذر الدمج: حدث نزاع خلال الدمج. pulls.rebase_conflict = تعذر الدمج: حدث نزاع خلال إعادة تأسيس الإيداع: %[1]s. مساعدة: جرب طريقة أخرى pulls.has_merged = فشل: لقد تم دمج هذا الطلب، فلا يمكنك دمجه مجددا أو تغيير الفرع الهدف. +new_repo_helper = يحتوي المستودع على جميع ملفات المشروع، بما في ذلك سجل التعديلات. هل تستضيف واحدًا بالفعل على منصة أخرى؟ نقل المستودع. +new_from_template = استخدم قالباً +new_from_template_description = يمكنك تحديد قالب مستودع موجود على هذا المثيل وتطبيق إعداداته. +new_advanced = إعدادات مقتدمة +new_advanced_expand = انقر للتوسعة +owner_helper = قد لا تظهر بعض منتديات المجموعة في القائمة المنسدلة بسبب الحد الأقصى لعدد المستودعات. +size_format = %[1]s: %[2]s, %[3]s: %[4]s +visibility = الرؤية +visibility_description = فقط المالك أو أعضاء المؤسسة إذا كان لديهم حقوق، سيتمكنون من رؤيته. +visibility_fork_helper = (سيؤثر تغيير ذلك على رؤية جميع المشتقات.) +clone_helper = تحتاج مساعدة في الاستنساخ؟ زُر المساعدة. +fork_from = اشتق من +fork_visibility_helper = لا يمكن تغيير ظهور المستودع المشتّق. +open_with_editor = افتح بـ %s +generate_repo = توليد لمستودع +generate_from = التوليد من +repo_gitignore_helper = حدد قوالب .gitignore +repo_gitignore_helper_desc = اختر الملفات التي لا تريد تتبعها من قائمة القوالب الخاصة باللغات الشائعة. يتم تضمين القطع الأثرية النموذجية التي تم إنشاؤها بواسطة أدوات البناء الخاصة بكل لغة في .gitignore بشكل افتراضي. +license_helper_desc = تحدد الرخصة ما يمكن للآخرين فعله أو عدم فعله بشيفرة برمجيتك. لست متأكدًا من الرخصة المناسبة لمشروعك؟ طالع اختيار الرخصة. +readme = README +readme_helper = حدد قالب ملف README +readme_helper_desc = هذا هو المكان الذي يمكنك فيه كتابة وصف كامل لمشروعك. +auto_init = تهيئة المستودع +auto_init_description = ابدأ سجل Git بملف README، مع إمكانية إضافة ملفات الرخصة و.gitignore اختيارياً. +default_branch_label = افتراضي +default_branch_helper = الفرع الافتراضي هو الفرع الأساسي لطلبات السحب ،وعمليات إلإيداع. +mirror_prune = تنقية +mirror_prune_desc = إزالة مراجع التتبع عن بُعد القديمة +mirror_interval = الفاصل الزمني للمرآة (وحدات الوقت الصحيحة هي 'h' ،'m' ،'s'). 0 لتعطيل المزامنة الدورية. (الحد الأدنى: %s) +mirror_interval_invalid = الفاصل الزمني للمرآة غير صالح. +mirror_public_key = مفتاح SSH عام +mirror_use_ssh.text = استخدم مصادقة SSH +mirror_use_ssh.helper = سيقوم Forgejo بعكس المستودع عبر Git عبر SSH وإنشاء زوج مفاتيح لك عند تحديد هذا الخيار. يجب عليك التأكد من أن المفتاح العام الذي تم إنشاؤه مخول للدفع إلى المستودع الوجهة. لا يمكنك استخدام التخويل المستند إلى كلمة المرور عند تحديد هذا الخيار. +mirror_use_ssh.not_available = المصادقة عبر SSH غير متاحة. +mirror_denied_combination = لا يمكن استخدام المصادقة المستندة إلى المفتاح العام وكلمة المرور معاً. +mirror_sync_on_commit = المزامنة عند دفع الإيداعات +mirror_address = استنساخ عبر URL +mirror_address_desc = ضع أي بيانات اعتماد مطلوبة في قسم المصادقة. +mirror_address_protocol_invalid = عنوان URL المقدم غير صالح. يمكن استخدام مواقع http(s):// أو git:// فقط للنسخ المتطابق. +mirror_lfs = تخزين الملفات الكبيرة (LFS) +mirror_lfs_desc = تنشيط النسخ المتطابق لبيانات LFS. +mirror_lfs_endpoint = نقطة نهاية LFS +mirror_lfs_endpoint_desc = ستحاول المزامنة استخدام عنوان url المستنسخ إلى تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS المستودع مخزنة في مكان آخر. +mirror_last_synced = آخر مزامنة +mirror_password_placeholder = (لم يتم تعديله) +mirror_password_blank_placeholder = (بلا تعيين) +mirror_password_help = تغيير اسم المستخدم لمسح كلمة المرور المخزنة. +watchers = المراقبون +stargazers = المميِّزون بنجمة +stars_remove_warning = سيؤدي ذلك إلى إزالة جميع النجوم من هذا المستودع. +stars = النجوم +reactions_more = و %d أكثر +language_other = أُخرى +adopt_search = أدخل اسم المستخدم للبحث عن مستودعات غير معتمدة... (اتركه فارغاً للعثور على الكل) +adopt_preexisting_label = اعتماد الملفات +adopt_preexisting = اعتماد الملفات الموجودة مسبقاً +adopt_preexisting_content = إنشاء مستودع من %s +adopt_preexisting_success = الملفات المعتمدة والمستودع الذي تم إنشاؤه من %s +delete_preexisting_success = الملفات المحذوفة غير المعتمدة في %s +blame_prior = عرض النّقد قبل هذا التغيير +blame.ignore_revs = جاري تجاهل المراجعات في .git-blame-ignore-revs. انقر هنا لتجاوز وعرض واجهة blame العادية. +blame.ignore_revs.failed = فشل تجاهل المراجعات في .git-blame-ignore-revs. +author_search_tooltip = عرض كحد أقصى 30 مستخدمًا +summary_card_alt = بطاقة ملخص المستودع %s +transfer.accept = قبول النقل +transfer.accept_desc = النقل إلى ”%s“ +transfer.reject = رفض النقل +transfer.reject_desc = إلغاء النقل إلى ”%s“ +transfer.no_permission_to_accept = لا تملك الصلاحية لقبول هذا النقل. +transfer.no_permission_to_reject = لا تملك الصلاحية لرفض هذا النقل. +desc.private = خاص +desc.public = عام +desc.template = قالب +desc.internal = داخلي +desc.archived = مؤرشف +template.items = عناصر القالب +template.git_content = محتوى Git (الفرع الافتراضي) +template.git_hooks_tooltip = يتعذر عليك حاليًا تعديل أو إزالة خطافات Git بمجرد إضافتها. حدد هذا فقط إذا كنت تثق بمستودع القالب. +template.topics = المواضيع +template.issue_labels = وسوم الإبلاغات +template.one_item = يجب على الأقل تحديد عنصر قالب واحد +template.invalid = يجب تحديد مستودع القوالب +archive.title = تمت أرشفة هذا المستودع. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +archive.title_date = تمت أرشفة هذا المستودع على %s. يمكنك عرض الملفات واستنساخه، لكن لا يمكنك إجراء أي تغييرات على حالته، مثل دفع وإنشاء بلاغات أو طلبات سحب أو تعليقات جديدة. +archive.nocomment = التعليق غير ممكن لأن المستودع تمت أرشفته. +archive.pull.noreview = هذا المستودع مؤرشف. لا يمكنك مراجعة طلبات السحب. +sync_fork.branch_behind_one = هذا الفرع هو %[1]d إيداع خلف %[2]s +sync_fork.branch_behind_few = هذا الفرع هو %[1]d إيداعات خلف %[2]s +sync_fork.button = مزامنة +form.reach_limit_of_creation_1 = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودع %d. +form.reach_limit_of_creation_n = لقد وصل المالك بالفعل إلى الحد الأقصى للمستودعات %d. +form.name_reserved = تم حجز اسم المستودع ”%s“. +form.name_pattern_not_allowed = النمط ”%s“ غير مسموح به في اسم المستودع. +form.string_too_long = السلسلة المحددة أطول من d حرفاً. +need_auth = المصادقة +migrate_options = خيارات الترحيل +migrate_options_mirror_helper = سيكون هذا المستودع مرآة +migrate_options_lfs = ترحيل ملفات LFS +migrate_options_lfs_endpoint.label = نقطة نهاية LFS +migrate_options_lfs_endpoint.description = سيحاول الترحيل استخدام مسار Git البعيد (remote) لـ تحديد خادم LFS. يمكنك أيضًا تحديد نقطة نهاية مخصصة إذا كانت بيانات LFS للمستودع مخزنة في مكان آخر. +migrate_options_lfs_endpoint.description.local = يتم دعم مسار الخادم المحلي أيضاً. +migrate_options_lfs_endpoint.placeholder = إذا تُركت فارغة، سيتم اشتقاق نقطة النهاية من عنوان URL المستنسخ +migrate_items = عناصر الترحيل +migrate_repo = ترحيل المستودع +migrate.repo_desc_helper = اتركه فارغاً لاستيراد الوصف الموجود +migrate.clone_address = ترحيل / استنساخ من عنوان URL +migrate.clone_address_desc = رابط HTTP(S) أو Git لاستنساخ مستودع موجود +migrate.clone_local_path = أو مسار خادم محلي +migrate.permission_denied = لا يُسمح لك باستيراد المستودعات المحلية. +migrate.permission_denied_blocked = لا يمكنك الاستيراد من مضيفين غير مسموح بهم، يُرجى الطلب من المسؤول التحقق من إعدادات ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. +migrate.invalid_local_path = المسار المحلي غير صالح. فهو غير موجود أو ليس مجلداً. +migrate.invalid_lfs_endpoint = نقطة نهاية LFS غير صالحة. +migrate.failed = أخفق الترحيل: %v +migrate.migrate_items_options = رمز الوصول الفريد مطلوب لترحيل العناصر الإضافية +migrated_from = تم الترحيل من %[2]s +migrated_from_fake = +migrate.migrate = الترحيل من %s +migrate.migrating = الترحيل من %s … +migrate.migrating_failed = فشل الترحيل من %s. +migrate.migrating_failed.error = أخفق الترحيل: %s +migrate.migrating_failed_no_addr = أخفق الترحيل. +migrate.github.description = ترحيل البيانات من github.com أو خادم GitHub Enterprise. +migrate.git.description = ترحيل مستودع فقط من أي خدمة Git. +migrate.gitlab.description = ترحيل البيانات من gitlab.com أو مثيلات GitLab الأخرى. +migrate.gitea.description = ترحيل البيانات من gitea.com أو مثيلات Gitea الأخرى. +migrate.gogs.description = ترحيل البيانات من notabug.org أو مثيلات Gogs الأخرى. +migrate.onedev.description = ترحيل البيانات من code.onedev.io أو مثيلات OneDev الأخرى. +migrate.codebase.description = ترحيل البيانات من codebasehq.com. +migrate.gitbucket.description = ترحيل البيانات من مثيلات GitBucket. +migrate.migrating_git = ترحيل بيانات Git +migrate.migrating_topics = ترحيل المواضيع +migrate.migrating_labels = ترحيل الوسوم +migrate.migrating_releases = ترحيل الإصدارات +migrate.migrating_issues = ترحيل البلاغات +migrate.migrating_pulls = ترحيل طلبات السحب +migrate.cancel_migrating_title = إلغاء الترحيل +migrate.cancel_migrating_confirm = تريد إلغاء عملية الترحيل هذه؟ +mirror_from = مرآة لـ +forked_from = مشتقّ من +generated_from = تم توليده من +fork_from_self = لا يمكنك اشتقاق مستودع تملكه. +fork_guest_user = سجّل الدخول لاشتقاق هذا المستودع. +watch_guest_user = سجّل الدخول لمشاهدة هذا المستودع. +star_guest_user = قم بتسجيل الدخول لإعطاء نجمة لهذا المستودع. +subscribe.issue.guest.tooltip = سجّل الدخول للاشتراك في هذا البلاغ. +subscribe.pull.guest.tooltip = سجّل الدخول للاشتراك في طلب السحب هذا. +watch = شاهد +unwatch = إلغاء المشاهدة +star = نجمة +unstar = إزالة النجمة +fork = اشتقاق +more_operations = المزيد من العمليات +no_desc = لا يوجد وصف +quick_guide = دليل سريع +clone_this_repo = استنسخ هذا المستودع +cite_this_repo = الاستشهاد بهذا المستودع +create_new_repo_command = إنشاء مستودع جديد على موجّه الأوامر +push_exist_repo = دفع مستودع موجود من موجّه الأوامر +empty_message = لا يحتوي هذا المستودع على أي محتوى. +broken_message = لا يمكن قراءة بيانات Git التي يستند إليها هذا المستودع. اتصل بمسؤول هذا المثيل أو احذف هذا المستودع. +code = الكود +code.desc = الوصول إلى الشيفرة المصدرية والملفات والالتزامات والفروع. +branch = فرع +tree = شجرة + [mail] admin.new_user.text = من فضلك اضغط هنا لإدارة هذا المستخدم من لوحة الإدارة. admin.new_user.subject = مستخدم جديد: %s سجل حالاً diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 30fa95e6be..1bb5386645 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -724,7 +724,7 @@ following_one = %d sledovaný followers.title.one = Sledující followers.title.few = Sledující following.title.one = Sleduje -following.title.few = Sleudje +following.title.few = Sleduje public_activity.visibility_hint.self_private = Vaše aktivita je viditelná pouze vám a správcům instance. Nastavení. public_activity.visibility_hint.admin_private = Tato aktivita je pro vás viditelná, protože jste administrátor, ale uživatel chce, aby zůstala soukromá. public_activity.visibility_hint.self_public = Vaše aktivita je viditelná všem, mimo interakcí v soukromých prostorech. Nastavení. @@ -3991,7 +3991,7 @@ variables.update.success=Proměnná byla upravena. runners.none = Nejsou dostupné žádné runnery runs.workflow = Workflow runners = Runnery -runs.pushed_by = pushnuto uživatelem +runs.pushed_by = pushnuta uživatelem need_approval_desc = Potřebovat schválení pro spouštění workflowů pro žádosti o sloučení forků. runners.runner_manage_panel = Správa runnerů runs.no_job_without_needs = Workflow musí obsahovat alespoň jednu práci bez závislostí. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 2e0928bb1f..9664c5840e 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -320,7 +320,7 @@ sqlite3_not_available=Ця версія Forgejo не підтримує SQLite3. invalid_db_setting=Налаштування бази даних є некоректними: %v invalid_repo_path=Помилковий шлях до кореня репозиторію: %v invalid_app_data_path=Некоректний шлях до даних програми: %v -run_user_not_match=Ім'я в «Користувач, від якого запустити» не є ім'ям поточного користувача: %s -> %s +run_user_not_match=Ім'я в «Користувач, від якого запустити» не є ім'ям поточного користувача: %s → %s internal_token_failed=Не вдалося згенерувати внутрішній токен: %v secret_key_failed=Не вдалося згенерувати секретний ключ: %v save_config_failed=Не в змозі зберегти конфігурацію: %v @@ -908,7 +908,7 @@ oauth2_application_create_description=Програми OAuth2 надають в authorized_oauth2_applications=Авторизовані програми OAuth2 revoke_key=Відкликати -revoke_oauth2_grant=Скасувати доступ +revoke_oauth2_grant=Відкликати доступ revoke_oauth2_grant_description=Скасування доступу для цієї програми третьої сторони не дозволить їй отримувати доступ до ваших даних. Ви впевнені? twofa_desc=Двофакторна автентифікація підвищує безпеку вашого облікового запису. @@ -934,7 +934,7 @@ manage_account_links_desc=Ці зовнішні акаунти прив'язан account_links_not_available=Наразі немає зовнішніх облікових записів, пов'язаних із вашим обліковим записом Forgejo. link_account=Прив'язати обліковий запис remove_account_link=Видалити пов'язаний обліковий запис -remove_account_link_desc=Видалення пов'язаного облікового запису відкликає його доступ до вашого облікового запису Forgejo. Продовжити? +remove_account_link_desc=Видалення пов'язаного облікового запису скасує його доступ до вашого облікового запису Forgejo. Продовжити? remove_account_link_success=Зв'язаний обліковий запис видалено. @@ -1093,7 +1093,7 @@ template_description=Шаблонні репозиторії дозволяют visibility=Видимість visibility_description=Тільки власник або члени організації які мають віповідні права, зможуть побачити. visibility_helper_forced=Адміністратор вашого сайту налаштував параметри: всі нові репозиторії будуть приватними. -visibility_fork_helper=(Ці зміни вплинуть на всі форки.) +visibility_fork_helper=(Буде змінено видимість усіх форків.) clone_helper=Потрібна допомога у клонуванні? Відвідайте сторінку Допомога. fork_repo=Створити форк репозиторію fork_from=Форк з @@ -1158,9 +1158,9 @@ delete_preexisting_success=Видалено неприйняті файли в % blame_prior=Переглянути анотацію, що передує цій зміні -transfer.accept=Дозволити трансфер +transfer.accept=Прийняти передачу transfer.accept_desc=`Перемістити до "%s"` -transfer.reject=Відхилити трансфер +transfer.reject=Відхилити передачу transfer.reject_desc=`Скасувати переміщення до "%s"` desc.private=Приватний @@ -1958,7 +1958,7 @@ settings.transfer_notices_1=- Ви втратите доступ до репоз settings.transfer_notices_2=- Ви збережете доступ, якщо новим власником стане організація, власником якої ви є. settings.transfer_notices_3=- Якщо репозиторій є приватним і передається окремому користувачеві, ця дія гарантує, що користувач має хоча б дозвіл на читаня репозитарію (і при необхідності змінює права дозволів). settings.transfer_owner=Новий власник -settings.transfer_perform=Здійснити перенесення +settings.transfer_perform=Здійснити передачу settings.transfer_started=`Цей репозиторій чекає підтвердження перенесення від "%s"` settings.transfer_succeed=Репозиторій був перенесений. settings.signing_settings=Параметри перевірки підпису @@ -2336,7 +2336,7 @@ issues.filter_poster_no_select = Усі автори pulls.merged_info_text = Гілку %s тепер можна видалити. find_file.go_to_file = Знайти файл visibility_helper = Зробити репозиторій приватним -projects.card_type.desc = Попередній вигляд карток +projects.card_type.desc = Попередній перегляд карток projects.card_type.text_only = Лише текст projects.card_type.images_and_text = Зображення і текст issues.filter_poster = Автор @@ -2830,6 +2830,22 @@ settings.authorization_header = Заголовок авторизації settings.authorization_header_desc = За наявності буде включено як заголовок авторизації для запитів. Приклади: %s. settings.ignore_stale_approvals = Ігнорувати застарілі схвалення +transfer.no_permission_to_accept = У вас немає дозволу прийняти цю передачу. +transfer.no_permission_to_reject = У вас немає дозволу відхилити цю передачу. +editor.revert = Вивернути %s на: +commit.revert = Вивернути +commit.revert-header = Вивернути: %s +commit.revert-content = Оберіть гілку, на яку вивернути: +issues.choose.ignore_invalid_templates = Недійсні шаблони проігноровано +issues.choose.invalid_templates = Знайдено %v недійсних шаблонів +issues.dependency.issue_batch_close_blocked = Неможливо виконати пакетне закриття вибраних задач, оскільки задача #%d має відкриті залежності +issues.review.pending.tooltip = Цей коментар наразі невидимий для інших користувачів. Щоб надіслати коментарі у черзі, виберіть «%s» → «%s/%s/%s» у верхній частині сторінки. +pulls.cmd_instruction_checkout_title = Переключення гілок +pulls.cmd_instruction_checkout_desc = З репозиторію вашого проєкту переключіться на нову гілку і протестуйте зміни. +pulls.cmd_instruction_merge_warning = Увага: в цьому репозиторії не ввімкнено «Автовизначення ручного об'єднання», тож позначити цей запит як об'єднаний вручну вам доведеться самостійно. +settings.transfer_abort_success = Передачу репозиторію %s успішно скасовано. +topic.format_prompt = Теми повинні починатися з літери або цифри, можуть містити дефіси («-») і крапки («.») і мати довжину до 35 символів. Дозволено використання лише малих літер. + [graphs] contributors.what = внески component_loading_info = Це може зайняти деякий час… @@ -2962,6 +2978,11 @@ teams.invite_team_member.list = Запрошення в очікуванні form.name_pattern_not_allowed = Вираз «%s» не може бути частиною назви організації. teams.add_nonexistent_repo = Репозиторій, який ви намагаєтеся додати, не існує. Спочатку створіть його. +settings.change_orgname_prompt = Зауважте, зміна назви організації також змінить URL-адресу організації і звільнить стару назву. +teams.none_access_helper = Опція «Немає доступу» впливає лише на приватні репозиторії. +teams.general_access = Індивідуальний доступ +teams.general_access_helper = Дозволи учасників будуть визначатися відповідно до наведеної нижче таблиці дозволів. + [admin] dashboard=Панель управління users=Облікові записи користувачів @@ -3035,16 +3056,16 @@ dashboard.mspan_structures_obtained=Отримано структур MSpan dashboard.mcache_structures_usage=Використання структур MCache dashboard.mcache_structures_obtained=Отримано структур MCache dashboard.profiling_bucket_hash_table_obtained=Отримано хеш-таблиць профілювання -dashboard.gc_metadata_obtained=Отримано метаданих збирача сміття (GC) +dashboard.gc_metadata_obtained=Отримано метаданих збирача сміття dashboard.other_system_allocation_obtained=Отримання інших виділень пам'яті -dashboard.next_gc_recycle=Наступний цикл збирача сміття (GC) -dashboard.last_gc_time=З останнього запуску збирача сміття (GC) +dashboard.next_gc_recycle=Наступний цикл збирача сміття +dashboard.last_gc_time=З останнього запуску збирача сміття dashboard.total_gc_time=Загальна пауза збирача сміття (GC) -dashboard.total_gc_pause=Загальна пауза збирача сміття (GC) -dashboard.last_gc_pause=Остання пауза збирача сміття (GC) -dashboard.gc_times=Кількість запусків збирача сміття (GC) -dashboard.delete_old_actions=Видалити всі старі дії з бази даних -dashboard.delete_old_actions.started=Видалення всіх старі дії з бази даних розпочато. +dashboard.total_gc_pause=Загальна пауза збирача сміття +dashboard.last_gc_pause=Остання пауза збирача сміття +dashboard.gc_times=Кількість запусків збирача сміття +dashboard.delete_old_actions=Видалити всі старі активності з бази даних +dashboard.delete_old_actions.started=Розпочато видалення всіх старих активностей з бази даних. users.user_manage_panel=Керування обліковими записами users.new_account=Створити обліковий запис @@ -3252,7 +3273,7 @@ config.git_version=Версія Git config.repo_root_path=Шлях до кореня репозиторію config.lfs_root_path=Кореневий шлях LFS config.log_file_root_path=Шлях до лог файлу -config.script_type=Тип скрипта +config.script_type=Тип скрипту config.reverse_auth_user=Користувач для авторизації на зворотному проксі config.ssh_config=Конфігурація SSH @@ -3301,7 +3322,7 @@ config.default_enable_timetracking=Увімкнути відстеження ч config.default_allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу config.no_reply_address=Домен прихованих адрес електронної пошти config.default_visibility_organization=Видимість за замовчуванням для нових організацій -config.default_enable_dependencies=Увімкнути залежності задачі за замовчуванням +config.default_enable_dependencies=Увімкнути залежності задач за замовчуванням config.webhook_config=Конфігурація вебхуків config.queue_length=Довжина черги @@ -3332,7 +3353,7 @@ config.session_config=Конфігурація сесії config.session_provider=Провайдер сесії config.provider_config=Конфігурація постачальника config.cookie_name=Назва файлу cookie -config.gc_interval_time=Інтервал запуску збирача сміття (GC) +config.gc_interval_time=Інтервал запуску збирача сміття config.session_life_time=Час життя сесії config.https_only=Тільки HTTPS config.cookie_life_time=Час життя файлу cookie @@ -3347,12 +3368,12 @@ config.git_disable_diff_highlight=Вимкнути підсвітку синта config.git_max_diff_lines=Максимум рядків на diff (на один файл) config.git_max_diff_line_characters=Максимум символів на diff (на одну строку) config.git_max_diff_files=Максимум diff-файлів (для показу) -config.git_gc_args=Аргументи збирача сміття (GC) +config.git_gc_args=Аргументи збирача сміття config.git_migrate_timeout=Тайм-аут міграції config.git_mirror_timeout=Тайм-аут оновлення дзеркала config.git_clone_timeout=Тайм-аут операції клонування -config.git_pull_timeout=Тайм-аут операції Pull -config.git_gc_timeout=Тайм-аут операції збирача сміття (GC) +config.git_pull_timeout=Тайм-аут операції отримання змін +config.git_gc_timeout=Тайм-аут операції збирача сміття config.log_config=Конфігурація журналу config.disabled_logger=Вимкнено @@ -3391,9 +3412,9 @@ monitor.queue.settings.submit=Оновити налаштування monitor.queue.settings.changed=Налаштування оновлено notices.system_notice_list=Сповіщення системи -notices.view_detail_header=Переглянути деталі повідомлення +notices.view_detail_header=Подробиці сповіщення notices.select_all=Вибрати все -notices.deselect_all=Скасувати виділення +notices.deselect_all=Скасувати вибір notices.inverse_selection=Інвертувати виділене notices.delete_selected=Видалити вибране notices.delete_all=Видалити всі cповіщення @@ -3491,6 +3512,12 @@ config.cache_test_failed = Не вдалося перевірити кеш: %v. config.cache_test_slow = Перевірку кешу завершено успішно, але відповідь повільна: %s. config.cache_test_succeeded = Перевірку кешу завершено успішно, відповідь отримано через %s. +dashboard.delete_old_system_notices = Видалити всі старі сповіщення системи з бази даних +dashboard.rebuild_issue_indexer = Перебудувати індексатор задач +users.purge = Повністю видалити користувач_ку +users.details = Дані користувача +auths.login_source_exist = Джерело автентифікації «%s» вже існує. + [action] create_repo=створив(ла) репозиторій %s rename_repo=репозиторій перейменовано з %[1]s на %[3]s @@ -3720,6 +3747,27 @@ npm.dependencies.bundle = Пакетні залежності npm.dependencies.peer = Однорангові залежності owner.settings.cleanuprules.pattern_full_match = Застосувати шаблон до повної назви пакунка +alpine.registry.key = Завантажте відкритий RSA-ключ реєстру в папку /etc/apk/keys/ для перевірки підпису індексу: +rpm.distros.redhat = у дистрибутивах на основі RedHat +rpm.distros.suse = у дистрибутивах на основі SUSE +owner.settings.cargo.title = Індекс реєстру Cargo +owner.settings.cargo.initialize = Ініціалізувати індекс +owner.settings.cargo.initialize.description = Для реєстру Cargo потрібен спеціальний Git-репозиторій з індексом. Використання цієї опції (пере)створить репозиторій і автоматично його налаштує. +owner.settings.cargo.initialize.error = Не вдалося ініціалізувати індекс Cargo: %v +owner.settings.cargo.initialize.success = Індекс Cargo успішно створено. +owner.settings.cargo.rebuild = Перебудувати індекс +owner.settings.cargo.rebuild.description = Перебудування може бути корисним, якщо індекс не синхронізовано зі збереженими пакунками Cargo. +owner.settings.cargo.rebuild.error = Не вдалося перебудувати індекс Cargo: %v +owner.settings.cargo.rebuild.no_index = Неможливо перебудувати, індекс не ініціалізовано. +owner.settings.cleanuprules.title = Правила очистки +owner.settings.cleanuprules.add = Додати правило очистки +owner.settings.cleanuprules.edit = Редагувати правило очистки +owner.settings.cleanuprules.none = Правил очистки ще немає. +owner.settings.cleanuprules.preview = Попередній перегляд правила очистки +owner.settings.cleanuprules.preview.none = Правило очистки не відповідає жодному пакунку. +owner.settings.cleanuprules.success.update = Правило очистки оновлено. +owner.settings.cleanuprules.success.delete = Правило очистки видалено. + [secrets] deletion = Видалити секрет creation.success = Секрет «%s» додано. diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 7fb22a0d33..703d03aa83 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -35,9 +35,9 @@ "discussion.locked": "Tato diskuze byla uzamčena. Komentování je omezené na přispěvatele.", "relativetime.future": "v budoucnu", "relativetime.years": { - "one": "Před %d rokem", + "one": "před %d rokem", "few": "Před %d lety", - "other": "Před %d lety" + "other": "před %d lety" }, "relativetime.1day": "včera", "relativetime.2days": "před dvěma dny", @@ -48,30 +48,30 @@ "relativetime.1year": "minulý rok", "relativetime.2years": "před dvěma lety", "relativetime.weeks": { - "one": "Před %d týdnem", + "one": "před %d týdnem", "few": "Před %d týdny", - "other": "Před %d týdny" + "other": "před %d týdny" }, "relativetime.days": { - "one": "Před %d dnem", + "one": "před %d dnem", "few": "Před %d dny", - "other": "Před %d dny" + "other": "před %d dny" }, "relativetime.mins": { - "one": "Před %d minutou", + "one": "před %d minutou", "few": "Před %d minutami", - "other": "Před %d minutami" + "other": "před %d minutami" }, "relativetime.hours": { - "one": "Před %d hodinou", + "one": "před %d hodinou", "few": "Před %d hodinami", - "other": "Před %d hodinami" + "other": "před %d hodinami" }, "relativetime.now": "nyní", "relativetime.months": { - "one": "Před %d měsícem", + "one": "před %d měsícem", "few": "Před %d měsíci", - "other": "Před %d měsíci" + "other": "před %d měsíci" }, "moderation.report_content": "Nahlásit obsah", "moderation.report_abuse_form.details": "Tento formulář je určen k nahlašování uživatelů, kteří si vytvářejí spamové profily, repozitáře, problémy, komentáře nebo se chovají nevhodně.", diff --git a/options/locale_next/locale_hi.json b/options/locale_next/locale_hi.json index 0967ef424b..bf87940be2 100644 --- a/options/locale_next/locale_hi.json +++ b/options/locale_next/locale_hi.json @@ -1 +1,59 @@ -{} +{ + "home.welcome.no_activity": "गतिविधि नहीं", + "home.welcome.activity_hint": "आपकी फीड में अभी कुछ भी नहीं है। आपके कार्य और रिपॉजिटरी यहाँ दिखेंगे।", + "home.explore_repos": "रिपॉजिटरी निहारें", + "home.explore_users": "उसेर्स देखें", + "home.explore_orgs": "संस्थाएं देखें", + "stars.list.none": "किसी ने भी चिन्हित/स्टार नहीं किया", + "watch.list.none": "कोई भी ये रिपॉजिटरी नहीं देख रहा", + "followers.incoming.list.self.none": "कोई भी प्रोफाइल फॉलो नहीं कर रहा", + "followers.incoming.list.none": "कोई भी यूजर को फॉलो नहीं कर रहा", + "followers.outgoing.list.self.none": "आप किसी को फॉलो ही नहीं कर रहे", + "followers.outgoing.list.none": "%s किसी को फॉलो नहीं कर रहे", + "relativetime.now": "अभी", + "relativetime.future": "भविष्य में", + "relativetime.1day": "कल", + "relativetime.2days": "दो दिन पहले", + "relativetime.1week": "पिछले हफ्ते", + "relativetime.2weeks": "दो हफ्ते पहले", + "relativetime.1month": "पिछले महीने", + "relativetime.2months": "दो महीने पहले", + "relativetime.1year": "पिछले साल", + "relativetime.2years": "दो साल पहले", + "repo.form.cannot_create": "वो जगह जहाँ पे रिपॉजिटरी रखीं जातीं हैं वहां जगह ख़त्म", + "repo.issue_indexer.title": "इशू ठौर", + "search.milestone_kind": "माइलस्टोन ढूंढें…", + "incorrect_root_url": "इस फॉरगेजो इंस्टैंस को %d सर्वर पे कॉन्फ़िगर किया गया है। आप फॉरगेजो को किसी अन्य url से देख रहे हैं, जिससे एप्लीकेशन टूटती है। अच्छा यूआरएल फॉरगेजो के एडमिन कण्ट्रोल करते हैं और रुट_यूआरएल जो की app. ini में है", + "themes.names.forgejo-auto": "फॉरगेजो - सिस्टम थीम फॉलो करें", + "themes.names.forgejo-light": "फॉरगेजो लाइट", + "themes.names.forgejo-dark": "फॉरगेजो डार्क", + "error.not_found.title": "पृष्ठ नहीं मिला", + "alert.asset_load_failed": "एसेट फाइल लोड नहीं हो पायी (path) ये पक्का करें की एसेट फाइल एक्सेस हो सकती है", + "alert.range_error": " एक अंक %d और %d के बीच में", + "install.invalid_lfs_path": "LFS रुट नहीं बना पाया इस path पर", + "profile.actions.tooltip": "और एक्शन्स", + "profile.edit.link": "प्रोफाइल बनाएं", + "feed.atom.link": "एटम फीड", + "keys.ssh.link": "SSH कीस", + "keys.gpg.link": "GPG कीस", + "admin.config.moderation_config": "सत्यापन कॉन्फ़िगरेशन", + "moderation.report_abuse": "कंप्लेंट करें", + "moderation.report_content": "कंप्लेंट करें", + "moderation.report_abuse_form.header": "कंप्लेंट करें एडमिनिस्ट्रेटर से", + "moderation.report_abuse_form.details": "इस फॉर्म का प्रयोग वो करें जो बताना चाहते हैं स्पैम प्रोफाइल, रिपॉजिटरी, इशू, कमैंट्स या गलत पेशी", + "moderation.report_abuse_form.invalid": "इनवैलिड आर्गुमेंट", + "moderation.report_abuse_form.already_reported": "आप पहले शिकायत कर चुके हैं", + "moderation.abuse_category": "वर्ग", + "moderation.abuse_category.placeholder": "वर्ग चुनें", + "moderation.abuse_category.spam": "स्पैम", + "moderation.abuse_category.malware": "मैलवेयर", + "moderation.abuse_category.illegal_content": "गैर कानूनी कंटेंट", + "moderation.abuse_category.other_violations": "प्लेटफार्म का कोई रूल तोडा हैं", + "moderation.report_remarks": "टिप्पणी", + "moderation.report_remarks.placeholder": "कुछ डिटेल्स बताओ जिस बारे में रिपोर्ट कर रहे हो", + "moderation.submit_report": "रिपोर्ट सबमिट करो", + "moderation.reporting_failed": "नयी रिपोर्ट सबमिट नहीं हो सकती", + "moderation.reported_thank_you": "रिपोर्ट के लिए शुक्रिया। एडमिन को बताया गया है", + "avatar.constraints_hint": "कस्टम अवतार का फ़ाइल आकार 200 किलोबाइट से अधिक नहीं होना चाहिए और 125x125 पिक्सेल से बड़ा नहीं होना चाहिए", + "meta.last_line": "तीतर के दो पीछे तीतर तीतर के दो आगे तीतर बोलो कितने तीतर ?" +} From 14d6c2943832a8e38f3130aee7334e40d78cdc9d Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Fri, 22 Aug 2025 15:09:03 +0500 Subject: [PATCH 59/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate Translation updates that were relevant to v12 branch were picked from this commit: be7b87c1c26540873ec18539e93091bdc368b9d7 Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: BlackSpirits Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Hiraku Co-authored-by: Juno Takano Co-authored-by: Lzebulon Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: adf19 Co-authored-by: amv-bamboo Co-authored-by: dobrovolskyi Co-authored-by: earl-warren Co-authored-by: justbispo Co-authored-by: retarded-beast Co-authored-by: xtex --- options/locale/locale_ar.ini | 63 +++++++++++--- options/locale/locale_be.ini | 14 ++- options/locale/locale_de-DE.ini | 24 +++--- options/locale/locale_pt-PT.ini | 2 +- options/locale/locale_ru-RU.ini | 6 +- options/locale/locale_sv-SE.ini | 10 +++ options/locale/locale_uk-UA.ini | 117 +++++++++++++++++--------- options/locale_next/locale_fr-FR.json | 8 +- 8 files changed, 171 insertions(+), 73 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index dab6787a53..a0adbef8f8 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -1010,7 +1010,7 @@ issues.dependency.issue_no_dependencies = بلا اعتمادية. issues.filter_sort.mostcomment = الأكثر تعليقات issues.label.filter_sort.reverse_by_size = الأكبر حجما release.message = صِف هذا الإصدار -editor.cancel_lower = ألغِ +editor.cancel_lower = إلغاء issues.label.filter_sort.reverse_alphabetically = أبجديا معكوسا trust_model_helper = اختر نموذج الثقة للتحقق من التوقيعات. الخيارات المتاحة هي: issues.unlock_error = لا يمكن فك قفل مسألة غير مقفلة. @@ -1044,7 +1044,7 @@ issues.label_archive = أرشف التصنيف issues.choose.blank_about = أنشئ مسألة من القالب المبدئي. issues.filter_poster = منشئ release.delete_release = احذف الإصدار -editor.cancel = ألغِ +editor.cancel = إلغاء issues.change_title_at = `غيّر العنوان من %s إلى %s %s` branch.new_branch_from = أنشئ فرعًا جديدًا من "%s" issues.due_date_form_edit = "عدّل" @@ -1069,15 +1069,15 @@ projects.desc = أدر المسائل وطلبات الدمج في لوحات م ambiguous_runes_header = `يحتوي هذا الملف على محارف يونيكود غامضة` executable_file = ملف تنفيذي settings.webhook.response = الاستجابة -editor.delete = احذف %s -file_view_raw = أظهر المجرد +editor.delete = حذف %s +file_view_raw = أظهر الخام settings.add_webhook = أضف خطاف ويب settings.slack_channel = قناة commits = إيداعات commit = إيداع -editor.upload_file = ارفع ملفا +editor.upload_file = رفع ملف settings.webhook.headers = الترويسات -org_labels_desc_manage = أدرها +org_labels_desc_manage = إدارة editor.patch = طبّق الرقعة editor.must_have_write_access = يجب أن يكون لديك إمكانية الكتابة حتى تعدّل هذا الملف أو تقترح تعديلات. template.webhooks = خطاطيف الويب @@ -1101,12 +1101,12 @@ settings.webhook.payload = المحتوى invisible_runes_header = `يحتوي هذا الملف على محارف يونيكود غير مرئية` editor.filename_help = أضف مجلدا بكتابة اسمه ثم شرطة مائلة ('/'). احذف مجلدا بضغط زر Backspace أول شيء في خانة الاسم. editor.commit_changes = أودع التعديلات -editor.add_file = أضف ملفا +editor.add_file = إضافة ملف settings.githook_name = اسم الخطاف editor.fork_before_edit = يجب أن تشتق هذا المستودع حتى تعدّل هذا الملف أو تقترح تعديلات. projects.description_placeholder = الوصف tag = وسم -file_raw = مجرد +file_raw = خام projects.create = أنشئ مشروعا settings.update_webhook = حدّث خطاف الويب editor.push_rejected_no_message = لقد رفض الخادوم التعديل بغير رسالة. نرجو مراجعة خطاطيف جت. @@ -1125,7 +1125,7 @@ editor.no_changes_to_show = لا توجد تعديلات لعرضها. settings.webhook.test_delivery = اختبار التوصيل commit_graph.hide_pr_refs = أخفِ طلبات الدمج editor.new_file = ملف جديد -file_view_source = أظهر المصدر +file_view_source = عرض المصدر settings.webhook_deletion_success = أزيل خطاف الويب. projects.title = العنوان settings.slack_domain = النطاق @@ -1154,11 +1154,11 @@ settings.add_hook_success = أضيف خطاف الويب. commit.revert-header = إرجاع: %s editor.file_already_exists = يوجد فعلا في هذا المستودع ملف باسم "%s". settings.web_hook_name_matrix = متركس -editor.filename_cannot_be_empty = لا يمكن ترك اسم الملف فارغا. +editor.filename_cannot_be_empty = لا يمكن ترك اسم الملف فارغاً. editor.add_tmpl = أضف '<%s>' editor.new_branch_name_desc = اسم الفرع الجديد… release = إصدار -editor.delete_this_file = احذف الملف +editor.delete_this_file = حذف الملف editor.or = أو editor.push_rejected_summary = رسالة الرفض الكاملة: settings.webhook_deletion = أزل خطاف الويب @@ -1581,7 +1581,7 @@ migrate.invalid_lfs_endpoint = نقطة نهاية LFS غير صالحة. migrate.failed = أخفق الترحيل: %v migrate.migrate_items_options = رمز الوصول الفريد مطلوب لترحيل العناصر الإضافية migrated_from = تم الترحيل من %[2]s -migrated_from_fake = +migrated_from_fake =تم الترحيل من %[1]s migrate.migrate = الترحيل من %s migrate.migrating = الترحيل من %s … migrate.migrating_failed = فشل الترحيل من %s. @@ -1631,6 +1631,45 @@ code.desc = الوصول إلى الشيفرة المصدرية والملفات branch = فرع tree = شجرة +clear_ref = 'مسح المرجع الحالي' +filter_branch_and_tag = تصفية الفرع أو العلامة +find_tag = البحث عن علامة +project = المشارييع +n_commit_one = %s إيداع +n_commit_few = %s إيداعات +n_branch_one = %s فرع +n_branch_few = %s فروع +n_tag_one = %s علامة +n_tag_few = ‪%s علامات +n_release_one = %s إصدار +n_release_few = %s إصدارات +file.title = %s عند %s +file_follow = متابعة الروابط الرمزية +file_view_rendered = عرض المُخرج النهائي +view_git_blame = عرض مسؤول تعديل git +stored_lfs = مخزن مع Git LFS +symbolic_link = رابط رمزي +vendored = مضمن +commit_graph = الرسم البياني للإيداع +commit.contained_in = هذا الإيداع موجود في: +commit.contained_in_default_branch = هذا الإيداع جزء من الفرع الافتراضي +normal_view = عرض عادي +line = سطر +lines = أسطر +from_comment = (تعليق) +no_eol.text = لا EOL +no_eol.tooltip = هذا الملف لا يحتوي على نهاية لخط الشخصية. +editor.edit_file = عدّل الملف +editor.cannot_edit_lfs_files = لا يمكن تحرير ملفات LFS في واجهة الويب. +editor.edit_this_file = عدل الملف +editor.this_file_locked = الملف مقفل +editor.file_delete_success = تم حذف الملف "%s". +editor.name_your_file = اسم ملفك… +editor.add_tmpl.filename = اسم الملف +editor.signoff_desc = أضف مقطورة موقّعة من قِبل المُجرّد في نهاية رسالة سجل الدخول. +editor.propose_file_change = اقتراح تغيير الملف +editor.new_branch_name = تسمية الفرع الجديد لهذا الإيداع + [mail] admin.new_user.text = من فضلك اضغط هنا لإدارة هذا المستخدم من لوحة الإدارة. admin.new_user.subject = مستخدم جديد: %s سجل حالاً diff --git a/options/locale/locale_be.ini b/options/locale/locale_be.ini index fe04dadc3e..25aff3019c 100644 --- a/options/locale/locale_be.ini +++ b/options/locale/locale_be.ini @@ -7,10 +7,20 @@ sign_in = Увайсці sign_in_or = або sign_out = Выйсці sign_up = Зарэгістравацца -link_account = Звязаць Уліковы запіс +link_account = Звязаць уліковы запіс register = Рэгістрацыя version = Версія powered_by = Працуе на ℅s page = Старонка home = Галоўная Старонка -sign_in_with_provider = Увайсці з %s \ No newline at end of file +sign_in_with_provider = Увайсці з %s +template = Шаблон +language = Мова +notifications = Апавяшчэнні +create_new = Стварыць… +user_profile_and_more = Профіль і налады… +signed_in_as = Увайшоў як +enable_javascript = Гэты вэб-сайт патрабуе JavaScript. +toc = Змест +licenses = Ліцэнзіі +return_to_forgejo = Вярнуцца да Forgejo \ No newline at end of file diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 9f55f79fe9..d252308062 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -633,7 +633,7 @@ repository_files_already_exist=Dateien für dieses Repository sind bereits vorha repository_files_already_exist.adopt=Dateien für dieses Repository existieren bereits und können nur übernommen werden. repository_files_already_exist.delete=Dateien für dieses Repository sind bereits vorhanden. Du must sie löschen. repository_files_already_exist.adopt_or_delete=Dateien für dieses Repository existieren bereits. Du musst sie entweder übernehmen oder löschen. -visit_rate_limit=Das Rate-Limit bei der Gegenseite wurde erreicht. +visit_rate_limit=Die Ratenbegrenzung bei der Gegenseite wurde erreicht. 2fa_auth_required=Die Gegenseite benötigt Zweifaktorauthentifikation. org_name_been_taken=Der Organisationsname ist bereits vergeben. team_name_been_taken=Der Teamname ist bereits vergeben. @@ -1515,7 +1515,7 @@ issues.filter_assignees=Verantwortliche filtern issues.filter_milestones=Meilenstein filtern issues.filter_projects=Projekt filtern issues.filter_labels=Label filtern -issues.filter_reviewers=Sichter filtern +issues.filter_reviewers=Prüfer filtern issues.new=Neues Issue issues.new.title_empty=Der Titel kann nicht leer sein issues.new.labels=Labels @@ -1535,7 +1535,7 @@ issues.new.closed_milestone=Geschlossene Meilensteine issues.new.assignees=Zuständige issues.new.clear_assignees=Zuständige entfernen issues.new.no_assignees=Niemand zuständig -issues.new.no_reviewers=Keine Sichter +issues.new.no_reviewers=Keine Prüfer issues.choose.get_started=Los geht's issues.choose.open_external_link=Öffnen issues.choose.blank=Standard @@ -1599,7 +1599,7 @@ issues.filter_type.assigned_to_you=Dir zugewiesen issues.filter_type.created_by_you=Von dir erstellt issues.filter_type.mentioning_you=Hat dich erwähnt issues.filter_type.review_requested=Sichtung angefordert -issues.filter_type.reviewed_by_you=Von dir gesichtet +issues.filter_type.reviewed_by_you=Von dir überprüft issues.filter_sort=Sortieren issues.filter_sort.latest=Neueste issues.filter_sort.oldest=Älteste @@ -1809,7 +1809,7 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.approve=hat die Änderungen %s genehmigt -issues.review.comment=hat %s gesichtet +issues.review.comment=hat %s überprüft issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed_label=Verworfen issues.review.left_comment=hat einen Kommentar hinterlassen @@ -1822,7 +1822,7 @@ issues.review.remove_review_request_self=hat die Sichtung %s verweigert issues.review.pending=Ausstehend issues.review.pending.tooltip=Dieser Kommentar ist derzeit nicht für andere Benutzer sichtbar. Um deine ausstehenden Kommentare einzureichen, wähle „%s“ -> „%s/%s/%s“ oben auf der Seite. issues.review.review=Review -issues.review.reviewers=Sichter +issues.review.reviewers=Prüfer issues.review.outdated=Veraltet issues.review.outdated_description=Der Inhalt hat sich geändert, seit dieser Kommentar abgegeben wurde issues.review.option.show_outdated_comments=Veraltete Kommentare anzeigen @@ -1905,8 +1905,8 @@ pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht e pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin zusammenführen. pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt. -pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Sichter angefragt wurden. -pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Sichtern fehlt. +pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Prüfer angefragt wurden. +pulls.blocked_by_official_review_requests=Dieser Pull-Request ist blockiert, weil ihm die Genehmigung von einem oder mehreren offiziellen Prüfern fehlt. pulls.blocked_by_outdated_branch=Dieser Pull-Request ist blockiert, da er veraltet ist. pulls.blocked_by_changed_protected_files_1=Dieser Pull-Request ist blockiert, weil er eine geschützte Datei ändert: pulls.blocked_by_changed_protected_files_n=Dieser Pull-Request ist blockiert, weil er geschützte Dateien ändert: @@ -2470,8 +2470,8 @@ settings.protect_required_approvals=Erforderliche Genehmigungen settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Sichtungen. settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Positivliste beschränken settings.protect_approvals_whitelist_enabled_desc=Nur Sichtungen von Benutzern oder Teams auf der Positivliste zählen zu den erforderlichen Genehmigungen. Existiert keine Positivliste, so zählen Sichtungen von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. -settings.protect_approvals_whitelist_users=Nutzer, die sichten dürfen -settings.protect_approvals_whitelist_teams=Teams, die sichten dürfen +settings.protect_approvals_whitelist_users=Autorisierte Prüfer +settings.protect_approvals_whitelist_teams=Teams, autorisiert zum Prüfen settings.dismiss_stale_approvals=Entferne alte Genehmigungen settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt. settings.require_signed_commits=Signierte Commits erforderlich @@ -2490,7 +2490,7 @@ settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ settings.protected_branch_deletion=Branch-Schutz löschen settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? settings.block_rejected_reviews=Zusammenführung bei abgelehnten Sichtungen blockieren -settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Sichter angefragt werden, auch wenn genügend Genehmigungen existieren. +settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Prüfer angefragt werden, auch wenn genügend Genehmigungen existieren. settings.block_on_official_review_requests=Merge bei offiziellen Sichtungsanfragen blockieren settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Sichtungsanfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist @@ -2902,7 +2902,7 @@ issues.reaction.alt_add = Füge %[1]s Reaktion zum Kommentar hinzu. issues.reaction.alt_remove = Entferne %[1]s Reaktion von diesem Kommentar. summary_card_alt = Zusammenfassungskarte des Repositorys %s release.summary_card_alt = Übersichtskarte eines Releases mit dem Titel „%s“ im Repository %s -archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht gesichtet werden. +archive.pull.noreview = Dieses Repository ist archiviert. Pull-Requests können nicht überprüft werden. 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 diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index d82ae69fa4..40f9f45945 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -1060,7 +1060,7 @@ user_unblock_success = O utilizador foi desbloqueado com sucesso. language.title = Idioma predefinido keep_activity_private.description = O seu trabalho público apenas estará visível para si e para os administradores da instância. language.description = Este idioma vai ser guardado na sua conta e ser usado como o predefinido depois de iniciar sessão. -language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Ler mais. +language.localization_project = Ajude-nos a traduzir o Forgejo para o seu idioma! Saiba mais. 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. Pode ainda reivindicar o nome de utilizador antigo durante o período de espera. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 304b07b7ee..01397f2e23 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -755,7 +755,7 @@ public_profile=Публичный профиль biography_placeholder=Кратко расскажите о себе другим! (Можно использовать Markdown) location_placeholder=Пусть все знают, откуда вы profile_desc=Ваш профиль -password_username_disabled=Нелокальным пользователям запрещено изменение их имени пользователя. Для получения более подробной информации обратитесь к администратору сайта. +password_username_disabled=Нелокальные пользователи не могут изменить имя. Для подробностей обратитесь к администрации сайта. full_name=Полное имя website=Веб-сайт location=Местоположение @@ -766,7 +766,7 @@ update_language_not_found=Язык «%s» недоступен. update_language_success=Язык обновлён. update_profile_success=Ваш профиль успешно обновлён. change_username=Ваше имя пользователя было изменено. -change_username_prompt=Обратите внимание: изменение имени пользователя также меняет URL вашей учётной записи. +change_username_prompt=Учтите, что при изменении имени пользователя ссылка на ваш профиль тоже будет изменена. change_username_redirect_prompt=Старое имя будет перенаправлять на новое до тех пор, пока оно не будет занято. continue=Далее cancel=Отмена @@ -1053,7 +1053,7 @@ hints = Подсказки additional_repo_units_hint = Предлагать включить больше разделов в репозиториях update_hints = Обновить подсказки update_hints_success = Подсказки обновлены. -additional_repo_units_hint_description = Показывать подсказку "Включить больше разделов" в репозиториях, в которых включены не все разделы. +additional_repo_units_hint_description = Показывать подсказку «Включить больше разделов» в репозиториях, в которых включены не все разделы. pronouns_custom = Другие pronouns = Местоимения pronouns_unspecified = Не указаны diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index ec57f025cd..b914f93fe0 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -205,15 +205,23 @@ link_modal.url = Url link_modal.description = Beskrivning link_modal.header = Lägg till en länk +buttons.enable_monospace_font = Aktivera jämnbrett typsnitt +buttons.disable_monospace_font = Avaktivera jämnbrett typsnitt +link_modal.paste_reminder = Tips: Med ett URL i ditt klippbord, kan du klistra in direkt i textredigeraren för att skapa en länk. + [filter] string.asc = A - Ö +string.desc = Ö - A + [error] occurred = Ett fel har inträffat server_internal = Internt serverfel network_error = Nätverksfel report_message = Om du tror att detta är en Forgejo-bugg, sök efter ärenden på Codeberg eller öppna ett nytt ärende om det behövs. +not_found = Målet kunde inte hittas. + [startpage] app_desc=En smidig, självhostad Git-tjänst install=Lätt att installera @@ -334,6 +342,8 @@ password_algorithm = Hashalgoritm för lösenord invalid_password_algorithm = Ogiltig hashalgoritm för lösenord env_config_keys_prompt = Följande miljövariabler kommer också att tillämpas på din konfigurationsfil: +smtp_from_invalid = "Skicka E-post som" adressen är ogiltig + [home] uname_holder=Användarnamn eller e-postadress password_holder=Lösenord diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 9664c5840e..bf55a78c87 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -15,7 +15,7 @@ page=Сторінка template=Шаблон language=Мова notifications=Сповіщення -active_stopwatch=Трекер робочого часу +active_stopwatch=Облік робочого часу create_new=Створити… user_profile_and_more=Профіль і налаштування… signed_in_as=Увійшов як @@ -660,13 +660,13 @@ Location = Розташування AccessToken = Токен доступу unable_verify_ssh_key = Не вдалося перевірити ключ SSH, перевірте його на наявність помилок. repository_force_private = Увімкнено примусову приватність: приватні репозиторії не можна зробити публічними. -must_use_public_key = Ключ, який ви надали, є приватним. Будь ласка, нікуди не завантажуйте свій приватний ключ. Використовуйте замість нього публічний ключ. +must_use_public_key = Ключ, який ви надали, є приватним. Будь ласка, нікуди не завантажуйте свій приватний ключ. Використовуйте замість нього відкритий ключ. openid_been_used = Адреса OpenID «%s» вже використовується. still_has_org = Ваш обліковий запис є учасником однієї або декількох організацій, спочатку покиньте їх. duplicate_invite_to_team = Цього користувача вже запрошено як учасника команди. organization_leave_success = Ви успішно покинули організацію %s. include_error = ` має містити підрядок «%s».` -invalid_group_team_map_error = ` призначення недійсне: %s` +invalid_group_team_map_error = ` зіставлення недійсне: %s` unsupported_login_type = Цей тип входу не підтримує видалення облікового запису. admin_cannot_delete_self = Ви не можете видалити себе, якщо ви є адміністратором. Спочатку зніміть із себе права адміністратора. unset_password = Для користувача не встановлено пароль. @@ -815,7 +815,7 @@ manage_gpg_keys=Керування ключами GPG add_key=Додати ключ ssh_desc=Ці відкриті ключі SSH повʼязані з вашим обліковим записом. Відповідні приватні ключі дозволяють отримати повний доступ до ваших репозиторіїв. Підтверджені ключі можна використати для підтвердження комітів Git, підписаних із SSH. principal_desc=Ці настройки SSH сертифікатів вказані у вашому обліковому записі та надають повний доступ до ваших репозиторіїв. -gpg_desc=Ці публічні ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. +gpg_desc=Ці відкриті ключі GPG пов'язані з вашим обліковим записом і використовуються для підтвердження комітів. Тримайте свої приватні ключі в безпеці, оскільки вони дозволяють підписувати коміти вашим особистим підписом. ssh_helper=Потрібна допомога? Дивіться гід на GitHub з генерації ключів SSH або виправлення типових неполадок SSH. gpg_helper= Потрібна допомога? Перегляньте посібник GitHub про GPG . add_new_key=Додати SSH ключ @@ -895,7 +895,7 @@ oauth2_applications_desc=Програми OAuth2 дають можливість remove_oauth2_application=Видалити програму OAuth2 remove_oauth2_application_desc=Видалення програми OAuth2 скасує доступ до всіх підписаних токенів доступу. Продовжити? remove_oauth2_application_success=Програму видалено. -create_oauth2_application=Створити новий додаток OAuth2 +create_oauth2_application=Створити нову програму OAuth2 create_oauth2_application_button=Створити програму oauth2_application_name=Назва програми save_application=Зберегти @@ -999,7 +999,7 @@ comment_type_group_pull_request_push = Додані коміти permissions_public_only = Тільки публічні select_permissions = Виберіть дозволи permissions_access_all = Усі (публічні, приватні й обмежені) -create_oauth2_application_success = Ви успішно створили новий додаток OAuth2. +create_oauth2_application_success = Ви успішно створили нову програму OAuth2. keep_email_private_popup = Ваша адреса електронної пошти не буде відображатися у вашому профілі і не буде використовуватися за замовчуванням для комітів, зроблених через веб-інтерфейс, таких як завантаження файлів, редагування і об'єднання комітів. Натомість ви можете використовувати спеціальну адресу %s для прив'язки комітів до свого облікового запису. Ця опція не вплине на існуючі коміти. blocked_since = Заблокований з %s can_not_add_email_activations_pending = Очікується активація, спробуйте ще раз за кілька хвилин, якщо хочете додати нову адресу електронної пошти. @@ -1023,7 +1023,7 @@ pronouns_custom_label = Інші займенники repo_and_org_access = Доступ до репозиторію та організації change_username_redirect_prompt.with_cooldown.few = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d днів. Протягом періоду захисту ви ще можете повернути собі старе ім'я. change_username_redirect_prompt.with_cooldown.one = Старе ім'я користувача буде доступне всім після періоду захисту, який триватиме %[1]d день. Протягом періоду захисту ви ще можете повернути собі старе ім'я. -change_username_redirect_prompt = Старе ім'я користувача буде перенаправленням, поки хтось не присвоїть ім'я собі. +change_username_redirect_prompt = Старе ім'я користувача буде переспрямуванням, поки хтось не присвоїть ім'я собі. comment_type_group_lock = Стан блокування webauthn_alternative_tip = Можливо, ви бажаєте налаштувати додатковий спосіб входу. user_unblock_success = Користувач_ку успішно розблоковано. @@ -1201,7 +1201,7 @@ migrate_items_pullrequests=Запити на злиття migrate_items_merge_requests=Запити на об'єднання migrate_items_releases=Релізи migrate_repo=Перенести репозиторій -migrate.clone_address=Міграція / клонувати з URL-адреси +migrate.clone_address=Міграція / клонування з URL-адреси migrate.clone_address_desc=URL-адреса HTTP(S) або Git «clone» існуючого репозиторію migrate.clone_local_path=або шлях до локального серверу migrate.permission_denied=Вам не дозволено імпортувати локальні репозиторії. @@ -1550,9 +1550,9 @@ issues.unlock=Розблокування обговорення issues.lock.unknown_reason=Неможливо заблокувати задачу з невідомою причиною. issues.lock_duplicate=Задача не може бути заблокованим двічі. issues.unlock_error=Не можливо розблокувати задачу, яка не заблокована. -issues.lock_with_reason=заблоковано як %s та обмежене обговорення для співавторів %s -issues.lock_no_reason=заблоковано та обмежене обговорення для співавторів %s -issues.unlock_comment=розблоковане обговорення %s +issues.lock_with_reason=блокує як %s та обмежує обговорення до співавторів %s +issues.lock_no_reason=блокує та обмежує обговорення до співавторів %s +issues.unlock_comment=розблоковує обговорення %s issues.lock_confirm=Заблокувати issues.unlock_confirm=Розблокувати issues.lock.notice_1=- Інші користувачі не можуть додавати нові коментарі до цієї задачі. @@ -1702,8 +1702,8 @@ pulls.required_status_check_administrator=Як адміністратор ви pulls.can_auto_merge_desc=Цей запит можна об'єднати автоматично. pulls.cannot_auto_merge_desc=Цей запит на злиття не може бути злитий автоматично через конфлікти. pulls.cannot_auto_merge_helper=Злийте вручну для вирішення конфліктів. -pulls.num_conflicting_files_1=%d конфліктуючий файл -pulls.num_conflicting_files_n=%d конфліктуючі файли +pulls.num_conflicting_files_1=%d конфліктний файл +pulls.num_conflicting_files_n=%d конфліктних файлів pulls.approve_count_1=%d схвалення pulls.approve_count_n=%d схвалень pulls.reject_count_1=%d запит на зміну @@ -1818,7 +1818,7 @@ activity.active_prs_count_1=%d активний запит на activity.active_prs_count_n=%d активних запитів на злиття activity.merged_prs_count_1=Об'єднаний запит на злиття activity.merged_prs_count_n=Об'єднані запити на злиття -activity.opened_prs_count_1=Запропонований запит на злиття +activity.opened_prs_count_1=Запропоновані запити на злиття activity.opened_prs_count_n=Запропонованих запитів на злиття activity.title.user_1=%d користувачем activity.title.user_n=%d користувачами @@ -1840,8 +1840,8 @@ activity.closed_issue_label=Закрито activity.new_issues_count_1=Нова задача activity.new_issues_count_n=Нові задачі activity.new_issue_label=Відкриті -activity.title.unresolved_conv_1=%d Незавершене обговорення -activity.title.unresolved_conv_n=%d Незавершених обговорень +activity.title.unresolved_conv_1=%d незавершене обговорення +activity.title.unresolved_conv_n=%d незавершених обговорень activity.unresolved_conv_desc=Список всіх старих задач і Pull Request'ів з недавньої активністю, але ще не закритих або прийнятих. activity.unresolved_conv_label=Відкрити activity.title.releases_1=%d випуск @@ -1912,20 +1912,20 @@ settings.use_external_wiki=Використовувати зовнішню ві settings.external_wiki_url=URL зовнішньої вікі settings.external_wiki_url_error=Зовнішня URL-адреса wiki не є допустимою URL-адресою. settings.external_wiki_url_desc=Відвідувачі будуть перенаправлені на URL-адресу, коли вони клацають по вкладці. -settings.issues_desc=Увімкнути відстеження задач в репозиторію +settings.issues_desc=Увімкнути відстеження задач settings.use_internal_issue_tracker=Використовувати вбудовану систему відстеження задач -settings.use_external_issue_tracker=Використовувати зовнішню систему обліку задач +settings.use_external_issue_tracker=Використовувати зовнішню систему відстеження задач settings.external_tracker_url=URL зовнішньої системи відстеження задач settings.external_tracker_url_error=URL зовнішнього баг-трекера не є допустимою URL-адресою. settings.external_tracker_url_desc=Відвідувачі перенаправляються на зовнішню URL-адресу, коли натискають вкладку 'Задачі'. -settings.tracker_url_format=Формат URL зовнішнього трекера задач +settings.tracker_url_format=Формат URL зовнішньої системи відстеження задач settings.tracker_url_format_error=Неправильний формат URL-адреси зовнішнього баг-трекера. -settings.tracker_issue_style=Формат номера для зовнішньої системи обліку задач +settings.tracker_issue_style=Формат номера для зовнішньої системи відстеження задач settings.tracker_issue_style.numeric=Цифровий settings.tracker_issue_style.alphanumeric=Буквено-цифровий settings.tracker_url_format_desc=Використовуйте шаблони {user}, {repo} та {index} для імені користувача, репозиторію та номеру задічі. settings.enable_timetracker=Увімкнути відстеження часу -settings.allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу +settings.allow_only_contributors_to_track_time=Дозволити відстеження часу тільки учасникам розробки settings.pulls_desc=Увімкнути запити на злиття в репозиторій settings.pulls.ignore_whitespace=Ігнорувати пробіл у конфліктах settings.pulls.enable_autodetect_manual_merge=Увімкнути автовизначення ручного злиття (Примітка: у деяких особливий випадках можуть виникнуть помилки) @@ -2033,7 +2033,7 @@ settings.slack_icon_url=URL іконки settings.slack_color=Колір settings.discord_username=Ім'я кристувача settings.discord_icon_url=URL іконки -settings.event_desc=Тригер: +settings.event_desc=Спрацьовує на: settings.event_push_only=Push події settings.event_send_everything=Усі події settings.event_choose=Власні події… @@ -2531,7 +2531,7 @@ issues.new.assign_to_me = Призначити собі contributors.contribution_type.additions = Додавання settings.add_web_hook_desc = Інтегрувати %s у цей репозиторій. settings.event_wiki_desc = Вікі-сторінку створено, перейменовано, відредаговано або видалено. -settings.mirror_settings.push_mirror.copy_public_key = Копіювати публічний ключ +settings.mirror_settings.push_mirror.copy_public_key = Копіювати відкритий ключ editor.add_tmpl.filename = назва файлу settings.unarchive.button = Розархівувати object_format = Формат об'єкта @@ -2814,7 +2814,7 @@ pulls.auto_merge_newly_scheduled_comment = `планує автоматично pulls.auto_merge_canceled_schedule_comment = `скасовує автоматичне об'єднання цього запиту на злиття після успішного завершення всіх перевірок %[1]s` pulls.delete.text = Ви дійсно хочете видалити цей запит на злиття? (Весь його вміст буде остаточно видалено. Можливо, варто його закрити і зберегти в архіві) comments.edit.already_changed = Не вдається зберегти зміни. Схоже, що хтось інший уже змінив вміст коментаря. Оновіть сторінку і спробуйте відредагувати ще раз, щоб уникнути перезапису чужих змін -signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає публічного ключа, пов'язаного з вашим обліковим записом. +signing.wont_sign.pubkey = Коміт не буде підписано, оскільки у вас немає відкритого ключа, пов'язаного з вашим обліковим записом. signing.wont_sign.twofa = Щоб підписувати коміти, у вас повинна бути ввімкнена двофакторна автентифікація. signing.wont_sign.parentsigned = Цей коміт не буде підписано, оскільки не підписано батьківський коміт. signing.wont_sign.basesigned = Злиття не буде підписано, оскільки не підписано базовий коміт. @@ -2846,6 +2846,18 @@ pulls.cmd_instruction_merge_warning = Увага: в цьому репо settings.transfer_abort_success = Передачу репозиторію %s успішно скасовано. topic.format_prompt = Теми повинні починатися з літери або цифри, можуть містити дефіси («-») і крапки («.») і мати довжину до 35 символів. Дозволено використання лише малих літер. +editor.invalid_commit_mail = Недійсна пошта для створення коміту. +editor.push_out_of_date = Схоже, дані для відправки застаріли. +editor.commit_email = Пошта автора +issues.filter_type.reviewed_by_you = Перевірено вами +issues.label_exclusive = Ексклюзивний +issues.label_archive = Архівна мітка +issues.label_archive_tooltip = Архівовані мітки за замовчуванням виключаються з пропозицій під час пошуку за міткою. +issues.cancel_tracking_history = `скасовує відстеження часу %s` +settings.webhook.delivery.success = Подію додано до черги доставки. Може знадобитися кілька секунд, перш ніж вона з'явиться в історії доставки. +settings.protect_status_check_matched = Збіг +settings.protected_branch_required_rule_name = Необхідна назва правила + [graphs] contributors.what = внески component_loading_info = Це може зайняти деякий час… @@ -3089,10 +3101,10 @@ users.update_profile_success=Обліковий запис користувач users.edit_account=Редагувати обліковий запис users.max_repo_creation=Максимальна кількість репозиторіїв users.max_repo_creation_desc=(Введіть -1, щоб використовувати глобальний ліміт за замовчуванням.) -users.is_activated=Обліковий запис користувача увімкнено +users.is_activated=Активований обліковий запис users.prohibit_login=Заблокований обліковий запис users.is_admin=Обліковий запис адміністратора -users.is_restricted=Обмежений +users.is_restricted=Обмежений обліковий запис users.allow_git_hook=Може створювати Git-хуки users.allow_git_hook_tooltip=Git-хуки виконуються від імені користувача ОС, від якого запущено Forgejo, і мають той самий рівень доступу до хоста. Таким чином, користувачі зі спеціальними правами Git-хуків можуть отримати доступ і змінювати всі репозиторії Forgejo, а також базу даних Forgejo. Вони також здатні отримати права адміністратора Forgejo. users.allow_import_local=Може імпортувати локальні репозиторії @@ -3174,16 +3186,16 @@ auths.domain=Домен auths.host=Хост auths.port=Порт auths.bind_dn=Прив'язати DN -auths.bind_password=Прив'язати пароль +auths.bind_password=Пароль bind auths.user_base=База пошуку користувачів auths.user_dn=DN користувача auths.attribute_username=Атрибут імені користувача auths.attribute_username_placeholder=Залиште порожнім, щоб використовувати ім'я користувача для реєстрації. auths.attribute_name=Атрибут імені -auths.attribute_surname=Атрибут Surname -auths.attribute_mail=Атрибут Email -auths.attribute_ssh_public_key=Атрибут Відкритий SSH ключ -auths.attributes_in_bind=Витягувати атрибути в контексті Bind DN +auths.attribute_surname=Атрибут прізвища +auths.attribute_mail=Атрибут адреси email +auths.attribute_ssh_public_key=Атрибут відкритого ключа SSH +auths.attributes_in_bind=Отримувати атрибути в контексті bind DN auths.allow_deactivate_all=Дозволити порожньому результату пошуку відключити всіх користувачів auths.use_paged_search=Використовувати посторінковий пошук auths.search_page_size=Розмір сторінки @@ -3237,15 +3249,15 @@ auths.tips.oauth2.general=Автентифікація OAuth2 auths.tip.oauth2_provider=Постачальник OAuth2 auths.tip.bitbucket=Зареєструйте нового споживача OAuth на %s і додайте дозвіл «Обліковий запис» — «Читання» auths.tip.nextcloud=`Зареєструйте нового споживача OAuth у вашому екземплярі за допомогою наступного меню "Налаштування -> Безпека -> клієнт OAuth 2.0"` -auths.tip.dropbox=Створіть новий додаток на %s -auths.tip.facebook=Зареєструйте новий додаток на %s і додайте модуль «Facebook Login» -auths.tip.github=Зареєструйте новий додаток OAuth на %s +auths.tip.dropbox=Створіть нову програму на %s +auths.tip.facebook=Зареєструйте нову програму на %s і додайте модуль «Facebook Login» +auths.tip.github=Зареєструйте нову програму OAuth на %s auths.tip.gitlab=Додайте новий додаток на https://gitlab.com/profile/applications auths.tip.google_plus=Отримайте облікові дані клієнта OAuth2 в консолі Google API на сторінці %s 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.discord=Зареєструйте нову програму на %s +auths.tip.yandex=Створіть нову програму на %s. У розділі «Yandex.Passport API» виберіть такі дозволи: «Доступ до адреси електронної пошти», «Доступ до аватара» і «Доступ до імені користувача, імені та прізвища, статі» auths.tip.mastodon=Введіть URL спеціального екземпляра для екземпляра mastodon, який ви хочете автентифікувати за допомогою (або використовувати за замовчуванням) auths.edit=Редагувати джерело автентифікації auths.activated=Це джерело автентифікація активоване @@ -3319,7 +3331,7 @@ config.default_keep_email_private=Приховувати адреси елект config.default_allow_create_organization=Дозволити створення організацій за замовчуванням config.enable_timetracking=Увімкнути відстеження часу config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням -config.default_allow_only_contributors_to_track_time=Враховувати тільки учасників розробки в підрахунку часу +config.default_allow_only_contributors_to_track_time=Дозволити відстеження часу тільки учасникам розробки config.no_reply_address=Домен прихованих адрес електронної пошти config.default_visibility_organization=Видимість за замовчуванням для нових організацій config.default_enable_dependencies=Увімкнути залежності задач за замовчуванням @@ -3347,7 +3359,7 @@ config.cache_config=Конфігурація кешу config.cache_adapter=Адаптер кешу config.cache_interval=Інтервал кешування config.cache_conn=Підключення до кешу -config.cache_item_ttl=Час зберігання даних кешу +config.cache_item_ttl=Час зберігання даних у кеші config.session_config=Конфігурація сесії config.session_provider=Провайдер сесії @@ -3415,7 +3427,7 @@ notices.system_notice_list=Сповіщення системи notices.view_detail_header=Подробиці сповіщення notices.select_all=Вибрати все notices.deselect_all=Скасувати вибір -notices.inverse_selection=Інвертувати виділене +notices.inverse_selection=Інвертувати вибір notices.delete_selected=Видалити вибране notices.delete_all=Видалити всі cповіщення notices.type=Тип @@ -3460,8 +3472,8 @@ monitor.queue.settings.desc = Пули динамічно зростають у monitor.queue.settings.remove_all_items_done = Усі елементи в черзі видалено. monitor.queue.settings.remove_all_items = Видалити всі config.app_slogan = Гасло екземпляра -auths.tip.gitea = Зареєструйте новий додаток OAuth. Інструкцію можна знайти на %s -auths.tip.gitlab_new = Зареєструйте новий додаток на %s +auths.tip.gitea = Зареєструйте нову програму OAuth. Інструкцію можна знайти на %s +auths.tip.gitlab_new = Зареєструйте нову програму на %s monitor.duration = Тривалість (с) users.reserved = Зарезервовано systemhooks.desc = Вебхуки автоматично сповіщають HTTP-сервер POST-запитами, коли в Forgejo відбуваються певні події. Вказані тут вебхуки спрацьовуватимуть для всіх репозиторіїв системи, тож врахуйте всі ймовірні наслідки для швидкодії. Докладніше — в посібнику з вебхуків. @@ -3518,6 +3530,19 @@ users.purge = Повністю видалити користувач_ку users.details = Дані користувача auths.login_source_exist = Джерело автентифікації «%s» вже існує. +users.local_import.description = Дозволити імпорт репозиторіїв з локальної файлової системи сервера. Це може становити загрозу безпеці. +auths.attribute_avatar = Атрибут аватара +auths.map_group_to_team = Зіставити групи LDAP з командами організації (залиште поле порожнім, щоб пропустити) +auths.oauth2_required_claim_name = Необхідна назва заявки +auths.oauth2_required_claim_name_helper = Вкажіть назву, щоб обмежити вхід з цього джерела користувачами із заявкою з такою назвою +auths.oauth2_required_claim_value = Необхідне значення заявки +auths.oauth2_required_claim_value_helper = Вкажіть значення, щоб обмежити вхід з цього джерела користувачами із заявкою з такими назвою і значенням +auths.oauth2_group_claim_name = Назва заявки, що надає назви груп для цього джерела. (Необов'язково) +auths.oauth2_admin_group = Значення групової заявки для адміністраторів. (Необов'язково — потрібна назва заявки вище) +auths.oauth2_restricted_group = Значення групової заявки для обмежених користувачів. (Необов'язково — потрібна назва заявки вище) +auths.oauth2_map_group_to_team = Зіставити заявлені групи з командами організації. (Необов'язково — потрібна назва заявки вище) +auths.tips.oauth2.general.tip = При реєстрації нової програми OAuth2 URL-адреса зворотного виклику/переспрямування повинна бути: + [action] create_repo=створив(ла) репозиторій %s rename_repo=репозиторій перейменовано з %[1]s на %[3]s @@ -3735,7 +3760,7 @@ alpine.registry = Налаштуйте цей реєстр, додавши URL cran.registry = Налаштуйте цей реєстр у файлі Rprofile.site: npm.registry = Налаштуйте цей реєстр у файлі .npmrc свого проєкту: chef.registry = Налаштуйте цей реєстр у файлі ~/.chef/config.rb: -owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки публічний ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. +owner.settings.chef.keypair.description = Запити до реєстру Chef повинні бути криптографічно підписані як засіб автентифікації. При генерації пари ключів на Forgejo зберігається тільки відкритий ключ. Приватний ключ надається вам для використання команд knife. Генерація нової пари ключів замінить попередню. nuget.dependency.framework = Цільовий фреймворк owner.settings.cleanuprules.preview.overview = Заплановано видалити %d пакунків. @@ -3768,6 +3793,9 @@ owner.settings.cleanuprules.preview.none = Правило очистки не в owner.settings.cleanuprules.success.update = Правило очистки оновлено. owner.settings.cleanuprules.success.delete = Правило очистки видалено. +rubygems.required.ruby = Необхідна версія Ruby +rubygems.required.rubygems = Необхідна версія RubyGem + [secrets] deletion = Видалити секрет creation.success = Секрет «%s» додано. @@ -3879,6 +3907,8 @@ runs.no_job_without_needs = Робочий потік повинен місти runs.no_job = Робочий потік повинен містити принаймні одне завдання workflow.dispatch.use_from = Використати робочий потік із +workflow.dispatch.trigger_found = Цей робочий потік спрацьовує на події workflow_dispatch. + [projects] type-3.display_name = Проєкт організації type-2.display_name = Проєкт репозиторію @@ -3946,6 +3976,9 @@ actions.write = Писати: вручну запускати, перез projects.write = Писати: створювати проєкти і стовпчики та редагувати їх. +ext_issues = Доступ до посилання на зовнішню систему відстеження задач. Налаштування дозволів відбувається поза сайтом. +ext_wiki = Доступ до посилання на зовнішню вікі. Налаштування дозволів відбувається поза сайтом. + [munits.data] pib = ПіБ eib = ЕіБ diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index da26d56107..ecfc928692 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -103,5 +103,11 @@ "settings.visibility.description": "La visibilité du profil affecte la capacité des autres à accéder à vos dépôts non-privés. Voir plus", "editor.textarea.shift_tab_hint": "Pas d'indentation sur cette ligne. Appuyez sur Maj + Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", "avatar.constraints_hint": "L'avatar personnalisé ne doit pas dépasser une taille de %[1]s ou être plus grand que %[2]dx%[3]d pixels", - "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur." + "editor.textarea.tab_hint": "Ligne déjà indentée. Appuyez sur Tab une nouvelle fois ou sur Échap pour quitter l'éditeur.", + "profile.actions.tooltip": "Plus d'actions", + "profile.edit.link": "Éditer le profil", + "keys.ssh.link": "Clé SSH", + "keys.gpg.link": "Clés GPG", + "repo.diff.commit.next-short": "Suiv.", + "repo.diff.commit.previous-short": "Préc." } From 011acee58c6a06641806b683a90153adcffe3f7e Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Fri, 22 Aug 2025 15:09:58 +0500 Subject: [PATCH 60/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate Translation updates that were relevant to v12 branch were picked from this commit: c0a1a604e6fce51c1770b7fbb8c97d62f3b4f1ad Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Dirk Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Ikuyo Co-authored-by: Juno Takano Co-authored-by: Lzebulon Co-authored-by: SomeTr Co-authored-by: Vaibhav Sunder Co-authored-by: Vyxie Co-authored-by: Wuzzy --- options/locale/locale_fr-FR.ini | 2 +- options/locale/locale_hi.ini | 60 ++++++++++++++++++++++++++- options/locale_next/locale_fr-FR.json | 3 +- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index db9872236b..bac541ef76 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -773,7 +773,7 @@ language=Langue ui=Thème hidden_comment_types=Catégories de commentaires masqués hidden_comment_types_description=Cochez les catégories suivantes pour masquer les commentaires correspondants des fils d'activité. Par exemple, « Label » cache les commentaires du genre « Cerise a attribué le label Bug il y a 2 heures». -hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket, révision, etc. +hidden_comment_types.ref_tooltip=Commentaires où ce ticket a été référencé sur un autre ticket/révision/… hidden_comment_types.issue_ref_tooltip=Commentaires où l’utilisateur change la branche/étiquette associée au ticket comment_type_group_reference=Référence comment_type_group_label=Label diff --git a/options/locale/locale_hi.ini b/options/locale/locale_hi.ini index 2f75c8d13e..36e4bf786f 100644 --- a/options/locale/locale_hi.ini +++ b/options/locale/locale_hi.ini @@ -39,4 +39,62 @@ organization = संगठन mirror = छवि settings = सेटिंग्स your_settings = आपकी सेटिंग्स -return_to_forgejo = फ़ोर्जेगो पे वापस जाएं \ No newline at end of file +return_to_forgejo = फ़ोर्जेगो पे वापस जाएं +toggle_menu = चालू-बंद करें +access_token = एक्सेस टोकन +webauthn_insert_key = अपनी सिक्योरिटी की डालें +webauthn_sign_in = सिक्योरिटी की का बटन दबाएं, नहीं है तो फिर से प्लग करें। +webauthn_press_button = अपनी सिक्योरिटी की पर बटन दबाएं… +webauthn_use_twofa = अपने फ़ोन से दो फैक्टर कोड लाएं +webauthn_error = सिक्योरिटी की रीड नहीं हो पा रही। +webauthn_unsupported_browser = आपका ब्राउज़र वेबौथ सपोर्ट नहीं करता। +webauthn_error_unknown = कोई अंजान एरर हुई, फिर से कोशिश करें। +webauthn_error_insecure = वेबौथ पर सिर्फ सुरक्षित कनेक्शन हो. HTTP टेस्ट के लिए ओरिजिन “लोकलहोस्ट” या “127.0.0.1” +webauthn_error_unable_to_process = सर्वर आपकी रिक्वेस्ट प्रोसेस नहीं कर पाया। +webauthn_error_duplicated = सिक्योरिटी की इस रिक्वेस्ट के लिए नहीं है। कृपया देखें की पहले से रजिस्टर्ड तो नहीं। +webauthn_error_empty = कृपया इस चाभी का नाम रखें। +webauthn_error_timeout = आपकी की पढ़ने से पहले टाइमआउट हो गया। पृष्ट रीलोड करें और फिर कोशिश करें। +new_mirror = नया मिरर +new_fork = नयी रिपॉजिटरी फोर्क +new_project = नया प्रोजेक्ट +new_project_column = नया स्तम्भ +admin_panel = वेबसाइट प्रबंधन +your_profile = प्रोफाइल +your_starred = सीतारित +new_repo.title = नई रिपॉजिटरी +new_migrate.title = नया प्रवासन +new_org.title = नई संस्था +new_repo.link = नई रिपॉजिटरी +new_migrate.link = नया प्रवासन +new_org.link = नई संस्था +all = सब +sources = स्रोत +mirrors = मिर्रोर्स +collaborative = सहयोगी +forks = फोर्क्स +activities = गतिविधियाँ +pull_requests = पुल्ल करें +issues = इश्यूज +milestones = महत्वपूर्ण +ok = ओके +cancel = रद्द करें +retry = फिर से करें +rerun = फिर से +rerun_all = फिर से सारे काम करें +save = सेव करें +add = जोड़ें +add_all = सब जोड़ें +remove = हटाएं +remove_all = सारा हटाएं +remove_label_str = हटाएं आइटम “%s” +edit = संपादित करना +view = देखें +test = टेस्ट +enabled = सक्षम किया गया +disabled = असक्षम किया गया +locked = लॉक्ड +copy = कॉपी +copy_generic = क्लिपबोर्ड पर कॉपी करें +copy_url = URL कॉपी करें +copy_hash = कॉपी हैश +copy_path = राह कॉपी करें \ No newline at end of file diff --git a/options/locale_next/locale_fr-FR.json b/options/locale_next/locale_fr-FR.json index ecfc928692..1fc9bfa4af 100644 --- a/options/locale_next/locale_fr-FR.json +++ b/options/locale_next/locale_fr-FR.json @@ -109,5 +109,6 @@ "keys.ssh.link": "Clé SSH", "keys.gpg.link": "Clés GPG", "repo.diff.commit.next-short": "Suiv.", - "repo.diff.commit.previous-short": "Préc." + "repo.diff.commit.previous-short": "Préc.", + "mail.actions.run_info_sha": "Commit: %[1]s" } From e1fed1d862fb98f35c7fdcf2790b77aa91ea51f4 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Fri, 22 Aug 2025 15:10:35 +0500 Subject: [PATCH 61/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Translation updates that were relevant to v12 branch were picked from this commit: aa9975131419a5b1b34d8407d4839f1c64c86404 Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: Artiman Co-authored-by: Atalanttore Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Gusted Co-authored-by: Juno Takano Co-authored-by: Kenneth Bruen Co-authored-by: Priit Jõerüüt Co-authored-by: SomeTr Co-authored-by: Vaibhav Sunder Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: adriand Co-authored-by: alextecplayz Co-authored-by: earl-warren Co-authored-by: xtex --- options/locale/locale_cs-CZ.ini | 2 + options/locale/locale_de-DE.ini | 8 +- options/locale/locale_et.ini | 430 +++++++++++++++----------- options/locale/locale_fa-IR.ini | 4 +- options/locale/locale_fil.ini | 2 + options/locale/locale_hi.ini | 110 ++++++- options/locale/locale_lv-LV.ini | 2 + options/locale/locale_nds.ini | 2 + options/locale/locale_nl-NL.ini | 2 + options/locale/locale_ro.ini | 95 +++++- options/locale/locale_uk-UA.ini | 4 +- options/locale_next/locale_cs-CZ.json | 3 +- options/locale_next/locale_de-DE.json | 3 +- options/locale_next/locale_et.json | 33 +- options/locale_next/locale_fil.json | 17 +- options/locale_next/locale_lv-LV.json | 3 +- options/locale_next/locale_nds.json | 3 +- options/locale_next/locale_nl-NL.json | 3 +- options/locale_next/locale_uk-UA.json | 3 +- 19 files changed, 525 insertions(+), 204 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 1bb5386645..ec1e4547f7 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -1091,6 +1091,8 @@ regenerate_token = Resetovat access_token_regeneration = Znovu vygenerovat přístupový token access_token_regeneration_desc = Opětovným vygenerováním tokenu znemožníte přístup k vašemu účtu aplikacím, které jej používají. Tato akce je nevratná. Chcete pokračovat? +ssh_token_help_ssh_agent = nebo, pokud používáte agenta SSH (s nastavenou proměnnou SSH_AUTH_SOCK): + [repo] new_repo_helper=Repozitář obsahuje všechny soubory projektu, včetně historie revizí. Už jej hostujete jinde? Migrovat repozitář. owner=Vlastník diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index d252308062..f66f241bbe 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -885,9 +885,9 @@ ssh_key_verified=Verifizierter Schlüssel ssh_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen. ssh_key_verify=Verifizieren ssh_invalid_token_signature=Der gegebene SSH-Schlüssel, Signatur oder Token stimmen nicht überein oder der Token ist veraltet. -ssh_token_required=Du musst eine Signatur für den Token unten angeben +ssh_token_required=Sie müssen eine Signatur für das Token unten angeben ssh_token=Token -ssh_token_help=Du kannst eine Signatur wie folgt generieren: +ssh_token_help=Sie können eine Signatur wie folgt generieren: ssh_token_signature=SSH-Textsignatur (armored signature) key_signature_ssh_placeholder=Beginnt mit „-----BEGIN SSH SIGNATURE-----“ verify_ssh_key_success=SSH-Key „%s“ wurde verifiziert. @@ -1093,6 +1093,8 @@ access_token_regeneration = Zugangstoken regenerieren access_token_regeneration_desc = Einen Token zu regenerieren, wird den Zugriff auf deinen Account von Anwendungen, die ihn nutzen, zurückziehen. Dies kann nicht rückgängig gemacht werden. Fortsetzen? regenerate_token = Regenerieren +ssh_token_help_ssh_agent = , oder, falls Sie einen SSH-Agenten benutzen (mit der Variable SSH_AUTH_SOCK gesetzt): + [repo] owner=Besitzer owner_helper=Einige Organisationen könnten in der Dropdown-Liste nicht angezeigt werden, da die Anzahl an Repositorys begrenzt ist. @@ -1654,7 +1656,7 @@ issues.create_comment=Kommentieren issues.closed_at=`hat dieses Issue %s geschlossen` issues.reopened_at=`hat dieses Issue %s wieder geöffnet` issues.commit_ref_at=`hat dieses Issue %s aus einem Commit referenziert` -issues.ref_issue_from=`hat %[1]s auf dieses Issue verwiesen %[3]s` +issues.ref_issue_from=`auf dieses Issue verwiesen %[3]s %[1]s` issues.ref_pull_from=`referenzierte diesen Pull-Request %[3]s %[1]s` issues.ref_closing_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es schließen wird, %[1]s` issues.ref_reopening_from=`referenzierte dieses Issue aus einem Pull-Request %[3]s der es wieder öffnen wird, %[1]s` diff --git a/options/locale/locale_et.ini b/options/locale/locale_et.ini index 441a7d8e07..7fcbc05bbb 100644 --- a/options/locale/locale_et.ini +++ b/options/locale/locale_et.ini @@ -1,67 +1,67 @@ [common] tracked_time_summary = Kokkuvõte jälgitavast ajast, mis põhineb probleemide nimekirja filtritel -your_settings = Seaded +your_settings = Seadistused home = Avaleht -dashboard = Armatuurlaud -explore = Uurige -help = Abi +dashboard = Töölaud +explore = Uuri +help = Abiteave logo = Logo sign_in = Logi sisse -sign_in_with_provider = Logi sisse koos %s +sign_in_with_provider = Logi sisse kasutajakontoga: %s sign_in_or = või -sign_out = Registreeru välja +sign_out = Logi välja sign_up = Registreeru -link_account = Lingi konto +link_account = Lingi väline kasutajakonto register = Registreeru version = Versioon page = Lehekülg template = Mall language = Keel -notifications = Teated +notifications = Teavitused active_stopwatch = Aktiivne aja jälgimine create_new = Loo… -user_profile_and_more = Profiil ja seaded… +user_profile_and_more = Profiil ja seadistused… signed_in_as = Sisselogitud kui -enable_javascript = See veebileht nõuab JavaScripti. +enable_javascript = See veebileht eeldab JavaScripti kasutamise lubamist. toc = Sisukord licenses = Litsentsid username = Kasutajanimi -webauthn_error_unable_to_process = Server ei saanud teie taotlust töödelda. -webauthn_error_duplicated = Turvalisuse võti ei ole selle taotluse puhul lubatud. Palun veenduge, et võti ei ole juba registreeritud. +webauthn_error_unable_to_process = Server ei saanud sinu päringut töödelda. +webauthn_error_duplicated = Turvavõti ei ole selle päringu puhul lubatud. Palun veendu, et võti ei ole juba registreeritud. return_to_forgejo = Tagasi Forgejo'sse -toggle_menu = Lülitage menüü -more_items = Rohkem esemeid +toggle_menu = Lülita menüü sisse/välja +more_items = Rohkem objekte email = E-posti aadress -password = Parool -access_token = Juurdepääsutähis -re_type = Kinnita parool +password = Salasõna +access_token = Tunnusluba +re_type = Kinnita salasõna twofa = Kahefaktoriline autentimine twofa_scratch = Kahefaktoriline kriipsukood -passcode = Passkood -webauthn_insert_key = Sisestage oma turvavõti -webauthn_sign_in = Vajutage turvavõtme nuppu. Kui teie turvavõtmel ei ole nuppu, sisestage see uuesti. -webauthn_press_button = Palun vajutage turvavõtme nuppu… -webauthn_use_twofa = Kasutage oma telefonist kahefaktorilist koodi -webauthn_error = Teie turvavõti ei saanud lugeda. -webauthn_unsupported_browser = Teie brauser ei toeta praegu WebAuthn. -webauthn_error_unknown = Tekkis tundmatu viga. Palun proovige uuesti. -webauthn_error_insecure = WebAuthn toetab ainult turvalisi ühendusi. HTTP kaudu testimiseks võite kasutada päritolu "localhost" või "127.0.0.1" -webauthn_error_empty = Sellele võtmele tuleb määrata nimi. -webauthn_error_timeout = Ajakatkestus saavutati enne võtme lugemist. Palun laadige see lehekülg uuesti ja proovige uuesti. +passcode = Salakood +webauthn_insert_key = Sisesta oma turvavõti +webauthn_sign_in = Vajuta turvavõtme nuppu. Kui sinu turvavõtmel ei ole nuppu, sisesta see uuesti. +webauthn_press_button = Palun vajuta turvavõtme nuppu… +webauthn_use_twofa = Sisesta oma telefonist kahefaktorilise autentimise kood +webauthn_error = Sinu turvavõtit ei saanud lugeda. +webauthn_unsupported_browser = Sinu veebibrauser ei toeta praegu WebAuthn-liidestust. +webauthn_error_unknown = Tekkis tundmatu viga. Palun proovi uuesti. +webauthn_error_insecure = WebAuthn toetab ainult turvalisi ühendusi. HTTP kaudu testimiseks võid kasutada lähteaadressina „localhost“ või „127.0.0.1“ +webauthn_error_empty = Palun lisa sellele võtmele täisnimi. +webauthn_error_timeout = Päring aegus enne võtme lugemist. Palun laadi see lehekülg uuesti ja proovi uuesti. repository = Hoidla organization = Organisatsioon -new_fork = Uus hoidla haru +new_fork = Uus lähekoodihoidla haru new_project = Uus projekt new_project_column = Uus veerg -admin_panel = Saidi administreerimine -settings = Seaded +admin_panel = Saidi haldus +settings = Seadistused your_profile = Profiil your_starred = Tähistatud tärniga -new_repo.title = Uus hoidla -new_migrate.title = Uus sisseränne +new_repo.title = Uus lähtekoodi hoidla +new_migrate.title = Uus kolimine new_org.title = Uus organisatsioon -new_repo.link = Uus hoidla -new_migrate.link = Uus sisseränne +new_repo.link = Uus lähtekoodi hoidla +new_migrate.link = Uus kolimine new_org.link = Uus organisatsioon all = Kõik sources = Allikad @@ -69,10 +69,10 @@ mirror = Peegelpilt mirrors = Peegelpildid forks = Harud activities = Tegevused -pull_requests = Tõmbepäringud -issues = Probleemid +pull_requests = Päringud koodi mestimiseks +issues = Veahaldus milestones = Verstapostid -ok = OK +ok = Sobib cancel = Tühista retry = Proovi uuesti rerun = Käivita uuesti @@ -81,74 +81,79 @@ add = Lisa add_all = Lisa kõik remove = Eemalda remove_all = Eemalda kõik -remove_label_str = Eemalda ühik "%s" -edit = Redigeeri +remove_label_str = Eemalda „%s“ objekt +edit = Muuda view = Vaata -test = Test -enabled = Võimaldatud -disabled = Välja lülitatud -locked = Lukkus +test = Testi +enabled = Kasutusel +disabled = Pole kasutusel +locked = Lukustatud copy = Kopeeri -copy_url = Kopeeri URL -copy_hash = Kooperi hash +copy_url = Kopeeri võrguaadress +copy_hash = Kopeeri räsi copy_content = Kopeeri sisu copy_branch = Kopeeri haru nimi copy_success = Kopeeritud! -copy_error = Kopeerimine ebaõnnestus +copy_error = Kopeerimine ei õnnestunud copy_type_unsupported = Seda failitüüpi ei saa kopeerida write = Kirjuta preview = Eelvaade -loading = Laadimine… +loading = Laadin… error = Viga -error404 = Lehekülge, millele te üritate jõuda, kas ei ole olemas või teil ei ole õigust seda vaadata. +error404 = Lehekülge, millele sa üritad jõuda, kas ei ole olemas, ta on eemaldatud või sul ei ole õigust seda vaadata. error413 = Sa oled oma kvoodi ammendanud. go_back = Mine tagasi -invalid_data = Kehtetud andmed: %v +invalid_data = Vigased andmed: %v never = Mitte kunagi unknown = Teadmata -rss_feed = RSS infovoog -confirm_delete_artifact = Kas oled kindel et soovite artefakti "%s" kustutada? -pin = +rss_feed = RSS-voog +confirm_delete_artifact = Kas oled kindel et soovid „%s“ artefakti kustutada? +pin =Tõsta esile artifacts = Artefaktid archived = Arhiveeritud -concept_system_global = Ülemaailmne +concept_system_global = Üldine concept_user_individual = Individuaalne -concept_code_repository = Hoidla +concept_code_repository = Lähtekoodi hoidla concept_user_organization = Organisatsioon -show_timestamps = Näita ajatemplid -show_log_seconds = Näita sekundit -download_logs = Logide allalaadimine +show_timestamps = Näita ajatempleid +show_log_seconds = Näita sekundeid +download_logs = Laadi logid alla name = Nimi value = Väärtus filter = Filter -filter.clear = Tühjendage filtrid +filter.clear = Tühjenda filtrid filter.is_archived = Arhiveeritud -filter.not_archived = Mitte arhiveeritud -filter.is_fork = Harud -filter.not_fork = Mitte harud -filter.is_mirror = Peegelpiltid -filter.not_mirror = Mitte peegelpiltid +filter.not_archived = Arhiveerimata +filter.is_fork = Koodiharud +filter.not_fork = Pole koodiharud +filter.is_mirror = Peegelpildid +filter.not_mirror = Pole peegelpilte filter.is_template = Mallid -filter.not_template = Mitte Mallid +filter.not_template = Pole mallid filter.public = Avalik filter.private = Privaatne rerun_all = Käivita uuesti kõik tööd new_mirror = Uus peegelpilt copy_generic = Kopeeri lõikelauale -confirm_delete_selected = Kinnitage et kustutada kõik valitud elemendid? -show_full_screen = Näita täisekraanil +confirm_delete_selected = Kas kinnitad kõikide valitud objektide kustutamise? +show_full_screen = Näita täisekraanivaates + +powered_by = Siin on kasutusel %s +captcha = Robotilõks +copy_path = Kopeeri asukoht +unpin = Lõpeta esiletõstmine [search] -search = Otsi... +search = Otsi… fuzzy = Hägus -fuzzy_tooltip = Lisage tulemused mis vastavad ka otsingu terminile +fuzzy_tooltip = Lisa tulemused mis lähedalt vastavad otsingusõnale union = Märksõnad exact = Täpne -exact_tooltip = Sisaldab ainult tulemusi mis vastavad täpsele otsingusõnale -repo_kind = Otsi hoidlad… +exact_tooltip = Sisaldab ainult tulemusi, mis vastavad täpsele otsingusõnale +repo_kind = Otsi lähtekoodihoidlad… user_kind = Otsi kasutajaid… org_kind = Otsi organisatsioone… -team_kind = Otsi meeskonnad… +team_kind = Otsi tiime… code_kind = Otsi koodi… code_search_by_git_grep = Praeguse koodi otsingu tulemused annab "git grep". Paremaid tulemusi võib saada, kui saidi administraator lubab koodi indekseerija. package_kind = Otsi pakette… @@ -160,41 +165,55 @@ no_results = Sobivaid tulemusi ei leitud. issue_kind = Otsi probleeme… milestone_kind = Otsi verstapostid... type_tooltip = Otsingu tüüp -code_search_unavailable = Koodide otsing ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. -union_tooltip = Sisaldab tulemused mis vastavad mis tahes tühikutega eraldatud võtmesõnadele -keyword_search_unavailable = Otsing märksõna järgi ei ole praegu saadaval. Palun võtke ühendust saidi administraatoriga. +code_search_unavailable = Koodiotsing ei ole praegu saadaval. Palun võta ühendust saidi administraatoriga. +union_tooltip = Kaasa tulemusi, mis vastavad mis tahes tühikutega eraldatud märksõnadele +keyword_search_unavailable = Otsing märksõna järgi ei ole praegu saadaval. Palun võtke ühendust saidi haldajaga. pull_kind = Otsi tõmbepäringuid… [aria] navbar = Navigatsiooniriba -footer.software = Selle tarkvara kohta +footer.software = Teave selle tarkvara kohta footer.links = Lingid +footer = Jalus + [heatmap] -number_of_contributions_in_the_last_12_months = %s panused viimase 12 kuu jooksul -contributions_zero = Panused ei ole +number_of_contributions_in_the_last_12_months = %s kaastööd viimase 12 kuu jooksul +contributions_zero = Kaastöid ei ole contributions_format = {contributions} {day} {month}, {year} -contributions_few = panused +contributions_few = kaastööd less = Vähem more = Rohkem -contributions_one = panus +contributions_one = kaastöö [editor] buttons.heading.tooltip = Lisa pealkiri -buttons.italic.tooltip = Lisa kursiivne tekst -buttons.quote.tooltip = Tsitaadi tekst +buttons.italic.tooltip = Lisa kaldkirjas tekst +buttons.quote.tooltip = Tsiteeri teksti buttons.code.tooltip = Lisa kood buttons.link.tooltip = Lisa link buttons.list.ordered.tooltip = Lisa nummerdatud nimekiri buttons.list.unordered.tooltip = Lisa nimekiri buttons.list.task.tooltip = Lisa ülesannete nimekiri -buttons.ref.tooltip = Viide probleemile või tõmbepäringule -buttons.switch_to_legacy.tooltip = Kasutage selle asemel pärandredaktorit -buttons.enable_monospace_font = Võimalda püsisammkiri -buttons.disable_monospace_font = Lülita välja püsisammkiri -buttons.indent.tooltip = Pesa esemed ühe taseme võrra -buttons.bold.tooltip = Lisa rasvane tekst -buttons.mention.tooltip = Mainige kasutajat või meeskonda +buttons.ref.tooltip = Viide probleemile või mestimispäringule +buttons.switch_to_legacy.tooltip = Selle asemel kasuta pärandredaktorit +buttons.enable_monospace_font = Kasuta püsisammkirja +buttons.disable_monospace_font = Lülita püsisammkiri välja +buttons.indent.tooltip = Liiguta objekte ühe taseme võrra +buttons.bold.tooltip = Lisa paks tekst +buttons.mention.tooltip = Maini kasutajat või tiimi + +buttons.unindent.tooltip = Võta tagasi objektide liigutamine ühe taseme võrra +buttons.new_table.tooltip = Lisa tabel +table_modal.header = Lisa tabel +table_modal.placeholder.header = Päis +table_modal.placeholder.content = Sisu +table_modal.label.rows = Read +table_modal.label.columns = Veerud +link_modal.header = Lisa link +link_modal.url = Võrguaadress +link_modal.description = Kirjeldus +link_modal.paste_reminder = Soovitus: kui link on uba lõikelaual olemas, siis võis ta otse lisada ja link luuakse automaatselt. [filter] string.asc = A - Z @@ -205,114 +224,179 @@ occurred = Tekkis viga invalid_csrf = Halb taotlus: vigane CSRF token not_found = Sihtmärki ei leitud. network_error = Võrguviga -server_internal = Sisemine serveri viga -report_message = Kui usute et tegemist on Forgejo veaga siis otsige probleeme Codebergist või avage vajadusel uus probleem. +server_internal = Serveri sisemine viga +report_message = Kui usud, et tegemist on Forgejo veaga, siis otsige sarnaseid vigu Codebergist või vajadusel koosta uus veakirjeldus. [startpage] -app_desc = Valutu, isehostitatud Git'i teenus +app_desc = Muretu Git'i teenus sinu omas serveris install = Lihtne paigaldada platform = Platvormiülene -platform_desc = Forgejo on kinnitust leidnud et töötab nii libre operatsioonisüsteemides nagu Linux ja FreeBSD, kui ka erinevatel protsessorarhitektuuridel. Valige see mis teile meeldib! +platform_desc = On kinnitust leidnud, et Forgejo töötab nii avatud operatsioonisüsteemides nagu Linux ja FreeBSD, kui ka erinevatel protsessorarhitektuuridel. Vali see mis sulle meeldib! lightweight = Kergekaaluline -lightweight_desc = Forgejo on väikeste miinimumnõuetega ja seda saab kasutada odaval Raspberry Pi'l. Säästa oma masina energiat! +lightweight_desc = Forgejo on väikeste miinimumnõuetega ja seda saad kasutada odaval Raspberry Pi'l. Säästa oma masina energiat! license = Avatud lähtekood -install_desc = Lihtsalt käivitage oma platvormi binaarsüsteem, tarnige see koos Dockeriga, või saada see pakendatud. -license_desc = Mine võta Forgejo! Liitu meiega andes oma panuse et muuta see projekt veelgi paremaks. Ärge häbenege olla kaasaaitaja! +install_desc = Lihtsalt käivita rakenduse fail oma platvormil, pane ta tööle Dockeriga või laadi ta pakendatuna. +license_desc = Laadi Forgejo alla! Liitu meiega andes oma panuse, et muuta see projekt veelgi paremaks. Ära pelga osalemist! [install] install = Paigaldamine title = Esialgne konfiguratsioon -docker_helper = Kui käivitate Forgejo't Dockeri sees, lugege dokumentatsiooni enne seadete muutmine. -require_db_desc = Forgejo vajab MySQL, PostgreSQL, SQLite3 või TiDB (MySQL protokoll). -db_title = Andmebaasi seaded +docker_helper = Kui käivitad Forgejo't Dockeris, loe enne seadistuste muutmist dokumentatsiooni. +require_db_desc = Forgejo vajab tööks ühte järgnevast andmebaasidest: MySQL, PostgreSQL, SQLite3 või TiDB (MySQL protokoll). +db_title = Andmebaasi seadistused db_type = Andmebaasi tüüp -host = Vastuvõtja +host = Server user = Kasutajanimi -password = Parool +password = Salasõna db_name = Andmebaasi nimi -db_schema = Skeem -db_schema_helper = Jäta tühjaks andmebaasi vaikimisi ("avalik"). +db_schema = Andmeskeem +db_schema_helper = Vaikimisi valiku kasutamiseks jäta tühjaks („public“). ssl_mode = SSL -path = Tee -sqlite_helper = SQLite3 andmebaasi failitee.
    Sisestage absoluutne tee, kui käivitate Forgejo't teenusena. -reinstall_error = Sa üritad installeerida olemasolevasse Forgejo andmebaasi -reinstall_confirm_message = Olemasoleva Forgejo andmebaasi uuesti paigaldamine võib põhjustada mitmeid probleeme. Enamasti peaksite Forgejo käivitamiseks kasutama olemasolevat "app.ini". Kui te teate, mida teete, kinnitage järgmist: -reinstall_confirm_check_1 = Andmed, mis on krüpteeritud SECRET_KEY'ga app.ini's, võivad kaduda: kasutajad ei pruugi saada 2FA/OTP-ga sisse logima ja peegelpiltid ei pruugi õigesti toimida. Selle kasti märkimisega kinnitate, et praegune app.ini fail sisaldab õiget SECRET_KEY'd. -reinstall_confirm_check_3 = Te kinnitate, et olete täiesti kindel, et see Forgejo töötab õiges app.ini asukohas ja et olete kindel, et peate uuesti installima. Te kinnitate, et tunnistate ülaltoodud riske. -err_empty_db_path = SQLite3 andmebaasi tee ei saa olla tühi. -no_admin_and_disable_registration = Kasutajate iseregistreerimist ei saa keelata ilma administraatori kontot loomata. -err_empty_admin_password = Administraatori parool ei saa olla tühi. -err_empty_admin_email = Administraatori e-posti aadress ei saa olla tühi. -err_admin_name_is_reserved = Administraatori kasutajanimi on kehtetu, kasutajanimi on reserveeritud -err_admin_name_pattern_not_allowed = Administraatori kasutajanimi on kehtetu, kasutajanimi vastab reserveeritud mustrile -err_admin_name_is_invalid = Administraatori kasutajanimi on kehtetu -general_title = Üldised seaded -app_name = Instantsi pealkiri -app_name_helper = Sisestage siia oma instantsi nimi. See kuvatakse igal leheküljel. -app_slogan = Instantse loosung -repo_path = Hoidla juurte tee -lfs_path = Git LFS'i juurte tee -lfs_path_helper = Failid jälgitatud Git LFS'ist salvestatakse sellesse kaustale. Jätke tühjaks et välja lülitada. +path = Asukoht +sqlite_helper = SQLite3 andmebaasi aukoht.
    Kui käivitad Forgejo'd teenusena, siis sisesta absoluutne asukoht. +reinstall_error = Sa üritad paigaldada olemasolevasse Forgejo andmebaasi +reinstall_confirm_message = Olemasoleva Forgejo andmebaasi uuesti paigaldamine võib põhjustada mitmeid probleeme. Enamasti peaksid Forgejo käivitamiseks kasutama olemasolevat „app.ini“. Kui te tead, mida teed, kinnita järgmist: +reinstall_confirm_check_1 = Andmed, mis on krüpteeritud SECRET_KEY'ga app.ini's, võivad kaduda: kasutajad ei pruugi saada sisselogimisel kaasutada 2FA/OTP võimalusi ja peegelpildid ei pruugi õigesti toimida. Selle kasti märkimisega kinnitad, et praegune app.ini fail sisaldab õiget SECRET_KEY'd. +reinstall_confirm_check_3 = Sa kinnitad, et oled täiesti kindel, et see Forgejo töötab õiges app.ini asukohas ja et oled kindel, et pead uuesti paigaldama. Sa kinnitad, et oled teadlik ülaltoodud riskidest. +err_empty_db_path = SQLite3 andmebaasi asukoht ei saa olla tühi. +no_admin_and_disable_registration = Kasutajate iseregistreerimist ei saa keelata ilma peakasutaja kontot loomata. +err_empty_admin_password = Peakasutaja salasõna ei saa olla tühi. +err_empty_admin_email = Peakasutaja e-posti aadress ei saa olla tühi. +err_admin_name_is_reserved = Peakasutaja kasutajanimi on vigane, kasutajanimi on reserveeritud +err_admin_name_pattern_not_allowed = Peakasutaja kasutajanimi on vigane, kasutajanimi vastab reserveeritud mustrile +err_admin_name_is_invalid = Peakasutaja kasutajanimi on vigane +general_title = Üldised seadistused +app_name = Serveri nimi või pealkiri +app_name_helper = Sisestage siia oma serveri või teenuse nimi. See kuvatakse igal leheküljel. +app_slogan = Serveri tunnuslause +repo_path = Hoidla juurkausta asukoht +lfs_path = Git LFS'i juurkausta asukoht +lfs_path_helper = Git LFS'ist jälgitud failid salvestatakse siia kausta. Kui jätad tühjaks, siis pole see võimalus kasutusel. run_user = Kasutaja kellena käivitada -run_user_helper = Operatsioonisüsteemi kasutajanimi, mille all Forgejo töötab. Pange tähele, et sellel kasutajal peab olema juurdepääs hoidlate juurte teele. +run_user_helper = Operatsioonisüsteemi kasutajanimi, kellena Forgejo töötab. Pane tähele, et sellel kasutajal peab olema juurdepääs hoidlate juurkaustale. domain = Serveri domeen -domain_helper = Serveri domeen või hostiaadress. +domain_helper = Serveri domeen või hosti aadress. ssh_port = SSH-serveri port -ssh_port_helper = Pordi number, mida SSH-server kasutab. Jätke tühjaks et välja lülitada SSH-serveri. -http_port = HTTP-kuulamise port +ssh_port_helper = Pordi number, mida SSH-server kasutab. SSH-serveri väljalülitamiseks jäta tühjaks. +http_port = Serveri HTTP port http_port_helper = Pordi number, mida Forgejo veebiserver kasutab. -app_url = Baasi URL -app_url_helper = Baasaadress HTTP(S) kloonimise URL-ide ja e-posti teadete jaoks. -log_root_path = Logi tee -log_root_path_helper = Logifailid kirjutatakse sellesse kaustale. -optional_title = Vabatahtlikud seaded -email_title = E-posti seaded -smtp_addr = SMTP vastuvõtja +app_url = Juur-võrguaadress +app_url_helper = Lähteaadress HTTP(S) kloonimise võrguaadresside ja e-posti teadete jaoks. +log_root_path = Logi asukoht +log_root_path_helper = Logifailid kirjutatakse siia kausta. +optional_title = Täiendavad seadistused +email_title = E-posti seadistused +smtp_addr = SMTP server smtp_port = SMTP port smtp_from = Saada e-kirjad nagu -smtp_from_invalid = "Saada e-kirjad nagu" aadress on kehtetu -smtp_from_helper = E-posti aadress, mida Forgejo kasutab. Sisestage tavaline e-posti aadress või kasutage formaati "Nimi" . +smtp_from_invalid = „Saada e-kirjad nagu“ aadress on vigane +smtp_from_helper = E-posti aadress, mida Forgejo kasutab. Sisesta tavaline e-posti aadress või kasutage vormingut "Nimi" . mailer_user = SMTP kasutajanimi -mailer_password = SMTP parool -register_confirm = Registreerimiseks on vaja e-posti kinnitust -mail_notify = Lubage e-posti teated -server_service_title = Serveri ja kolmanda osapoole teenuste seaded -offline_mode = Lülita sisse lokaalse režiimi -disable_gravatar = Lülita välja Gravatar -federated_avatar_lookup = Lülita sisse föderaalsed avatarid -federated_avatar_lookup.description = Otsige avatare kasutades Libravatar'i. +mailer_password = SMTP salasõna +register_confirm = Registreerimiseks on vaja e-posti kinnitamist +mail_notify = Kasuta teavitamist e-posti teel +server_service_title = Serveri ja kolmanda osapoole teenuste seadistused +offline_mode = Lülita sisse lokaalne režiim +disable_gravatar = Lülita Gravatar välja +federated_avatar_lookup = Kaasuta födereeritud tunnuspilte +federated_avatar_lookup.description = Otsi tunnuspilte Libravatari teenusest. disable_registration = Lülita välja iseregistreerimine allow_only_external_registration = Luba registreerimine ainult väliste teenuste kaudu allow_only_external_registration.description = Kasutajad saavad uusi kontosid luua ainult seadistatud väliste teenuste abil. -openid_signin = Lülita sisse OpenID sisselogimise -openid_signin.description = Luba kasutajatel OpenID kaudu sisse logida. -openid_signup = Lülita sisse OpenID iseregistreerimine -enable_captcha = Lülita sisse registreerimise CAPTCHA -enable_captcha.description = Nõudke kasutajatelt CAPTCHA läbimist kontode loomiseks. -require_sign_in_view = Nõua sisselogimist et vaadata instantsi sisu -default_keep_email_private = Peida e-posti aadressid vaikimisi -default_keep_email_private.description = Lülita sisse uute kasutajate e-posti aadressi varjamine vaikimisi, et see teave ei lekiks kohe pärast registreerimist. -default_allow_create_organization = Lubada organisatsioonide loomine vaikimisi -default_enable_timetracking = Aja jälgimise sisselülitamine vaikimisi -default_enable_timetracking.description = Lubage uute repositooriumide jaoks vaikimisi aja jälgimise funktsiooni kasutamine. -admin_title = Administraatori konto seaded -admin_setting.description = Administraatori konto loomine on vabatahtlik. Esimesest registreeritud kasutajast saab automaatselt administraator. -admin_name = Administraatori kasutajanimi -admin_password = Parool -confirm_password = Parooli kinnitamine +openid_signin = Kasuta OpenID abil sisselogimist +openid_signin.description = Luba kasutajatel OpenID abil sisse logida. +openid_signup = Kasuta OpenID abil iseregistreerimist +enable_captcha = Kasuta registreerimisel robotilõksu +enable_captcha.description = Eelda, et kasutajad lahendavad registreerimisel robotilõksu ülesande. +require_sign_in_view = Serveri sisu vaatamiseks eelda sisselogimist +default_keep_email_private = Vaikimisi peida e-posti aadressid +default_keep_email_private.description = Lülita sisse uute kasutajate e-posti aadresside vaikimisi varjamine - see väldib vastava teabe lekkimist kohe pärast registreerimist. +default_allow_create_organization = Vaikimisi luba organisatsioonide loomine +default_enable_timetracking = Vaikimisi lülita sisse aja jälgimine +default_enable_timetracking.description = Vaikimisi luba uute lähtekoodihoidlate jaoks aja jälgimise funktsiooni kasutamise. +admin_title = Peakasutaja konto seaded +admin_setting.description = Peakasutaja konto loomine on vabatahtlik. Esimesest registreeritud kasutajast saab automaatselt peakasutaja. +admin_name = Peakasutaja kasutajanimi +admin_password = Salasõna +confirm_password = Kinnita salasõna admin_email = E-posti aadress -config_location_hint = Need konfiguratsioonivalikud salvestatakse sees: +config_location_hint = Need konfiguratsioonivalikud salvestatakse siin: install_btn_confirm = Paigalda Forgejo -test_git_failed = Ei saanud testida käsku "git": %v -invalid_db_setting = Andmebaasi seaded on vigased: %v -invalid_db_table = Andmebaasi tabel "%s" on vigane: %v +test_git_failed = Ei saanud testida käsku „git“: %v +invalid_db_setting = Andmebaasi seadistused on vigased: %v +invalid_db_table = Andmebaasi tabel „%s“ on vigane: %v allow_dots_in_usernames = Luba kasutajatel kasutada oma kasutajanimedes punkte. Ei mõjuta olemasolevaid kontosid. -default_allow_create_organization.description = Lubage uutel kasutajatel vaikimisi luua organisatsioone. Kui see valik on välja lülitatud, peab administraator andma uutele kasutajatele organisatsioonide loomise loa. -disable_gravatar.description = Lülita välja Gravatari või muude kolmandate osapoolte avatariallikate kasutamine. Kasutajate avatarite jaoks kasutatakse vaikimisi pilte, kui nad ei lae oma avatari üles. -openid_signup.description = Luba kasutajatel luua kontosid OpenID kaudu, kui iseregistreerimine on sisse lülitatud. -require_sign_in_view.description = Piirake sisule juurdepääsu sisselogitud kasutajatele. Külalised saavad külastada ainult autentimislehti. -reinstall_confirm_check_2 = Hoidlad ja seadeid võib olla vaja uuesti sünkroniseerida. Selle kasti märkimisega kinnitate, et sünkroniseerite hoidlate ja authorized_keys'i faili konksud käsitsi uuesti. Te kinnitate, et tagate, et hoidlate ja peegelpilti seaded on õiged. -app_slogan_helper = Sisestage siia oma loosung. Jätke tühjaks, et välja lülitada. -repo_path_helper = Kauged Git-hoidlad salvestatakse sellesse kaustale. -sqlite3_not_available = See Forgejo versioon ei toeta SQLite3. Palun laadige alla ametlik binaarversioon %s (mitte "gobuild"i versioon). -offline_mode.description = Lülitage kolmandate osapoolte sisu edastamise võrgud välja ja teenindage kõiki ressursse lokaalselt. \ No newline at end of file +default_allow_create_organization.description = Luba uutel kasutajatel vaikimisi luua organisatsioone. Kui see valik on välja lülitatud, peab peakasutaja andma uutele kasutajatele organisatsioonide loomise loa. +disable_gravatar.description = Lülita välja Gravatari või muude kolmandate osapoolte tunnuspiltide allikate kasutamine. Kasutajate tunnuspiltidena kasutatakse vaikimisi pilte, kui nad enda oma ei laadi üles. +openid_signup.description = Kui iseregistreerimine on kasutusel, luba kasutajatel luua kontosid OpenID abil. +require_sign_in_view.description = Piira juurdepääsu vaid sisselogitud kasutajatega. Külalised saavad külastada ainult autentimislehti. +reinstall_confirm_check_2 = Hoidlad ja seadistused võivad uuesti vajada sünkroniseerimist. Selle kasti märkimisega kinnitad, et sünkroniseerid hoidlate ja authorized_keys'i faili haagid käsitsi uuesti. Sa kinnitad, et tagad, et hoidlate ja peegelpiltide seadistused on õiged. +app_slogan_helper = Sisesta siia oma serveri tunnuslause. Kui jätad tühjaks, siis pole tunnuslause kasutusel. +repo_path_helper = Kaugseadmete Git-hoidlad salvestatakse siia kausta. +sqlite3_not_available = See Forgejo versioon ei toeta SQLite3 andmebaasi. Palun laadi alla ametlik binaarversioon %s (mitte „gobuild“-versioon). +offline_mode.description = Lülitage kolmandate osapoolte sisuedastusvõrgud välja ja jaga kõiki ressursse kohalikust serverist. +password_algorithm = Salasõna räsialgoritm +invalid_password_algorithm = Vigane salasõna räsialgoritm + +[auth] +forgot_password_title = Ununenud salasõna +forgot_password = Kas salasõna ununes? +must_change_password = Muuda oma salasõna +allow_password_change = Eelda, et kasutajad muudavad oma salasõna (soovitatav) + +[mail] +password_change.subject = Sinu salasõna on muutunud +password_change.text_1 = Sinu kasutajakonto salasõna on just muutunud. +totp_disabled.text_1 = Lisaautentimine ehk ajapõhise salasõna (TOTP) kasutamine on sinu kasutajakontol just välja lülitatud. + +[form] +Password = Salasõna +Retype = Korda salasõna +password_not_match = Salasõnad ei klapi. +username_password_incorrect = Kassutajanimi või salasõna pole õige. + +[settings] +update_password = Muuda salasõna +old_password = Senine salasõna +new_password = Uus salasõna +retype_new_password = Korda uut salasõna +password_incorrect = Senine salasõna pole õige. +change_password_success = Sina salasõna on nüüd muudetud. Edaspidi kasuta sisselogimiseks seda uut salasõna. + +[repo] +mirror_interval_invalid = Peegelpildi välp pole korrektne. +mirror_public_key = Avalik SSH-võti +mirror_use_ssh.text = Kasuta SSH-autentimist +mirror_sync = sünkroonis +mirror_sync_on_commit = Sünkrooni sissekannete tegemisel +mirror_address = Klooni võrguaadressilt +mirror_password_placeholder = (Muutmata) +mirror_password_blank_placeholder = (Seadistamata) +mirror_password_help = Salvestatud salasõna kustutamiseks muuda kasutajanime. +commits.search_branch = See haru +commits.search_all = Kõik harud +commits.author = Autor +commits.message = Sõnum +commits.browse_further = Sirvi edasi +commits.renamed_from = Nimi muudetud, vana nimi oli „%s“ +commits.date = Kuupäev +commits.older = Vanemad +commits.newer = Uuemad +commitstatus.error = Viga +commitstatus.failure = Tõrge +commitstatus.pending = Ootel +commitstatus.success = Valmis +ext_issues = Välised vead +projects = Projektid + +[admin] +users.password_helper = Kui sa ei taha salasõna muuta, siis jäta väli tühjaks. +auths.bind_password = Seo salasõna +config.reset_password_code_lives = Taastekoodi aegumine + +[actions] +variables = Muutujad +variables.management = Halda muutujaid +variables.creation = Lisa muutuja +variables.none = Muutujaid veel pole. +variables.deletion = Eemalda muutuja \ No newline at end of file diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index 23cb74f814..f8199da658 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -301,7 +301,7 @@ federated_avatar_lookup=فعال سازی آواتار مشترک federated_avatar_lookup.description=پیدا کردن آواتار با استفاده از Libravatar. disable_registration=غیرفعال‌کردن خود ثبت نامی disable_registration.description=غیرفعال کردن ثبت نام کاربر. تنها مدیر ها قادر خواهند بود حساب کاربری جدید اضافه کنند. -allow_only_external_registration.description=اجازه ثبت نام فقط از طریق خدمات خارجی +allow_only_external_registration.description=اجازه ثبت نام فقط از طریق خدمات خارجی. openid_signin=فعالسازی ورود با OpenID openid_signin.description=فعالسازی ورود کاربر با OpenID. openid_signup=فعالسازی ثبت نام با OpenID @@ -2762,7 +2762,7 @@ directory = پوشه [search] type_tooltip = جستجو گونه -search = جستجو... +search = جستجو… fuzzy = درهم fuzzy_tooltip = پیامدهایی را درج کنید که دقیقا با عبارت جستجو همخوانی داشته باشند regexp = عبارات باقاعده diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index 487768ea22..c28e90c87a 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -1034,6 +1034,8 @@ regenerate_token = I-regenerate access_token_regeneration_desc = Ang pag-regenerate ng token ay babawiin ang access sa iyong account para sa mga application na gumagamit nito. Hindi ito mababawi. Magpatuloy? regenerate_token_success = Na-generate muli ang token. Ang mga application na gumagamit nito ay hindi na maa-access ang iyong account at dapat ma-update gamit ang bagong token. +ssh_token_help_ssh_agent = o, kung gumagamit ka ng SSH agent (na nakatakda ang variable na SSH_AUTH_SOCK): + [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. clone_helper = Kailangan ng tulong sa pagpili? Bisitahin ang Tulong. diff --git a/options/locale/locale_hi.ini b/options/locale/locale_hi.ini index 36e4bf786f..9279f35b59 100644 --- a/options/locale/locale_hi.ini +++ b/options/locale/locale_hi.ini @@ -97,4 +97,112 @@ copy = कॉपी copy_generic = क्लिपबोर्ड पर कॉपी करें copy_url = URL कॉपी करें copy_hash = कॉपी हैश -copy_path = राह कॉपी करें \ No newline at end of file +copy_path = राह कॉपी करें +copy_content = विषय-वस्तु कॉपी करें +copy_branch = शाखा नाम कॉपी करें +copy_success = कॉपी हो गया! +copy_error = कॉपी नहीं हुआ +copy_type_unsupported = इस तरह की फाइल कॉपी नहीं होगी +write = लिखें +preview = पूर्वावलोकन +loading = लोड हो रहा… +error = त्रुटि +error404 = जो पृष्ट आप देखना चाहते हैं या तो है ही नहीं , हटा दिया गया या आप देखने को अधिकृत नहीं की देख पाएं। +error413 = आपका कोटा ख़तम हो गया। +go_back = वापस जाएं +invalid_data = डाटा मान्य नहीं: %v +never = कभी नहीं +unknown = अनजान +rss_feed = RSS फीड +pin = पिन +unpin = पिन हटाएं +artifacts = पुरावशेष +confirm_delete_artifact = क्या आप ये पुरावशेष हटाना चाहते हैं "%s" ? +archived = संग्रहीत +concept_system_global = वैश्विक +concept_user_individual = एकल +concept_code_repository = रिपॉजिटरी +concept_user_organization = संस्था +show_timestamps = समय-मोहर दिखाएं +show_log_seconds = सेकंड दिखाएं +show_full_screen = पूरी स्क्रीन पे दिखाएं +download_logs = लॉग डाउनलोड करें +confirm_delete_selected = ये सारे डिलीट कर दें? +name = नाम +value = मूल्य +filter = फ़िल्टर +filter.clear = फ़िल्टर हटाएं +filter.is_archived = संगृहीत +filter.not_archived = संगृहीत नहीं +filter.is_fork = फोर्क्स +filter.not_fork = फोर्क्स नहीं +filter.is_mirror = मिर्रोर्स +filter.not_mirror = मिर्रोर्स नहीं +filter.is_template = टेम्पलेट्स +filter.not_template = टेम्पलेट्स नहीं +filter.public = सार्वजनिक +filter.private = निजी + +[search] +search = ढूंढें… +type_tooltip = ढूंढ़ने का जैसे +fuzzy = फ़ज़्ज़ी +fuzzy_tooltip = जो उपाय ढूंढे गए टर्म से जुड़े हैं भी दिखाएं +union = केंद्र +union_tooltip = वो उपाय भी दिखाएं जो जगह छोढ़े कीवर्ड्स से मिलते हैं +exact = एकदम +exact_tooltip = सिर्फ वो रिजल्ट दिखाएं जो एकदम सर्च टर्म से मिलते हैं +regexp = रेगएक्सप् +regexp_tooltip = व्याख्या करें सर्च टर्म की व्यावहारिक तरीके से +repo_kind = रेपो ढूंढें… +user_kind = यूजर ढूंढें… +org_kind = संस्था ढूंढें… +team_kind = टीम ढूंढें… +code_kind = कोड ढूंढें… +code_search_unavailable = कोड ढूंढ़ना अभी नहीं हो सकता। कृपया एडमिनिस्ट्रेटर से बात करें। +package_kind = पैकेजेस ढूंढें… +project_kind = प्रोजेक्ट्स ढूंढें… +branch_kind = शाखा ढूंढें… +commit_kind = कमिट्स ढूढें… +runner_kind = रनर्स ढूंढें… +no_results = मिलता उपाय नहीं है। +issue_kind = इश्यूज ढूंढें… +pull_kind = पुल्स ढूंढें… +keyword_search_unavailable = कीवर्ड से ढूंढ़ना अभी नहीं हो सकता। कृपया एडमिनिस्ट्रेटर से बात करें। + +[aria] +navbar = संचालन बार +footer = फुटर +footer.software = इस सॉफ्टवेयर के बारे में +footer.links = संयोजक + +[heatmap] +number_of_contributions_in_the_last_12_months = %s योगदान पिछले 12 महीनो में +contributions_zero = कोई योगदान नहीं +contributions_format = {योगदान} आज {दिन} {महीना} {साल} +contributions_one = योगदान +contributions_few = योगदानों +less = कम +more = ज़्यादा + +[editor] +buttons.heading.tooltip = शीर्षक जोड़ें +buttons.bold.tooltip = बोल्ड लेख जोड़ें +buttons.italic.tooltip = इटैलिक लेख जोड़ें +buttons.quote.tooltip = पाठ जोड़ें +buttons.code.tooltip = कोड जोड़ें +buttons.link.tooltip = संयोजक जोड़ें +buttons.list.unordered.tooltip = बुलेट लिस्ट जोड़ें +buttons.list.ordered.tooltip = अंकित लिस्ट जोड़ें +buttons.list.task.tooltip = कार्यों की सूचि जोड़ें +buttons.mention.tooltip = यूजर या टीम को ज़ाहिर करें +buttons.ref.tooltip = इशू या पुल्ल निवेदन ज़ाहिर करें +buttons.switch_to_legacy.tooltip = पुराना एडिटर इस्तेमाल करें +buttons.enable_monospace_font = एकसमान रिक्ति फ़ॉन्ट चालू करें +buttons.disable_monospace_font = एकसमान रिक्ति फ़ॉन्ट बंद करें +buttons.indent.tooltip = चीज़ों को एक लेवल नेस्ट करें +buttons.unindent.tooltip = चीज़ों को एक लेवल नेस्ट से निकालें +buttons.new_table.tooltip = टेबल जोड़ें +table_modal.header = टेबल जोड़ें +table_modal.placeholder.header = शीर्षक +table_modal.placeholder.content = विषयवस्तु \ No newline at end of file diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 4a98e1aa9d..647a306e4a 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -1090,6 +1090,8 @@ access_token_regeneration = Izveidot piekļuves pilnvaru no jauna regenerate_token_success = Pilnvara tika izveidota no jauna. Lietotnēm, kas to izmanto, vairs nav piekļuve kontam, un tajās ir jāizmanto jaunā pilnvara. access_token_regeneration_desc = Pilnvaras izveidošana no jauna atsauks piekļuvi kontam lietotnēm, kuras to izmanto. Darbība ir neatgriezeniska. Turpināt? +ssh_token_help_ssh_agent = vai, ja izmanto SSH aģentu (ar iestatītu mainīgo SSH_AUTH_SOCK): + [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. owner=Īpašnieks diff --git a/options/locale/locale_nds.ini b/options/locale/locale_nds.ini index fe504b2a04..2bcd2c5cf7 100644 --- a/options/locale/locale_nds.ini +++ b/options/locale/locale_nds.ini @@ -888,6 +888,8 @@ regenerate_token = Neei maken access_token_regeneration_desc = Wenn du een Teken neei maakst, verlesen Anwennens, wat ’t bruken, Togang to dienem Konto. Dat kann nich torüggnohmen worden. Wiedermaken? regenerate_token_success = Dat Teken is neei maakt worden. Anwennens, wat ’t bruken, hebben keenen Togang to dienem Konto mehr un mutten mit de nejen Teken verneeit worden. +ssh_token_help_ssh_agent = of, wenn du eenen SSH-Agenten bruukst (un de SSH_AUTH_SOCK-Variaabel sett is): + [repo] rss.must_be_on_branch = Du muttst up eenem Twieg wesen, um eenen RSS-Schuuv to hebben. admin.manage_flags = Flaggen verwalten diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 7f121395b9..f53ec2ad7c 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -1093,6 +1093,8 @@ regenerate_token = Opnieuw genereren regenerate_token_success = De token is opnieuw gegenereerd. Toepassingen die het gebruiken, hebben niet langer toegang tot uw account en moeten worden bijgewerkt om de nieuwe token te gebruiken. access_token_regeneration_desc = Als u een token opnieuw genereert, wordt de toegang tot uw account ingetrokken voor toepassingen die de token gebruiken. Dit kan niet ongedaan worden gemaakt. Doorgaan? +ssh_token_help_ssh_agent = of, als u een SSH-agent gebruikt (met de variabele SSH_AUTH_SOCK ingesteld): + [repo] owner=Eigenaar owner_helper=Sommige organisaties kunnen niet worden weergegeven in de dropdown vanwege een limiet op het maximale aantal repositories. diff --git a/options/locale/locale_ro.ini b/options/locale/locale_ro.ini index c52788a05e..ec4bcbdf13 100644 --- a/options/locale/locale_ro.ini +++ b/options/locale/locale_ro.ini @@ -15,7 +15,7 @@ concept_user_organization = Organizație logo = Logo help = Ajutor sign_up = Înregistrare -link_account = Conectare cont +link_account = Conectați conturi register = Înregistrare template = Șablon language = Limbă @@ -88,7 +88,7 @@ remove_label_str = Șterge elementul "%s" save = Salvează remove = Șterge copy_path = Copiază cale -error404 = Pagina pe care încerci să o vizitezi fie nu există sau nu ești autorizat să o vezi. +error404 = Pagina pe care încercați să o vizitați fie nu există, a fost ștearsă sau nu sunteți autorizat să o puteți vedea. filter.not_archived = Nearhivat activities = Activități confirm_delete_selected = Ștergi toate elementele selectate? @@ -101,9 +101,49 @@ home = Acasă dashboard = Panou de Control version = Versiune powered_by = Susținut de %s -active_stopwatch = Monitorizor de timp activ +active_stopwatch = Contor timp activ more_items = Mai multe elemente +tracked_time_summary = Rezumat al timpului monitorizat, bazat pe filtrele listei de probleme +signed_in_as = Conectat ca +toggle_menu = Afișează sau ascunde meniul +twofa_scratch = Cod de rezervă pentru autentificare prin doi factori +passcode = Cod de acces +repository = Repozitoriu +new_fork = Bifurcație de repozitoriu nouă +new_project_column = Coloană nouă +new_repo.title = Repozitoriu nou +new_repo.link = Repozitoriu nou +all = Tot +collaborative = Colaborativ +forks = Bifurcații +pull_requests = Cereri de extragere +issues = Probleme +milestones = Etape +rerun = Reporniți +rerun_all = Reporniți toate joburile +add_all = Adaugă toate +remove_all = Șterge toate +copy_branch = Copiați numele ramurii +write = Scrieți +error413 = V-ați epuizat cota. +invalid_data = Date invalide: %v +pin = Fixați +unpin = Desprindeți +concept_code_repository = Repozitoriu +show_timestamps = Afișați marcajele temporale +show_full_screen = Afișați pe tot ecranul +download_logs = Descărcați jurnalele +value = Valoare +filter.is_fork = Bifurcații +filter.not_fork = Nu sunt bifurcații +filter.is_mirror = Copii identice +filter.not_mirror = Nu sunt copii identice +filter.is_template = Șabloane +filter.not_template = Nu sunt șabloane +filter.public = Publice +filter.private = Private + [editor] table_modal.header = Adaugă tabel table_modal.placeholder.content = Conținut @@ -119,6 +159,20 @@ buttons.code.tooltip = Adaugă cod buttons.quote.tooltip = Citează text buttons.link.tooltip = Adaugă un link +buttons.heading.tooltip = Adăugați titlu +buttons.list.unordered.tooltip = Adăugați o listă +buttons.list.task.tooltip = Adăugați o listă de sarcini +buttons.ref.tooltip = Faceți o referire la o problemă sau o cerere de extragere +buttons.switch_to_legacy.tooltip = Folosiți în schimb editorul vechi +buttons.enable_monospace_font = Activați fontul monospațiat +buttons.disable_monospace_font = Dezactivați fontul monospațiat +buttons.indent.tooltip = Indentați obiectele cu un nivel +buttons.unindent.tooltip = Nu mai indentați obiectele cu un nivel +link_modal.header = Adăugați un link +link_modal.url = URL +link_modal.description = Descriere +link_modal.paste_reminder = Indiciu: Cu un URL salvat în clipboard, puteți lipi direct în editor pentru a crea un link. + [filter] string.asc = A - Z string.desc = Z - A @@ -127,10 +181,16 @@ string.desc = Z - A server_internal = Eroare internă a serverului network_error = Eroare de rețea +occurred = A apărut o eroare +report_message = Dacă credeți că aceasta este o problemă cu Forgejo, vă rugăm să căutați probleme pe Codeberg sau să deschideți o nouă problemă dacă este necesar. +not_found = Obiectul nu a putut fi găsit. + [startpage] install = Ușor de instalat license = Sursă deschisă +app_desc = Un serviciu Git fără probleme, auto-găzduit + [install] require_db_desc = Forgejo are nevoie de MySQL, PostgreSQL, SQLite3 sau TiDB (protocol MySQL). db_title = Setări bază de date @@ -220,16 +280,33 @@ invalid_db_setting = Setările pentru bază de date sunt invalide: %v no_reply_address = Domeniu pentru adrese de email ascunse [search] -user_kind = Caută utilizatori… -team_kind = Caută echipe… -code_kind = Caută cod… -project_kind = Caută proiecte… -package_kind = Caută pachete… -org_kind = Caută organizații… +user_kind = Căutați utilizatori… +team_kind = Căutați echipe… +code_kind = Căutați cod… +project_kind = Căutați proiecte… +package_kind = Căutați pachete… +org_kind = Căutați organizații… code_search_unavailable = Căutarea de cod nu este disponibilă momentan. Te rog contactează administratorul site-ului. keyword_search_unavailable = Căutarea după cuvânt cheie nu este disponibilă momentan. Te rog contactează administratorul site-ului. no_results = Nu a fost găsit niciun rezultat corespunzător. +search = Căutați… +type_tooltip = Tipul căutării +fuzzy = Aproximată +fuzzy_tooltip = Includeți rezultate care sunt asemănătoare termenului de căutare +union = Cuvinte cheie +union_tooltip = Includeți rezultate care sunt asemănătoare cuvintelor cheie separate prin spațiu +exact = Exactă +exact_tooltip = Includeți doar rezultate care se potrivesc exact termenului de căutare +regexp = Expresie regulată +regexp_tooltip = Interpretați termenul de căutare ca o expresie regulată +repo_kind = Căutați repozitorii… +branch_kind = Căutați ramuri… +commit_kind = Căutați comiteri… +runner_kind = Căutați executori… +issue_kind = Căutați probleme… +pull_kind = Căutați cereri de extragere… + [aria] navbar = Bară de navigare footer = Subsol diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index bf55a78c87..0cf3db37bc 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1080,6 +1080,8 @@ quota.sizes.assets.all = Ресурси access_token_regeneration_desc = Регенерація токена скасує доступ програм, які використовують цей токен, до вашого облікового запису. Це незворотна дія. Продовжити? regenerate_token_success = Токен згенеровано наново. Програми, які його використовують, більше не мають доступу до вашого облікового запису; ви повинні відновити доступ за допомогою нового токена. +ssh_token_help_ssh_agent = або, якщо ви використовуєте агент SSH (із встановленою змінною SSH_AUTH_SOCK): + [repo] owner=Власник owner_helper=Деякі організації можуть не відображатися у випадаючому списку через максимальну кількість репозиторііїв. @@ -2781,7 +2783,7 @@ blame.ignore_revs = Зміни в .git-blame-ignore-revs ігн blame.ignore_revs.failed = Не вдалося проігнорувати зміни в .git-blame-ignore-revs. template.git_hooks_tooltip = Наразі ви не можете змінювати або видаляти додані Git-хуки. Вибирайте лише якщо ви довіряєте репозиторію шаблону. migrate.github_token_desc = Ви можете ввести тут один або кілька токенів через кому, щоб пришвидшити міграцію в обхід обмеження частоти звернень до API GitHub. ОБЕРЕЖНО: зловживання цією функцією може порушити політику постачальника послуг і призвести до блокування облікового запису. -migrate.github.description = Перенесіть дані з github.com або сервера GitHub Enterprise. +migrate.github.description = Перенести дані з github.com або сервера GitHub Enterprise. migrate.cancel_migrating_confirm = Бажаєте скасувати перенесення? editor.new_branch_name = Укажіть назву нової гілки для цього коміту projects.column.edit = Редагувати стовпчик diff --git a/options/locale_next/locale_cs-CZ.json b/options/locale_next/locale_cs-CZ.json index 703d03aa83..67b875b72b 100644 --- a/options/locale_next/locale_cs-CZ.json +++ b/options/locale_next/locale_cs-CZ.json @@ -111,5 +111,6 @@ "feed.atom.link": "Zdroj Atom", "keys.ssh.link": "Klíče SSH", "keys.gpg.link": "Klíče GPG", - "mail.actions.run_info_sha": "Revize: %[1]s" + "mail.actions.run_info_sha": "Revize: %[1]s", + "repo.pulls.already_merged": "Sloučení selhalo: tato žádost již byla sloučena." } diff --git a/options/locale_next/locale_de-DE.json b/options/locale_next/locale_de-DE.json index c64e25b242..df8d8833dc 100644 --- a/options/locale_next/locale_de-DE.json +++ b/options/locale_next/locale_de-DE.json @@ -103,5 +103,6 @@ "keys.ssh.link": "SSH-Schlüssel", "keys.gpg.link": "GPG-Schlüssel", "profile.actions.tooltip": "Mehr Aktionen", - "mail.actions.run_info_sha": "Commit: %[1]s" + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.pulls.already_merged": "Zusammenführung fehlgeschlagen. Der Pull-Request wurde bereits zusammengeführt." } diff --git a/options/locale_next/locale_et.json b/options/locale_next/locale_et.json index a10447fa98..a1581dec0a 100644 --- a/options/locale_next/locale_et.json +++ b/options/locale_next/locale_et.json @@ -1,3 +1,34 @@ { - "search.milestone_kind": "Otsi verstapostid..." + "search.milestone_kind": "Otsi verstaposte…", + "home.welcome.no_activity": "Hetkel on siin tühjus", + "home.welcome.activity_hint": "Sinu uudisvoos ei leidu veel mitte midagi. Kui toimetad midagi sinu jälgitavates lähtekoodihoidlates, siis sinu tegevused ja aktiivsus on siin näha.", + "home.explore_repos": "Uuri lähtekoodi hoidlaid", + "home.explore_users": "Otsi kasutajaid", + "home.explore_orgs": "Tutvu organisatsioonidega", + "stars.list.none": "Keegi pole seda koodihoidlat veel tähekeega märgistanud.", + "watch.list.none": "Keegi pole seda koodihoidlat veel jälgima asunud.", + "followers.incoming.list.self.none": "Mitte keegi ei jälgi sinu kasutajaprofiili.", + "followers.incoming.list.none": "Mitte keegi ei jälgi seda kasutajat.", + "followers.outgoing.list.self.none": "Sina ei jälgi mitte kedagi.", + "followers.outgoing.list.none": "%s ei jälgi mitte kedagi.", + "relativetime.now": "praegu", + "relativetime.future": "tulevikus", + "relativetime.1day": "eile", + "relativetime.2days": "kaks päeva tagasi", + "relativetime.1week": "eelmisel nädalal", + "relativetime.2weeks": "kaks nädalat tagasi", + "relativetime.1month": "eelmisel kuul", + "relativetime.2months": "kaks kuud tagasi", + "relativetime.1year": "eelmisel aastal", + "relativetime.2years": "kaks aastat tagasi", + "themes.names.forgejo-auto": "Forgejo (süsteemi kujundus)", + "themes.names.forgejo-light": "Forgejo hele kujundus", + "themes.names.forgejo-dark": "Forgejo tume kujundus", + "error.not_found.title": "Lehte ei leidu", + "alert.range_error": " peab olema number %[1]s ja %[2]s vahel.", + "profile.edit.link": "Muuda profiili", + "feed.atom.link": "Atom-uudisvoog", + "keys.ssh.link": "SSH võtmed", + "keys.gpg.link": "GPG võtmed", + "meta.last_line": "Tänud, et oled Forgejo'd tõlkinud! Work hard and put in the effort, and love will come. (Tee tööd ja näe vaeva, siis tuleb armastus - A. H. Tammsaare)" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 20b35b8edb..2e1e3f5625 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -33,19 +33,19 @@ "relativetime.now": "ngayon", "relativetime.mins": { "one": "%d minuto ang nakalipas", - "other": "%d minuto ang nakalipas" + "other": "%d (na) minuto ang nakalipas" }, "relativetime.days": { "one": "%d araw ang nakalipas", - "other": "%d araw ang nakalipas" + "other": "%d (na) araw ang nakalipas" }, "relativetime.weeks": { "one": "%d linggo ang nakalipas", - "other": "%d linggo ang nakalipas" + "other": "%d (na) linggo ang nakalipas" }, "relativetime.years": { "one": "%d taon ang nakalipas", - "other": "%d taon ang nakalipas" + "other": "%d (na) taon ang nakalipas" }, "relativetime.2days": "2 araw ang nakalipas", "relativetime.2weeks": "2 linggo ang nakalipas", @@ -55,12 +55,12 @@ "relativetime.2years": "2 taon ang nakalipas", "relativetime.1day": "kahapon", "relativetime.hours": { - "one": "%d oras ang nakalipas", - "other": "%d oras ang nakalipas" + "one": "%d kras ang nakalipas", + "other": "%d (na) oras ang nakalipas" }, "relativetime.months": { "one": "%d buwan ang nakalipas", - "other": "%d buwan ang nakalipas" + "other": "%d (na) buwan ang nakalipas" }, "discussion.locked": "Naka-kandado ang pag-uusap na ito. Nilimitahan ang pagkomento sa mga tagatulong.", "relativetime.1month": "nakaraang buwan", @@ -103,5 +103,6 @@ "keys.ssh.link": "Mga SSH key", "keys.gpg.link": "Mga GPG key", "profile.actions.tooltip": "Higit pang mga aksyon", - "mail.actions.run_info_sha": "Commit: %[1]s" + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.pulls.already_merged": "Nabigo ang pagsasama: Naisama na ang hiling sa paghila na ito." } diff --git a/options/locale_next/locale_lv-LV.json b/options/locale_next/locale_lv-LV.json index 75835bc89c..30d789751a 100644 --- a/options/locale_next/locale_lv-LV.json +++ b/options/locale_next/locale_lv-LV.json @@ -111,5 +111,6 @@ "feed.atom.link": "Atom barotne", "keys.ssh.link": "SSH atslēgas", "keys.gpg.link": "GPG atslēgas", - "mail.actions.run_info_sha": "Iesūtījums: %[1]s" + "mail.actions.run_info_sha": "Iesūtījums: %[1]s", + "repo.pulls.already_merged": "Apvienošana neizdevās: šis izmaiņu pieprasījums jau ir iekļauts." } diff --git a/options/locale_next/locale_nds.json b/options/locale_next/locale_nds.json index c1769e55e6..25fb212496 100644 --- a/options/locale_next/locale_nds.json +++ b/options/locale_next/locale_nds.json @@ -103,5 +103,6 @@ "keys.gpg.link": "GPG-Slötels", "profile.actions.tooltip": "Mehr Aktioonen", "profile.edit.link": "Profil bewarken", - "mail.actions.run_info_sha": "Kommitteren: %[1]s" + "mail.actions.run_info_sha": "Kommitteren: %[1]s", + "repo.pulls.already_merged": "Tosamenföhren fehlslagen: Deeser Haalvörslag is al tosamenföhrt worden." } diff --git a/options/locale_next/locale_nl-NL.json b/options/locale_next/locale_nl-NL.json index 5c9c8a9b07..064c34c3cf 100644 --- a/options/locale_next/locale_nl-NL.json +++ b/options/locale_next/locale_nl-NL.json @@ -103,5 +103,6 @@ "admin.dashboard.cleanup_offline_runners": "Offline runners opruimen", "settings.visibility.description": "Profielzichtbaarheid beïnvloedt de mogelijkheid van anderen om toegang te krijgen tot je niet-privé repositories. Lees meer", "avatar.constraints_hint": "Eigen avatars mogen niet groter zijn dan %[1]s in grootte of groter zijn dan %[2]dx%[3]d pixels", - "mail.actions.run_info_sha": "Commit: %[1]s" + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.pulls.already_merged": "Samenvoegen mislukt: deze pull request is al samengevoegd." } diff --git a/options/locale_next/locale_uk-UA.json b/options/locale_next/locale_uk-UA.json index 81b69ec859..fa653980da 100644 --- a/options/locale_next/locale_uk-UA.json +++ b/options/locale_next/locale_uk-UA.json @@ -111,5 +111,6 @@ "profile.edit.link": "Редагувати профіль", "feed.atom.link": "Стрічка Atom", "profile.actions.tooltip": "Більше дій", - "mail.actions.run_info_sha": "Коміт: %[1]s" + "mail.actions.run_info_sha": "Коміт: %[1]s", + "repo.pulls.already_merged": "Не вдалося об'єднати: цей запит на злиття вже об'єднано." } From 680339830d6a0ca755aa0a1639b306843fda6b63 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Fri, 22 Aug 2025 15:11:12 +0500 Subject: [PATCH 62/80] [v12.0/forgejo] i18n: update of translations from Codeberg Translate Translation updates that were relevant to v12 branch were picked from this commit: db3bdbdbc143a634d36e484029ab951753498f90 Changes to strings that are only present in the v13 branch were not picked. Below is a list of co-authors of the ported commit. It may contain co-authors who's changes were not picked due to only being relevant to v13. Co-authored-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: Benedikt Straub Co-authored-by: Codeberg Translate Co-authored-by: Edgarsons Co-authored-by: Fjuro Co-authored-by: Juno Takano Co-authored-by: Laurent FAVOLE Co-authored-by: Outbreak2096 Co-authored-by: Salif Mehmed Co-authored-by: SomeTr Co-authored-by: Vyxie Co-authored-by: Wuzzy Co-authored-by: earl-warren Co-authored-by: fr0zi Co-authored-by: iago Co-authored-by: oscarotero Co-authored-by: xtex --- options/locale/locale_cs-CZ.ini | 2 +- options/locale/locale_de-DE.ini | 2 +- options/locale/locale_fr-FR.ini | 2 ++ options/locale/locale_gl.ini | 15 ++++++++++++ options/locale/locale_pt-BR.ini | 2 ++ options/locale/locale_ru-RU.ini | 14 +++++++----- options/locale/locale_uk-UA.ini | 4 +++- options/locale/locale_zh-CN.ini | 2 ++ options/locale_next/locale_es-ES.json | 33 ++++++++++++++++++++++++++- options/locale_next/locale_fil.json | 2 +- options/locale_next/locale_pl-PL.json | 15 +++++++++++- options/locale_next/locale_pt-BR.json | 3 ++- options/locale_next/locale_ru-RU.json | 3 ++- options/locale_next/locale_zh-CN.json | 3 ++- 14 files changed, 87 insertions(+), 15 deletions(-) diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index ec1e4547f7..7e74f42b41 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -13,7 +13,7 @@ link_account=Propojit účet register=Registrace version=Verze powered_by=Běží na %s -page=Strana +page=Stránka template=Šablona language=Jazyk notifications=Oznámení diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index f66f241bbe..5b4b1d3408 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -750,7 +750,7 @@ twofa=Zwei-Faktor-Authentifizierung (TOTP) account_link=Verknüpfte Benutzerkonten organization=Organisationen uid=UID -webauthn=Hardware-Sicherheitsschlüssel +webauthn=Zwei-Faktor-Authentifizierung (Sicherheitsschlüssel) public_profile=Öffentliches profil biography_placeholder=Erzähle anderen ein wenig über dich selbst! (Markdown wird unterstützt) diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index bac541ef76..ef70b05713 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -1091,6 +1091,8 @@ keep_pronouns_private.description = Cela masquera votre pronoms aux visiteurs qu storage_overview = Vue d'ensemble du stockage quota = Quota +ssh_token_help_ssh_agent = ou, si vous utilisez un agent SSH (avec la variable SSH_AUTH_SOCK affectée) : + [repo] new_repo_helper=Un dépôt contient tous les fichiers d’un projet, ainsi que l’historique de leurs modifications. Vous avez déjà ça ailleurs ? Migrez-le ici.. owner=Propriétaire diff --git a/options/locale/locale_gl.ini b/options/locale/locale_gl.ini index c380e9b2ff..b5c8222bbc 100644 --- a/options/locale/locale_gl.ini +++ b/options/locale/locale_gl.ini @@ -229,6 +229,9 @@ license = Código aberto lightweight_desc = Forgejo precisa duns requerimentos mínimos e pode funcionar nunha Raspberry Pi barata. Aforra enerxía na túa máquina! lightweight = Lixeiro +platform_desc = Forgejo funciona en sistemas operativos libres como Linux e FreeBSD, como tamén en diferentes arquitecturas CPU. Escolle a túa prefereida! +license_desc = Vai a Forgejo! Únete e colabora para facer este proxecto incluso mellor. Non teñas vergoña en convertirte en colaboradora! + [error] occurred = Ocorreu un erro missing_csrf = Solicitude incorrecta: non hai ningún token CSRF presente @@ -296,6 +299,18 @@ domain = Dominio do servidor ssh_port = Porto do servidor SSH require_db_desc = Forgejo precisa MySQL, PostgreSQL, SQLite3 ou TiDB (protocolo MySQL). +docker_helper = Se Forgejo está correndo dentro de Docker, por favor le a documentación antes de cambiar algunha configuración. +host = Servidor +lfs_path_helper = Os arquivos trackeados por Git LFS gárdanse neste directorio. Deixao en branco para deshabilitar. +run_user_helper = O usuario do sistema operativo que executa Forgejo. Ten en conta que este usuario debe poder acceder á raíz do repositorio. +domain_helper = Dominio ou enderezo do servidor. +ssh_port_helper = Número do porto que se usa para o servidor SSH. Déixao en branco para deshabilitar o servidor SSH. +smtp_from_helper = Enderezo email que usa Forgejo. Podes poñer simplemente un email ou usar o formato "Nome" . +register_confirm = Esixir a confirmación do email para rexistrarse +mail_notify = Permitir notificacións por email +server_service_title = Configuración do servidor e de servizos de terceiros +offline_mode = Habilitar o modo local + [repo] sync_fork.branch_behind_few = Esta rama ten %d achegas por detrás de %s sync_fork.button = Sincronizar diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index cd5cc14833..4ce186c44e 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -1092,6 +1092,8 @@ regenerate_token_success = O token foi regenerado. Aplicações que usam este to access_token_regeneration = Regenerar token de acesso access_token_regeneration_desc = Regenerar um token de acesso irá revogar o acesso a essa conta para as aplicações que estiverem utilizando este token. Isto não pode ser desfeito. Continuar? +ssh_token_help_ssh_agent = ou, se você estiver usando um agente SSH (com a variável SSH_AUTH_SOCK definida): + [repo] owner=Proprietário owner_helper=Algumas organizações podem não aparecer no menu devido a um limite de contagem dos repositórios. diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 01397f2e23..e231cf6e6f 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -1092,6 +1092,8 @@ access_token_regeneration_desc = Будет создан новый токен, regenerate_token_success = Токен был заменён. Приложения, использующие его, более не имеют доступа к этой учётной записи и должны получить новый токен. access_token_regeneration = Замена токена доступа +ssh_token_help_ssh_agent = или, если вы используете SSH-агент, (с заданной переменной SSH_AUTH_SOCK): + [repo] owner=Владелец owner_helper=Некоторые организации могут не отображаться в раскрывающемся списке из-за максимального ограничения количества репозиториев. @@ -1917,13 +1919,13 @@ pulls.merge_commit_id=ИД коммита слияния pulls.require_signed_wont_sign=Данная ветвь ожидает подписанные коммиты, однако слияние не будет подписано pulls.invalid_merge_option=Этот параметр слияния нельзя использовать для этого запроса на слияние. -pulls.merge_conflict=Слияние не удалось: произошел конфликт во время слияния. Совет: попробуйте другую стратегию +pulls.merge_conflict=Слияние не удалось: при слиянии произошел конфликт. Попробуйте другую стратегию pulls.merge_conflict_summary=Сообщение об ошибке -pulls.rebase_conflict=Слияние не удалось: произошел конфликт во время слияния: %[1]s. Совет: попробуйте другую стратегию +pulls.rebase_conflict=Слияние не удалось: при перебазировании коммита произошел конфликт: %[1]s. Попробуйте другую стратегию pulls.rebase_conflict_summary=Сообщение об ошибке -pulls.unrelated_histories=Слияние не удалось: у источника и цели слияния нет общей истории. Совет: попробуйте другую стратегию -pulls.merge_out_of_date=Слияние не удалось: при создании слияния база данных была обновлена. Подсказка: попробуйте ещё раз. -pulls.head_out_of_date=Слияние не удалось: во время слияния головной коммит был обновлён. Попробуйте ещё раз. +pulls.unrelated_histories=Слияние не удалось: источник и целевая ветвь нет имеют общую историю. Попробуйте другую стратегию +pulls.merge_out_of_date=Слияние не удалось: при выполнении слияния база данных была обновлена. Попробуйте снова. +pulls.head_out_of_date=Слияние не удалось: при слиянии был изменён головной коммит. Попробуйте снова. pulls.push_rejected=Отправка была отклонена. Проверьте Git-хуки этого репозитория. pulls.push_rejected_summary=Полная причина отклонения pulls.push_rejected_no_message=Отправка была отклонена и удалённый сервер не указал причину. Проверьте Git-хуки этого репозитория @@ -2754,7 +2756,7 @@ settings.wiki_rename_branch_main_desc = Переименовать внутре settings.wiki_branch_rename_success = Название ветви вики репозитория успешно нормализовано. ambiguous_runes_description = `Этот файл содержит символы Юникода, которые легко спутать с похожими. Если так и должно быть, можете спокойно игнорировать это предупреждение. Отобразить символы можно кнопкой Экранирования.` editor.invalid_commit_mail = Неправильная почта для создания коммита. -pulls.has_merged = Слияние не удалось: запрос уже был слит, изменение целевой ветви или повторное слияние невозможно. +pulls.has_merged = Не удалось: слияние уже выполнено – изменение целевой ветви или повторное слияние невозможно. settings.enter_repo_name = Введите имя владельца и название репозитория как указано: signing.wont_sign.error = Не удалось проверить возможность подписать коммит. signing.wont_sign.nokey = Сервер не предоставляет ключ для подписи коммита. diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 0cf3db37bc..7980e70255 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -414,7 +414,7 @@ confirmation_mail_sent_prompt=Новий лист підтвердження б must_change_password=Оновіть свій пароль allow_password_change=Вимагати в користувача змінити пароль (рекомендується) reset_password_mail_sent_prompt=Лист підтвердження було надіслано %s. Щоб завершити відновлення облікового запису, перевірте вхідні й перейдіть за наведеним посиланням (на це маєте %s). -active_your_account=Активувати обліковий запис +active_your_account=Активація облікового запису account_activated=Обліковий запис активовано prohibit_login=Обліковий запис заблоковано resent_limit_prompt=Вибачте, ви вже запросили активацію по електронній пошті нещодавно. Будь ласка, зачекайте 3 хвилини, а потім спробуйте ще раз. @@ -3798,6 +3798,8 @@ owner.settings.cleanuprules.success.delete = Правило очистки ви rubygems.required.ruby = Необхідна версія Ruby rubygems.required.rubygems = Необхідна версія RubyGem +pypi.requires = Необхідний Python + [secrets] deletion = Видалити секрет creation.success = Секрет «%s» додано. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index fd6e625b5e..af0db76a8e 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1091,6 +1091,8 @@ regenerate_token = 重新生成 access_token_regeneration = 重新生成访问令牌 access_token_regeneration_desc = 重新生成令牌将撤销使用该令牌的应用程序对您的账号的访问权限。此操作无法撤销。继续? +ssh_token_help_ssh_agent = 或者,如果您使用的是 SSH 代理(已设置 SSH_AUTH_SOCK 变量): + [repo] new_repo_helper=代码仓库包含了所有的项目文件,包括版本历史记录。已经在其他地方托管了?迁移仓库。 owner=拥有者 diff --git a/options/locale_next/locale_es-ES.json b/options/locale_next/locale_es-ES.json index 82fbcfb4b6..323072c7e4 100644 --- a/options/locale_next/locale_es-ES.json +++ b/options/locale_next/locale_es-ES.json @@ -31,5 +31,36 @@ "home.explore_repos": "Explorar repositorios", "home.explore_users": "Explorar usuarios", "home.explore_orgs": "Explorar organizaciones", - "moderation.abuse_category.malware": "Malware" + "moderation.abuse_category.malware": "Malware", + "home.welcome.activity_hint": "Aún no hay nada en tu feed. Tus acciones y la actividad en los repositorios a los que sigues se mostrarán aquí.", + "stars.list.none": "Nadie le ha dado una estrella a este repo.", + "watch.list.none": "Nadie está observando este repo.", + "followers.incoming.list.self.none": "Nadie está siguiendo tu perfil.", + "followers.incoming.list.none": "Nadie está siguiendo a este usuario.", + "followers.outgoing.list.self.none": "No estás siguiendo a nadie.", + "followers.outgoing.list.none": "%s ya no me está siguiendo.", + "relativetime.1day": "ayer", + "relativetime.1week": "la semana pasada", + "relativetime.2weeks": "hace dos semanas", + "relativetime.1month": "el mes pasado", + "relativetime.2months": "hace dos meses", + "relativetime.1year": "el año pasado", + "relativetime.2years": "hace dos años", + "repo.form.cannot_create": "Todos los espacios en los que puedes crear repositorios llegaron al límite de repositorios.", + "incorrect_root_url": "Esta instancia de Forgejo está configurada para servirse en \"%s\". Estás viendo Forgejo a través de una URL distinta, lo que puede hacer que se rompan partes de la aplicación. La URL canónica la controlan los administdadores xd Forgejo a través del ajuste ROOT_URL en app.ini.", + "alert.asset_load_failed": "Error al cargar recursos desde {path}. Por favor, asegúrage de que se puede acceder a los recursos.", + "alert.range_error": " debe ser un número entre %[1]s y %[2]s.", + "install.invalid_lfs_path": "Imposible crear la raíz LFS en la ruta indicada: %[1]s", + "profile.actions.tooltip": "Mas acciones", + "profile.edit.link": "Editar perfil", + "feed.atom.link": "Feed Atom", + "keys.ssh.link": "Claves SSH", + "keys.gpg.link": "Claves GPG", + "admin.config.moderation_config": "Configuración de moderación", + "moderation.report_abuse": "Reportar abuso", + "moderation.report_content": "Reportar contenido", + "moderation.report_abuse_form.header": "Reportar a uso a un administrador", + "moderation.report_abuse_form.invalid": "Argumentos inválidos", + "moderation.report_abuse_form.already_reported": "Ya has reportado este contenido", + "moderation.abuse_category": "Categoría" } diff --git a/options/locale_next/locale_fil.json b/options/locale_next/locale_fil.json index 2e1e3f5625..f2d24e445b 100644 --- a/options/locale_next/locale_fil.json +++ b/options/locale_next/locale_fil.json @@ -55,7 +55,7 @@ "relativetime.2years": "2 taon ang nakalipas", "relativetime.1day": "kahapon", "relativetime.hours": { - "one": "%d kras ang nakalipas", + "one": "%d oras ang nakalipas", "other": "%d (na) oras ang nakalipas" }, "relativetime.months": { diff --git a/options/locale_next/locale_pl-PL.json b/options/locale_next/locale_pl-PL.json index 8f9eea2302..cc056c0b1e 100644 --- a/options/locale_next/locale_pl-PL.json +++ b/options/locale_next/locale_pl-PL.json @@ -2,5 +2,18 @@ "repo.pulls.merged_title_desc": "scala %[1]d commity/ów z %[2]s do %[3]s %[4]s", "repo.pulls.title_desc": "chce scalić %[1]d commity/ów z %[2]s do %[3]s", "search.milestone_kind": "Wyszukaj kamienie milowe...", - "incorrect_root_url": "Ta instancja Forgejo jest skonfigurowana do korzystania z \"%s\". Obecnie oglądasz Forgejo za pomocą innego URL, co może powodować błędne działanie tej aplikacji. URL kanoniczny jest kontrolowany przez administratorów Forgejo za pomocą ROOT_URL w app.ini." + "incorrect_root_url": "Ta instancja Forgejo jest skonfigurowana do korzystania z \"%s\". Obecnie oglądasz Forgejo za pomocą innego URL, co może powodować błędne działanie tej aplikacji. URL kanoniczny jest kontrolowany przez administratorów Forgejo za pomocą ROOT_URL w app.ini.", + "home.welcome.no_activity": "Brak aktywności", + "home.welcome.activity_hint": "Na razie nie ma nic w twoich aktualnościach. Pojawią się tutaj twoje działania oraz wydarzenia z repozytoriów, które obserwujesz.", + "home.explore_repos": "Eksploruj repozytoria", + "home.explore_users": "Eksploruj użytkowników", + "home.explore_orgs": "Eksploruj organizacje", + "stars.list.none": "Nikt nie dał gwiazdki temu repozytorium.", + "watch.list.none": "Nikt nie obserwuje tego repozytorium.", + "followers.incoming.list.self.none": "Nikt nie obserwuje twojego profilu.", + "followers.incoming.list.none": "Nikt nie obserwuje tego użytkownika.", + "followers.outgoing.list.self.none": "Nie obserwujesz nikogo.", + "followers.outgoing.list.none": "%s nie obserwuje nikogo.", + "relativetime.now": "teraz", + "relativetime.future": "w przyszłości" } diff --git a/options/locale_next/locale_pt-BR.json b/options/locale_next/locale_pt-BR.json index 0fc65a7dd6..0d42dd7e4b 100644 --- a/options/locale_next/locale_pt-BR.json +++ b/options/locale_next/locale_pt-BR.json @@ -111,5 +111,6 @@ "feed.atom.link": "Feed Atom", "keys.ssh.link": "Chaves SSH", "keys.gpg.link": "Chaves GPG", - "mail.actions.run_info_sha": "Commit: %[1]s" + "mail.actions.run_info_sha": "Commit: %[1]s", + "repo.pulls.already_merged": "Mescla falhou: Este pull request já foi mesclado." } diff --git a/options/locale_next/locale_ru-RU.json b/options/locale_next/locale_ru-RU.json index e49eb79ca2..d2a2fb5965 100644 --- a/options/locale_next/locale_ru-RU.json +++ b/options/locale_next/locale_ru-RU.json @@ -111,5 +111,6 @@ "keys.ssh.link": "Ключи SSH", "keys.gpg.link": "Ключи GPG", "profile.edit.link": "Изменить профиль", - "mail.actions.run_info_sha": "Коммит: %[1]s" + "mail.actions.run_info_sha": "Коммит: %[1]s", + "repo.pulls.already_merged": "Слияние не удалось: слияние уже выполнено." } diff --git a/options/locale_next/locale_zh-CN.json b/options/locale_next/locale_zh-CN.json index 743cced8c1..12ffcf041b 100644 --- a/options/locale_next/locale_zh-CN.json +++ b/options/locale_next/locale_zh-CN.json @@ -79,5 +79,6 @@ "repo.diff.commit.previous-short": "上一个", "feed.atom.link": "Atom 订阅源", "profile.edit.link": "编辑个人资料", - "mail.actions.run_info_sha": "提交:%[1]s" + "mail.actions.run_info_sha": "提交:%[1]s", + "repo.pulls.already_merged": "合并失败:此合并请求已被合并。" } From 25484228e67c2c851f7a4987078201d8e2c96600 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 23 Aug 2025 13:49:05 +0200 Subject: [PATCH 63/80] [v12.0/forgejo] fix(code-search): fix broken pagination. (#9006) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9000 Missing parameters for: - repo: path and mode - user: mode - explore: mode resolves forgejo/forgejo!8997 and codeberg/community!2098 ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9000): fix(code-search): fix broken pagination. Co-authored-by: Shiny Nematoda Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9006 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/explore/code.go | 1 + routers/web/repo/search.go | 2 ++ routers/web/user/code.go | 1 + tests/integration/repo_search_test.go | 42 +++++++++++++++++++++++++-- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go index f0b12e9142..6697755c22 100644 --- a/routers/web/explore/code.go +++ b/routers/web/explore/code.go @@ -147,6 +147,7 @@ func Code(ctx *context.Context) { pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) pager.SetDefaultParams(ctx) pager.AddParam(ctx, "l", "Language") + pager.AddParam(ctx, "mode", "CodeSearchMode") ctx.Data["Page"] = pager ctx.HTML(http.StatusOK, tplExploreCode) diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 1671378a3b..ad10542c01 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -165,6 +165,8 @@ func Search(ctx *context.Context) { pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) pager.SetDefaultParams(ctx) pager.AddParam(ctx, "l", "Language") + pager.AddParam(ctx, "mode", "CodeSearchMode") + pager.AddParam(ctx, "path", "CodeSearchPath") ctx.Data["Page"] = pager ctx.HTML(http.StatusOK, tplSearch) diff --git a/routers/web/user/code.go b/routers/web/user/code.go index ac1852e410..b5c5e54953 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -131,6 +131,7 @@ func CodeSearch(ctx *context.Context) { pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) pager.SetDefaultParams(ctx) pager.AddParam(ctx, "l", "Language") + pager.AddParam(ctx, "mode", "CodeSearchMode") ctx.Data["Page"] = pager ctx.HTML(http.StatusOK, tplUserCode) diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go index aaaa4618cf..b145d9f427 100644 --- a/tests/integration/repo_search_test.go +++ b/tests/integration/repo_search_test.go @@ -5,6 +5,7 @@ package integration import ( "net/http" + "net/url" "testing" "forgejo.org/models/db" @@ -92,8 +93,8 @@ func testSearchRepo(t *testing.T, indexer bool) { testSearch(t, "/user2/glob/search?q=file5&page=1&mode=exact", []string{}, indexer) } -func testSearch(t *testing.T, url string, expected []string, indexer bool) { - req := NewRequest(t, "GET", url) +func testSearch(t *testing.T, rawURL string, expected []string, indexer bool) { + req := NewRequest(t, "GET", rawURL) resp := MakeRequest(t, req, http.StatusOK) doc := NewHTMLParser(t, resp.Body) @@ -119,4 +120,41 @@ func testSearch(t *testing.T, url string, expected []string, indexer bool) { filenames := resultFilenames(t, doc) assert.ElementsMatch(t, expected, filenames) + + testSearchPagination(t, rawURL, doc) +} + +// Tests that the variables set in the url persist for all the paginated links +func testSearchPagination(t *testing.T, rawURL string, doc *HTMLDoc) { + original, err := queryFromStr(rawURL) + require.NoError(t, err) + + hrefs := doc. + Find(".pagination.menu a[href]:not(.disabled)"). + Map(func(i int, el *goquery.Selection) string { + attr, ok := el.Attr("href") + require.True(t, ok) + return attr + }) + query := make([]url.Values, len(hrefs)) + for i, href := range hrefs { + query[i], err = queryFromStr(href) + require.NoError(t, err) + } + + for key := range original { + for i, q := range query { + assert.Equal(t, original.Get(key), q.Get(key), + "failed at index '%d' with url '%v'", i, hrefs[i]) + } + } +} + +func queryFromStr(rawURL string) (url.Values, error) { + u, err := url.Parse(rawURL) + if err != nil { + return nil, err + } + + return url.ParseQuery(u.RawQuery) } From 6636550157e39fd72980cd2c6c6b1a23930dbc37 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Mon, 25 Aug 2025 21:50:02 +0200 Subject: [PATCH 64/80] [v12.0/forgejo] fix: Actions workflows triggered by comments or labels to pull requests may access secrets (#9025) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9003 This avoids issue_comment events on pull requests to get that flag set and subsequently not get access to secrets. ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9003): Actions workflows triggered by comments or labels to pull requests may access secrets Co-authored-by: BtbN Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9025 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- services/actions/notifier.go | 2 +- services/actions/notifier_helper.go | 10 ++- services/actions/notifier_helper_test.go | 95 ++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/services/actions/notifier.go b/services/actions/notifier.go index a5bac730be..33ac675fcc 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -343,7 +343,7 @@ func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, commen newNotifyInputFromIssue(comment.Issue, event). WithDoer(doer). WithPayload(payload). - WithPullRequest(comment.Issue.PullRequest). + WithPullRequestData(comment.Issue.PullRequest). Notify(ctx) return } diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index c94223bc3b..7dd449eda5 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -102,6 +102,12 @@ func (input *notifyInput) WithPayload(payload api.Payloader) *notifyInput { return input } +// for cases like issue comments on PRs, which have the PR data, but don't run on its ref +func (input *notifyInput) WithPullRequestData(pr *issues_model.PullRequest) *notifyInput { + input.PullRequest = pr + return input +} + func (input *notifyInput) WithPullRequest(pr *issues_model.PullRequest) *notifyInput { input.PullRequest = pr if input.Ref == "" { @@ -219,7 +225,7 @@ func notify(ctx context.Context, input *notifyInput) error { } } - if input.PullRequest != nil { + if input.PullRequest != nil && !actions_module.IsDefaultBranchWorkflow(input.Event) { // detect pull_request_target workflows baseRef := git.BranchPrefix + input.PullRequest.BaseBranch baseCommit, err := gitRepo.GetCommit(baseRef) @@ -315,7 +321,7 @@ func handleWorkflows( } isForkPullRequest := false - if pr := input.PullRequest; pr != nil { + if pr := input.PullRequest; pr != nil && !actions_module.IsDefaultBranchWorkflow(input.Event) { switch pr.Flow { case issues_model.PullRequestFlowGithub: isForkPullRequest = pr.IsFromFork() diff --git a/services/actions/notifier_helper_test.go b/services/actions/notifier_helper_test.go index 9166dc3b95..fabc783bd0 100644 --- a/services/actions/notifier_helper_test.go +++ b/services/actions/notifier_helper_test.go @@ -8,9 +8,16 @@ import ( actions_model "forgejo.org/models/actions" "forgejo.org/models/db" + issues_model "forgejo.org/models/issues" + repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + actions_module "forgejo.org/modules/actions" + "forgejo.org/modules/git" + api "forgejo.org/modules/structs" webhook_module "forgejo.org/modules/webhook" + "github.com/nektos/act/pkg/jobparser" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -49,3 +56,91 @@ func Test_SkipPullRequestEvent(t *testing.T) { unittest.AssertSuccessfulInsert(t, run) assert.True(t, SkipPullRequestEvent(db.DefaultContext, webhook_module.HookEventPullRequestSync, repoID, commitSHA)) } + +func Test_IssueCommentOnForkPullRequestEvent(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 3}) + require.NoError(t, pr.LoadIssue(db.DefaultContext)) + + require.True(t, pr.IsFromFork()) + + commit := &git.Commit{ + ID: git.MustIDFromString("0000000000000000000000000000000000000000"), + CommitMessage: "test", + } + detectedWorkflows := []*actions_module.DetectedWorkflow{ + { + TriggerEvent: &jobparser.Event{ + Name: "issue_comment", + }, + }, + } + input := ¬ifyInput{ + Repo: repo, + Doer: doer, + Event: webhook_module.HookEventIssueComment, + PullRequest: pr, + Payload: &api.IssueCommentPayload{}, + } + + unittest.AssertSuccessfulDelete(t, &actions_model.ActionRun{RepoID: repo.ID}) + + err := handleWorkflows(db.DefaultContext, detectedWorkflows, commit, input, "") + require.NoError(t, err) + + runs, err := db.Find[actions_model.ActionRun](db.DefaultContext, actions_model.FindRunOptions{ + RepoID: repo.ID, + }) + require.NoError(t, err) + require.Len(t, runs, 1) + + assert.Equal(t, webhook_module.HookEventIssueComment, runs[0].Event) + assert.False(t, runs[0].IsForkPullRequest) +} + +func Test_OpenForkPullRequestEvent(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}) + doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 3}) + require.NoError(t, pr.LoadIssue(db.DefaultContext)) + + require.True(t, pr.IsFromFork()) + + commit := &git.Commit{ + ID: git.MustIDFromString("0000000000000000000000000000000000000000"), + CommitMessage: "test", + } + detectedWorkflows := []*actions_module.DetectedWorkflow{ + { + TriggerEvent: &jobparser.Event{ + Name: "pull_request", + }, + }, + } + input := ¬ifyInput{ + Repo: repo, + Doer: doer, + Event: webhook_module.HookEventPullRequest, + PullRequest: pr, + Payload: &api.PullRequestPayload{}, + } + + unittest.AssertSuccessfulDelete(t, &actions_model.ActionRun{RepoID: repo.ID}) + + err := handleWorkflows(db.DefaultContext, detectedWorkflows, commit, input, "") + require.NoError(t, err) + + runs, err := db.Find[actions_model.ActionRun](db.DefaultContext, actions_model.FindRunOptions{ + RepoID: repo.ID, + }) + require.NoError(t, err) + require.Len(t, runs, 1) + + assert.Equal(t, webhook_module.HookEventPullRequest, runs[0].Event) + assert.True(t, runs[0].IsForkPullRequest) +} From 5538ab29e3fc3c4f81374eba1ac6fa7ce5fc11c5 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 30 Aug 2025 18:42:11 +0200 Subject: [PATCH 65/80] [v12.0/forgejo] fix: only redirect to a new owner (organization or user) if the user has permissions to view the new owner (#9091) **Backport: https://codeberg.org/forgejo/forgejo/pulls/9072** Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9091 --- models/fixtures/repo_redirect.yml | 6 +++ models/fixtures/user_redirect.yml | 12 ++++++ models/repo/redirect.go | 9 +++-- models/repo/redirect_test.go | 12 ------ models/user/redirect.go | 12 +----- models/user/redirect_test.go | 26 ------------- routers/api/v1/api.go | 11 +++--- routers/api/v1/user/helper.go | 3 +- routers/web/repo/githttp.go | 3 +- services/context/org.go | 5 ++- services/context/repo.go | 7 ++-- services/context/user.go | 5 ++- services/redirect/main_test.go | 18 +++++++++ services/redirect/repo.go | 37 ++++++++++++++++++ services/redirect/repo_test.go | 55 ++++++++++++++++++++++++++ services/redirect/user.go | 30 ++++++++++++++ services/redirect/user_test.go | 65 +++++++++++++++++++++++++++++++ services/user/user_test.go | 3 +- 18 files changed, 252 insertions(+), 67 deletions(-) delete mode 100644 models/user/redirect_test.go create mode 100644 services/redirect/main_test.go create mode 100644 services/redirect/repo.go create mode 100644 services/redirect/repo_test.go create mode 100644 services/redirect/user.go create mode 100644 services/redirect/user_test.go diff --git a/models/fixtures/repo_redirect.yml b/models/fixtures/repo_redirect.yml index 8850c8d780..82d365c600 100644 --- a/models/fixtures/repo_redirect.yml +++ b/models/fixtures/repo_redirect.yml @@ -3,3 +3,9 @@ owner_id: 2 lower_name: oldrepo1 redirect_repo_id: 1 + +- + id: 2 + owner_id: 17 + lower_name: oldrepo24 + redirect_repo_id: 24 diff --git a/models/fixtures/user_redirect.yml b/models/fixtures/user_redirect.yml index f471e94511..2f7a523c0c 100644 --- a/models/fixtures/user_redirect.yml +++ b/models/fixtures/user_redirect.yml @@ -3,3 +3,15 @@ lower_name: olduser1 redirect_user_id: 1 created_unix: 1730000000 + +- + id: 2 + lower_name: oldorg22 + redirect_user_id: 22 + created_unix: 1730000000 + +- + id: 3 + lower_name: oldorg23 + redirect_user_id: 23 + created_unix: 1730000000 diff --git a/models/repo/redirect.go b/models/repo/redirect.go index 9c44a255d0..e5239a3684 100644 --- a/models/repo/redirect.go +++ b/models/repo/redirect.go @@ -14,8 +14,9 @@ import ( // ErrRedirectNotExist represents a "RedirectNotExist" kind of error. type ErrRedirectNotExist struct { - OwnerID int64 - RepoName string + OwnerID int64 + RepoName string + MissingPermission bool } // IsErrRedirectNotExist check if an error is an ErrRepoRedirectNotExist. @@ -49,8 +50,8 @@ func init() { db.RegisterModel(new(Redirect)) } -// LookupRedirect look up if a repository has a redirect name -func LookupRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) { +// GetRedirect returns the redirect for a given pair of ownerID and repository name. +func GetRedirect(ctx context.Context, ownerID int64, repoName string) (int64, error) { repoName = strings.ToLower(repoName) redirect := &Redirect{OwnerID: ownerID, LowerName: repoName} if has, err := db.GetEngine(ctx).Get(redirect); err != nil { diff --git a/models/repo/redirect_test.go b/models/repo/redirect_test.go index d84cbbed54..2f2210588f 100644 --- a/models/repo/redirect_test.go +++ b/models/repo/redirect_test.go @@ -10,21 +10,9 @@ import ( repo_model "forgejo.org/models/repo" "forgejo.org/models/unittest" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestLookupRedirect(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - repoID, err := repo_model.LookupRedirect(db.DefaultContext, 2, "oldrepo1") - require.NoError(t, err) - assert.EqualValues(t, 1, repoID) - - _, err = repo_model.LookupRedirect(db.DefaultContext, unittest.NonexistentID, "doesnotexist") - assert.True(t, repo_model.IsErrRedirectNotExist(err)) -} - func TestNewRedirect(t *testing.T) { // redirect to a completely new name require.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/user/redirect.go b/models/user/redirect.go index 75876f17d2..bcb421d4a1 100644 --- a/models/user/redirect.go +++ b/models/user/redirect.go @@ -21,7 +21,8 @@ import ( // ErrUserRedirectNotExist represents a "UserRedirectNotExist" kind of error. type ErrUserRedirectNotExist struct { - Name string + Name string + MissingPermission bool } // IsErrUserRedirectNotExist check if an error is an ErrUserRedirectNotExist. @@ -81,15 +82,6 @@ func GetUserRedirect(ctx context.Context, userName string) (*Redirect, error) { return redirect, nil } -// LookupUserRedirect look up userID if a user has a redirect name -func LookupUserRedirect(ctx context.Context, userName string) (int64, error) { - redirect, err := GetUserRedirect(ctx, userName) - if err != nil { - return 0, err - } - return redirect.RedirectUserID, nil -} - // NewUserRedirect create a new user redirect func NewUserRedirect(ctx context.Context, ID int64, oldUserName, newUserName string) error { oldUserName = strings.ToLower(oldUserName) diff --git a/models/user/redirect_test.go b/models/user/redirect_test.go deleted file mode 100644 index c598fb045f..0000000000 --- a/models/user/redirect_test.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package user_test - -import ( - "testing" - - "forgejo.org/models/db" - "forgejo.org/models/unittest" - user_model "forgejo.org/models/user" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestLookupUserRedirect(t *testing.T) { - require.NoError(t, unittest.PrepareTestDatabase()) - - userID, err := user_model.LookupUserRedirect(db.DefaultContext, "olduser1") - require.NoError(t, err) - assert.EqualValues(t, 1, userID) - - _, err = user_model.LookupUserRedirect(db.DefaultContext, "doesnotexist") - assert.True(t, user_model.IsErrUserRedirectNotExist(err)) -} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 95c3ee4791..c3e8b532cf 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -100,6 +100,7 @@ import ( "forgejo.org/services/auth" "forgejo.org/services/context" "forgejo.org/services/forms" + redirect_service "forgejo.org/services/redirect" _ "forgejo.org/routers/api/v1/swagger" // for swagger generation @@ -153,12 +154,12 @@ func repoAssignment() func(ctx *context.APIContext) { owner, err = user_model.GetUserByName(ctx, userName) if err != nil { if user_model.IsErrUserNotExist(err) { - if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil { + if redirectUserID, err := redirect_service.LookupUserRedirect(ctx, ctx.Doer, userName); err == nil { context.RedirectToUser(ctx.Base, userName, redirectUserID) } else if user_model.IsErrUserRedirectNotExist(err) { ctx.NotFound("GetUserByName", err) } else { - ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) + ctx.Error(http.StatusInternalServerError, "LookupRedirect", err) } } else { ctx.Error(http.StatusInternalServerError, "GetUserByName", err) @@ -173,7 +174,7 @@ func repoAssignment() func(ctx *context.APIContext) { repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) if err != nil { if repo_model.IsErrRepoNotExist(err) { - redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName) + redirectRepoID, err := redirect_service.LookupRepoRedirect(ctx, ctx.Doer, owner.ID, repoName) if err == nil { context.RedirectToRepo(ctx.Base, redirectRepoID) } else if repo_model.IsErrRedirectNotExist(err) { @@ -638,13 +639,13 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) { ctx.Org.Organization, err = organization.GetOrgByName(ctx, ctx.Params(":org")) if err != nil { if organization.IsErrOrgNotExist(err) { - redirectUserID, err := user_model.LookupUserRedirect(ctx, ctx.Params(":org")) + redirectUserID, err := redirect_service.LookupUserRedirect(ctx, ctx.Doer, ctx.Params(":org")) if err == nil { context.RedirectToUser(ctx.Base, ctx.Params(":org"), redirectUserID) } else if user_model.IsErrUserRedirectNotExist(err) { ctx.NotFound("GetOrgByName", err) } else { - ctx.Error(http.StatusInternalServerError, "LookupUserRedirect", err) + ctx.Error(http.StatusInternalServerError, "LookupRedirect", err) } } else { ctx.Error(http.StatusInternalServerError, "GetOrgByName", err) diff --git a/routers/api/v1/user/helper.go b/routers/api/v1/user/helper.go index fe0943091f..1de4d72161 100644 --- a/routers/api/v1/user/helper.go +++ b/routers/api/v1/user/helper.go @@ -8,6 +8,7 @@ import ( user_model "forgejo.org/models/user" "forgejo.org/services/context" + redirect_service "forgejo.org/services/redirect" ) // GetUserByParamsName get user by name @@ -16,7 +17,7 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *user_model.User user, err := user_model.GetUserByName(ctx, username) if err != nil { if user_model.IsErrUserNotExist(err) { - if redirectUserID, err2 := user_model.LookupUserRedirect(ctx, username); err2 == nil { + if redirectUserID, err2 := redirect_service.LookupUserRedirect(ctx, ctx.Doer, username); err2 == nil { context.RedirectToUser(ctx.Base, username, redirectUserID) } else { ctx.NotFound("GetUserByName", err) diff --git a/routers/web/repo/githttp.go b/routers/web/repo/githttp.go index 650b1d88f4..aac251dc3d 100644 --- a/routers/web/repo/githttp.go +++ b/routers/web/repo/githttp.go @@ -31,6 +31,7 @@ import ( "forgejo.org/modules/structs" "forgejo.org/modules/util" "forgejo.org/services/context" + redirect_service "forgejo.org/services/redirect" repo_service "forgejo.org/services/repository" "github.com/go-chi/cors" @@ -111,7 +112,7 @@ func httpBase(ctx *context.Context) *serviceHandler { return nil } - if redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, reponame); err == nil { + if redirectRepoID, err := redirect_service.LookupRepoRedirect(ctx, ctx.Doer, owner.ID, reponame); err == nil { context.RedirectToRepo(ctx.Base, redirectRepoID) return nil } diff --git a/services/context/org.go b/services/context/org.go index 3ddc40b6b3..c7d06b9bcc 100644 --- a/services/context/org.go +++ b/services/context/org.go @@ -16,6 +16,7 @@ import ( "forgejo.org/modules/markup/markdown" "forgejo.org/modules/setting" "forgejo.org/modules/structs" + redirect_service "forgejo.org/services/redirect" ) // Organization contains organization context @@ -48,13 +49,13 @@ func GetOrganizationByParams(ctx *Context) { ctx.Org.Organization, err = organization.GetOrgByName(ctx, orgName) if err != nil { if organization.IsErrOrgNotExist(err) { - redirectUserID, err := user_model.LookupUserRedirect(ctx, orgName) + redirectUserID, err := redirect_service.LookupUserRedirect(ctx, ctx.Doer, orgName) if err == nil { RedirectToUser(ctx.Base, orgName, redirectUserID) } else if user_model.IsErrUserRedirectNotExist(err) { ctx.NotFound("GetUserByName", err) } else { - ctx.ServerError("LookupUserRedirect", err) + ctx.ServerError("LookupRedirect", err) } } else { ctx.ServerError("GetUserByName", err) diff --git a/services/context/repo.go b/services/context/repo.go index cce3a5fa70..2a70c0b08b 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -35,6 +35,7 @@ import ( "forgejo.org/modules/setting" "forgejo.org/modules/util" asymkey_service "forgejo.org/services/asymkey" + redirect_service "forgejo.org/services/redirect" "github.com/editorconfig/editorconfig-core-go/v2" ) @@ -477,12 +478,12 @@ func RepoAssignment(ctx *Context) context.CancelFunc { return nil } - if redirectUserID, err := user_model.LookupUserRedirect(ctx, userName); err == nil { + if redirectUserID, err := redirect_service.LookupUserRedirect(ctx, ctx.Doer, userName); err == nil { RedirectToUser(ctx.Base, userName, redirectUserID) } else if user_model.IsErrUserRedirectNotExist(err) { ctx.NotFound("GetUserByName", nil) } else { - ctx.ServerError("LookupUserRedirect", err) + ctx.ServerError("LookupRedirect", err) } } else { ctx.ServerError("GetUserByName", err) @@ -519,7 +520,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { repo, err := repo_model.GetRepositoryByName(ctx, owner.ID, repoName) if err != nil { if repo_model.IsErrRepoNotExist(err) { - redirectRepoID, err := repo_model.LookupRedirect(ctx, owner.ID, repoName) + redirectRepoID, err := redirect_service.LookupRepoRedirect(ctx, ctx.Doer, owner.ID, repoName) if err == nil { RedirectToRepo(ctx.Base, redirectRepoID) } else if repo_model.IsErrRedirectNotExist(err) { diff --git a/services/context/user.go b/services/context/user.go index a82c90d7a6..63260a49aa 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -9,6 +9,7 @@ import ( "strings" user_model "forgejo.org/models/user" + redirect_service "forgejo.org/services/redirect" ) // UserAssignmentWeb returns a middleware to handle context-user assignment for web routes @@ -68,12 +69,12 @@ func userAssignment(ctx *Base, doer *user_model.User, errCb func(int, string, an contextUser, err = user_model.GetUserByName(ctx, username) if err != nil { if user_model.IsErrUserNotExist(err) { - if redirectUserID, err := user_model.LookupUserRedirect(ctx, username); err == nil { + if redirectUserID, err := redirect_service.LookupUserRedirect(ctx, doer, username); err == nil { RedirectToUser(ctx, username, redirectUserID) } else if user_model.IsErrUserRedirectNotExist(err) { errCb(http.StatusNotFound, "GetUserByName", err) } else { - errCb(http.StatusInternalServerError, "LookupUserRedirect", err) + errCb(http.StatusInternalServerError, "LookupRedirect", err) } } else { errCb(http.StatusInternalServerError, "GetUserByName", err) diff --git a/services/redirect/main_test.go b/services/redirect/main_test.go new file mode 100644 index 0000000000..7363791caa --- /dev/null +++ b/services/redirect/main_test.go @@ -0,0 +1,18 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT +package redirect + +import ( + "testing" + + "forgejo.org/models/unittest" + + _ "forgejo.org/models" + _ "forgejo.org/models/actions" + _ "forgejo.org/models/activities" + _ "forgejo.org/models/forgefed" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/services/redirect/repo.go b/services/redirect/repo.go new file mode 100644 index 0000000000..7070ab4e2f --- /dev/null +++ b/services/redirect/repo.go @@ -0,0 +1,37 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT +package redirect + +import ( + "context" + + access_model "forgejo.org/models/perm/access" + repo_model "forgejo.org/models/repo" + user_model "forgejo.org/models/user" +) + +// LookupRepoRedirect returns the repository ID if there's a redirect registered for +// the ownerID repository name pair. It checks if the doer has permission to view +// the new repository. +func LookupRepoRedirect(ctx context.Context, doer *user_model.User, ownerID int64, repoName string) (int64, error) { + redirectID, err := repo_model.GetRedirect(ctx, ownerID, repoName) + if err != nil { + return 0, err + } + + redirectRepo, err := repo_model.GetRepositoryByID(ctx, redirectID) + if err != nil { + return 0, err + } + + perm, err := access_model.GetUserRepoPermission(ctx, redirectRepo, doer) + if err != nil { + return 0, err + } + + if !perm.HasAccess() { + return 0, repo_model.ErrRedirectNotExist{OwnerID: ownerID, RepoName: repoName, MissingPermission: true} + } + + return redirectID, nil +} diff --git a/services/redirect/repo_test.go b/services/redirect/repo_test.go new file mode 100644 index 0000000000..cad8414035 --- /dev/null +++ b/services/redirect/repo_test.go @@ -0,0 +1,55 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT +package redirect + +import ( + "testing" + + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLookupRepoRedirect(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + normalUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) + ownerUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}) + + testOk := func(t *testing.T, doer *user_model.User, ownerID int64, repoName string, expectedRedirectID int64) { + t.Helper() + + redirectID, err := LookupRepoRedirect(t.Context(), doer, ownerID, repoName) + require.NoError(t, err) + assert.Equal(t, expectedRedirectID, redirectID) + } + + testFail := func(t *testing.T, doer *user_model.User, ownerID int64, repoName string) { + t.Helper() + + redirectID, err := LookupRepoRedirect(t.Context(), doer, ownerID, repoName) + require.ErrorIs(t, err, repo_model.ErrRedirectNotExist{OwnerID: ownerID, RepoName: repoName, MissingPermission: true}) + assert.Zero(t, redirectID) + } + + t.Run("Public repository", func(t *testing.T) { + ownerID := int64(2) + reponame := "oldrepo1" + + testOk(t, nil, ownerID, reponame, 1) + testOk(t, normalUser, ownerID, reponame, 1) + testOk(t, ownerUser, ownerID, reponame, 1) + }) + + t.Run("Private repository", func(t *testing.T) { + ownerID := int64(17) + reponame := "oldrepo24" + + testFail(t, nil, ownerID, reponame) + testFail(t, normalUser, ownerID, reponame) + testOk(t, ownerUser, ownerID, reponame, 24) + }) +} diff --git a/services/redirect/user.go b/services/redirect/user.go new file mode 100644 index 0000000000..2b1d38bbc0 --- /dev/null +++ b/services/redirect/user.go @@ -0,0 +1,30 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT +package redirect + +import ( + "context" + + user_model "forgejo.org/models/user" +) + +// LookupUserRedirect returns the userID if there's a redirect registered for the +// username. It additionally checks if the doer has permission to view the new +// user. +func LookupUserRedirect(ctx context.Context, doer *user_model.User, userName string) (int64, error) { + redirect, err := user_model.GetUserRedirect(ctx, userName) + if err != nil { + return 0, err + } + + redirectUser, err := user_model.GetUserByID(ctx, redirect.RedirectUserID) + if err != nil { + return 0, err + } + + if !user_model.IsUserVisibleToViewer(ctx, redirectUser, doer) { + return 0, user_model.ErrUserRedirectNotExist{Name: userName, MissingPermission: true} + } + + return redirect.RedirectUserID, nil +} diff --git a/services/redirect/user_test.go b/services/redirect/user_test.go new file mode 100644 index 0000000000..d1ddcc2ebf --- /dev/null +++ b/services/redirect/user_test.go @@ -0,0 +1,65 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT +package redirect + +import ( + "testing" + + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLookupUserRedirect(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + normalUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + testOk := func(t *testing.T, doer *user_model.User, username string, expectedRedirectID int64) { + t.Helper() + + redirectID, err := LookupUserRedirect(t.Context(), doer, username) + require.NoError(t, err) + assert.Equal(t, expectedRedirectID, redirectID) + } + + testFail := func(t *testing.T, doer *user_model.User, username string) { + t.Helper() + + redirectID, err := LookupUserRedirect(t.Context(), doer, username) + require.ErrorIs(t, err, user_model.ErrUserRedirectNotExist{Name: username, MissingPermission: true}) + assert.Zero(t, redirectID) + } + + t.Run("Public visibility", func(t *testing.T) { + username := "olduser1" + redirectID := int64(1) + + testOk(t, nil, username, redirectID) + testOk(t, normalUser, username, redirectID) + testOk(t, adminUser, username, redirectID) + }) + + t.Run("Limited visibility", func(t *testing.T) { + username := "oldorg22" + redirectID := int64(22) + + testFail(t, nil, username) + testOk(t, normalUser, username, redirectID) + testOk(t, adminUser, username, redirectID) + }) + + t.Run("Private visibility", func(t *testing.T) { + orgUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) + username := "oldorg23" + redirectID := int64(23) + + testFail(t, nil, username) + testFail(t, normalUser, username) + testOk(t, orgUser, username, redirectID) + testOk(t, adminUser, username, redirectID) + }) +} diff --git a/services/user/user_test.go b/services/user/user_test.go index f1cab60a6d..2df3148315 100644 --- a/services/user/user_test.go +++ b/services/user/user_test.go @@ -25,6 +25,7 @@ import ( "forgejo.org/modules/setting" "forgejo.org/modules/test" "forgejo.org/modules/timeutil" + redirect_service "forgejo.org/services/redirect" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -196,7 +197,7 @@ func TestRenameUser(t *testing.T) { require.NoError(t, RenameUser(db.DefaultContext, user, newUsername)) unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID, Name: newUsername, LowerName: strings.ToLower(newUsername)}) - redirectUID, err := user_model.LookupUserRedirect(db.DefaultContext, oldUsername) + redirectUID, err := redirect_service.LookupUserRedirect(db.DefaultContext, user, oldUsername) require.NoError(t, err) assert.Equal(t, user.ID, redirectUID) From 43664f79b93fa7926c2d93536ee2522a40fb5b0c Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 30 Aug 2025 18:42:39 +0200 Subject: [PATCH 66/80] [v12.0/forgejo] fix: don't allow credentials in migrate/push mirror URL (#9078) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9064 It is no longer possible to specify the user and password when providing a URL for migrating a repository, the fields dedicated to that purpose on the form must be used instead. This is to prevent that those credentials are displayed in the repository settings that are visible by the repository admins, in the case where the migration is a mirror. ## Release notes - Security bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9064): don't allow credentials in migrate/push mirror URL Co-authored-by: Gergely Nagy Co-authored-by: Gusted Co-authored-by: Earl Warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9078 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/error.go | 4 ++ options/locale_next/locale_en-US.json | 1 + routers/api/v1/repo/migrate.go | 2 + routers/api/v1/repo/mirror.go | 2 + routers/web/repo/migrate.go | 2 + routers/web/repo/setting/setting.go | 10 ++++- services/forms/repo_form.go | 3 ++ .../mirror.yml | 9 ++++ .../repository.yml | 34 ++++++++++++++ tests/integration/mirror_pull_test.go | 18 ++++++++ tests/integration/mirror_push_test.go | 43 ++++++++++++++++++ tests/integration/repo_migrate_test.go | 44 +++++++++++++++++++ 12 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 tests/integration/fixtures/TestPullMirrorRedactCredentials/mirror.yml create mode 100644 tests/integration/fixtures/TestPullMirrorRedactCredentials/repository.yml diff --git a/models/error.go b/models/error.go index ebaa8a135d..99c8ded766 100644 --- a/models/error.go +++ b/models/error.go @@ -121,6 +121,7 @@ type ErrInvalidCloneAddr struct { IsInvalidPath bool IsProtocolInvalid bool IsPermissionDenied bool + HasCredentials bool LocalPath bool } @@ -143,6 +144,9 @@ func (err *ErrInvalidCloneAddr) Error() string { if err.IsURLError { return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url is invalid", err.Host) } + if err.HasCredentials { + return fmt.Sprintf("migration/cloning from '%s' is not allowed: the provided url contains credentials", err.Host) + } return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host) } diff --git a/options/locale_next/locale_en-US.json b/options/locale_next/locale_en-US.json index 3262b49f6c..c92d9e9521 100644 --- a/options/locale_next/locale_en-US.json +++ b/options/locale_next/locale_en-US.json @@ -54,6 +54,7 @@ "other": "wants to merge %[1]d commits from %[2]s into %[3]s" }, "repo.form.cannot_create": "All spaces in which you can create repositories have reached the limit of repositories.", + "migrate.form.error.url_credentials": "The URL contains contains credentials, put them in the username and password fields respectively", "repo.issue_indexer.title": "Issue Indexer", "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/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index a848a950db..e58545c2f6 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -283,6 +283,8 @@ func handleRemoteAddrError(ctx *context.APIContext, err error) { } case addrErr.IsInvalidPath: ctx.Error(http.StatusUnprocessableEntity, "", "Invalid local path, it does not exist or not a directory.") + case addrErr.HasCredentials: + ctx.Error(http.StatusUnprocessableEntity, "", "The URL contains credentials.") default: ctx.Error(http.StatusInternalServerError, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error()) } diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index bc48c6acb7..8412b4a95b 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -441,6 +441,8 @@ func HandleRemoteAddressError(ctx *context.APIContext, err error) { ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid Url ") case addrErr.IsPermissionDenied: ctx.Error(http.StatusUnauthorized, "CreatePushMirror", "Permission denied") + case addrErr.HasCredentials: + ctx.Error(http.StatusBadRequest, "CreatePushMirror", "The URL contains credentials") default: ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Unknown error") } diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go index 86d2461e94..3a5cf30dbe 100644 --- a/routers/web/repo/migrate.go +++ b/routers/web/repo/migrate.go @@ -138,6 +138,8 @@ func handleMigrateRemoteAddrError(ctx *context.Context, err error, tpl base.TplN } case addrErr.IsInvalidPath: ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), tpl, form) + case addrErr.HasCredentials: + ctx.RenderWithErr(ctx.Tr("migrate.form.error.url_credentials"), tpl, form) default: log.Error("Error whilst updating url: %v", err) ctx.RenderWithErr(ctx.Tr("form.url_error", "unknown"), tpl, form) diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 6f35e19880..518ddc1b77 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -542,7 +542,13 @@ func SettingsPost(ctx *context.Context) { mirror_service.AddPullMirrorToQueue(repo.ID) - ctx.Flash.Info(ctx.Tr("repo.settings.pull_mirror_sync_in_progress", repo.OriginalURL)) + sanitizedOriginalURL, err := util.SanitizeURL(repo.OriginalURL) + if err != nil { + ctx.ServerError("SanitizeURL", err) + return + } + + ctx.Flash.Info(ctx.Tr("repo.settings.pull_mirror_sync_in_progress", sanitizedOriginalURL)) ctx.Redirect(repo.Link() + "/settings") case "push-mirror-sync": @@ -1091,6 +1097,8 @@ func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.R } case addrErr.IsInvalidPath: ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), tplSettingsOptions, form) + case addrErr.HasCredentials: + ctx.RenderWithErr(ctx.Tr("migrate.form.error.url_credentials"), tplSettingsOptions, form) default: ctx.ServerError("Unknown error", err) } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index bb81e939b0..3e9ef171a2 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -105,6 +105,9 @@ func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, err if err != nil { return "", &models.ErrInvalidCloneAddr{IsURLError: true, Host: remoteAddr} } + if u.User != nil { + return "", &models.ErrInvalidCloneAddr{Host: remoteAddr, HasCredentials: true} + } if len(authUsername)+len(authPassword) > 0 { u.User = url.UserPassword(authUsername, authPassword) } diff --git a/tests/integration/fixtures/TestPullMirrorRedactCredentials/mirror.yml b/tests/integration/fixtures/TestPullMirrorRedactCredentials/mirror.yml new file mode 100644 index 0000000000..d5010d433f --- /dev/null +++ b/tests/integration/fixtures/TestPullMirrorRedactCredentials/mirror.yml @@ -0,0 +1,9 @@ +- + id: 1001 + repo_id: 1 + interval: 3600 + enable_prune: false + updated_unix: 0 + next_update_unix: 0 + lfs_enabled: false + lfs_endpoint: "" diff --git a/tests/integration/fixtures/TestPullMirrorRedactCredentials/repository.yml b/tests/integration/fixtures/TestPullMirrorRedactCredentials/repository.yml new file mode 100644 index 0000000000..ea76a569ea --- /dev/null +++ b/tests/integration/fixtures/TestPullMirrorRedactCredentials/repository.yml @@ -0,0 +1,34 @@ +- + id: 1001 + owner_id: 2 + owner_name: user2 + lower_name: repo1001 + name: repo1001 + default_branch: master + original_url: https://:TOKEN@example.com/example/example.git + num_watches: 0 + num_stars: 0 + num_forks: 0 + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + num_milestones: 0 + num_closed_milestones: 0 + num_projects: 0 + num_closed_projects: 0 + is_private: false + is_empty: false + is_archived: false + is_mirror: true + status: 0 + is_fork: false + fork_id: 0 + is_template: false + template_id: 0 + size: 0 + is_fsck_enabled: true + close_issues_via_commit_in_any_branch: false + created_unix: 1751320800 + updated_unix: 1751320800 + topics: '[]' diff --git a/tests/integration/mirror_pull_test.go b/tests/integration/mirror_pull_test.go index c98af1b773..666ac3d3fd 100644 --- a/tests/integration/mirror_pull_test.go +++ b/tests/integration/mirror_pull_test.go @@ -1,9 +1,11 @@ // Copyright 2019 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration import ( + "net/http" "testing" "forgejo.org/models/db" @@ -13,6 +15,7 @@ import ( "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" "forgejo.org/modules/migration" + forgejo_context "forgejo.org/services/context" mirror_service "forgejo.org/services/mirror" release_service "forgejo.org/services/release" repo_service "forgejo.org/services/repository" @@ -101,3 +104,18 @@ func TestMirrorPull(t *testing.T) { require.NoError(t, err) assert.Equal(t, initCount, count) } + +func TestPullMirrorRedactCredentials(t *testing.T) { + defer unittest.OverrideFixtures("tests/integration/fixtures/TestPullMirrorRedactCredentials")() + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + session.MakeRequest(t, NewRequestWithValues(t, "POST", "/user2/repo1001/settings", map[string]string{ + "_csrf": GetCSRF(t, session, "/user2/repo1001/settings"), + "action": "mirror-sync", + }), http.StatusSeeOther) + + flashCookie := session.GetCookie(forgejo_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Equal(t, "info%3DPulling%2Bchanges%2Bfrom%2Bthe%2Bremote%2Bhttps%253A%252F%252Fexample.com%252Fexample%252Fexample.git%2Bat%2Bthe%2Bmoment.", flashCookie.Value) +} diff --git a/tests/integration/mirror_push_test.go b/tests/integration/mirror_push_test.go index 1377d1eb1b..c4f8b76501 100644 --- a/tests/integration/mirror_push_test.go +++ b/tests/integration/mirror_push_test.go @@ -17,6 +17,7 @@ import ( "time" asymkey_model "forgejo.org/models/asymkey" + auth_model "forgejo.org/models/auth" "forgejo.org/models/db" repo_model "forgejo.org/models/repo" "forgejo.org/models/unit" @@ -26,7 +27,9 @@ import ( "forgejo.org/modules/gitrepo" "forgejo.org/modules/optional" "forgejo.org/modules/setting" + api "forgejo.org/modules/structs" "forgejo.org/modules/test" + "forgejo.org/modules/translation" gitea_context "forgejo.org/services/context" doctor "forgejo.org/services/doctor" "forgejo.org/services/migrations" @@ -38,6 +41,46 @@ import ( "github.com/stretchr/testify/require" ) +func TestPushMirrorRedactCredential(t *testing.T) { + defer test.MockVariableValue(&setting.Mirror.Enabled, true)() + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + cloneAddr := "https://:TOKEN@example.com/example/example.git" + + t.Run("Web route", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + resp := session.MakeRequest(t, NewRequestWithValues(t, "POST", "/user2/repo1/settings", map[string]string{ + "_csrf": GetCSRF(t, session, "/user2/repo1/settings"), + "action": "push-mirror-add", + "push_mirror_address": cloneAddr, + "push_mirror_interval": "0", + }), http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Contains(t, + htmlDoc.doc.Find(".ui.negative.message").Text(), + translation.NewLocale("en-US").Tr("migrate.form.error.url_credentials"), + ) + }) + + t.Run("API route", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + resp := MakeRequest(t, NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/push_mirrors", &api.CreatePushMirrorOption{ + RemoteAddress: cloneAddr, + Interval: "0", + }).AddTokenAuth(token), http.StatusBadRequest) + + var respBody map[string]any + DecodeJSON(t, resp, &respBody) + + assert.Equal(t, "The URL contains credentials", respBody["message"]) + }) +} + func TestMirrorPush(t *testing.T) { onGiteaRun(t, testMirrorPush) } diff --git a/tests/integration/repo_migrate_test.go b/tests/integration/repo_migrate_test.go index 233a55ef8f..90c6779bb9 100644 --- a/tests/integration/repo_migrate_test.go +++ b/tests/integration/repo_migrate_test.go @@ -1,4 +1,5 @@ // Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration @@ -9,7 +10,9 @@ import ( "net/http/httptest" "testing" + auth_model "forgejo.org/models/auth" "forgejo.org/modules/structs" + "forgejo.org/modules/translation" "forgejo.org/tests" "github.com/stretchr/testify/assert" @@ -55,3 +58,44 @@ func TestRepoMigrate(t *testing.T) { }) } } + +func TestRepoMigrateCredentials(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + cloneAddr := "https://:TOKEN@example.com/example/example.git" + + t.Run("Web route", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + resp := session.MakeRequest(t, NewRequestWithValues(t, "POST", "/repo/migrate?service_type=1", map[string]string{ + "_csrf": GetCSRF(t, session, "/repo/migrate?service_type=1"), + "clone_addr": cloneAddr, + "uid": "2", + "repo_name": "example", + "service": "1", + }), http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Contains(t, + htmlDoc.doc.Find(".ui.negative.message").Text(), + translation.NewLocale("en-US").Tr("migrate.form.error.url_credentials"), + ) + }) + + t.Run("API route", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + resp := MakeRequest(t, NewRequestWithJSON(t, "POST", "/api/v1/repos/migrate", &structs.MigrateRepoOptions{ + CloneAddr: cloneAddr, + RepoOwnerID: 2, + RepoName: "example", + }).AddTokenAuth(token), http.StatusUnprocessableEntity) + + var respBody map[string]any + DecodeJSON(t, resp, &respBody) + + assert.Equal(t, "The URL contains credentials.", respBody["message"]) + }) +} From 321015195538b292ec1738669a743c260914ccb1 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 30 Aug 2025 18:43:22 +0200 Subject: [PATCH 67/80] [v12.0/forgejo] fix: ensure GetUserByEmail only considers validated emails (#9085) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9075 Only validated emails can be used to: - assert if a signature can be trusted or, - to assign comments, issues to an existing user during a migration The emails that were not yet validated could previously used as if they were validated, incorrectly showing commits as trusted or assigning comments, issues to the user associated with this email during migrations. Existing migrations are not modified when they were incorrectly assigned to an email that is not validated. The trust status of all commit signatures will now show differently depending on the validation status of an email. ## Release notes - Security bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9075): ensure GetUserByEmail only considers validated emails Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9085 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/user/user.go | 4 ++-- models/user/user_test.go | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/models/user/user.go b/models/user/user.go index 6b54776adf..bfd7e6063f 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -1163,8 +1163,8 @@ func GetUserByEmail(ctx context.Context, email string) (*User, error) { email = strings.ToLower(email) // Otherwise, check in alternative list for activated email addresses - emailAddress := &EmailAddress{LowerEmail: email, IsActivated: true} - has, err := db.GetEngine(ctx).Get(emailAddress) + emailAddress := &EmailAddress{} + has, err := db.GetEngine(ctx).Where("lower_email = ? AND is_activated = ?", email, true).Get(emailAddress) if err != nil { return nil, err } diff --git a/models/user/user_test.go b/models/user/user_test.go index fd9d05653f..288a45105b 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -835,3 +835,25 @@ func TestPronounsPrivacy(t *testing.T) { assert.Equal(t, "any", user.GetPronouns(true)) }) } + +func TestGetUserByEmail(t *testing.T) { + require.NoError(t, unittest.PrepareTestDatabase()) + + t.Run("Normal", func(t *testing.T) { + u, err := user_model.GetUserByEmail(t.Context(), "user2@example.com") + require.NoError(t, err) + assert.EqualValues(t, 2, u.ID) + }) + + t.Run("Not activated", func(t *testing.T) { + u, err := user_model.GetUserByEmail(t.Context(), "user11@example.com") + require.ErrorIs(t, err, user_model.ErrUserNotExist{Name: "user11@example.com"}) + assert.Nil(t, u) + }) + + t.Run("Not primary", func(t *testing.T) { + u, err := user_model.GetUserByEmail(t.Context(), "user1-3@example.com") + require.NoError(t, err) + assert.EqualValues(t, 1, u.ID) + }) +} From 8d1cf92e128bb4b209949d1ee95de2f874b174c5 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 30 Aug 2025 18:43:53 +0200 Subject: [PATCH 68/80] [v12.0/forgejo] fix: require password login for creation of new token (#9080) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9070 Obtaining a [personal access token via the API](https://forgejo.org/docs/latest/user/api-usage/#generating-and-listing-api-tokens) is no longer possible if the password used for basic authentication is an API token or an [OAuth2 token](https://forgejo.org/docs/latest/user/api-usage/#oauth2-provider): it has to be the user password. Such privilege escalation was only possible for tokens with write permissions to the user. This requirement is already enforced when API calls are made with an authorization header [as described in the documentation](https://forgejo.org/docs/latest/user/api-usage/#authentication), but it was not enforced with basic authentication. As a consequence it was possible for an API token with `write:user` permissions or an OAuth2 token to obtain a new token with a wider or identical scope. ## Release notes - Bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9070): require password login for creation of new token Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9080 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/api/v1/api.go | 7 ++- services/auth/basic.go | 1 + tests/integration/api_token_test.go | 81 +++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index c3e8b532cf..b50ca19148 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -415,8 +415,11 @@ func reqBasicOrRevProxyAuth() func(ctx *context.APIContext) { if ctx.IsSigned && setting.Service.EnableReverseProxyAuthAPI && ctx.Data["AuthedMethod"].(string) == auth.ReverseProxyMethodName { return } - if !ctx.IsBasicAuth { - ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required") + + // Require basic authorization method to be used and that basic + // authorization used password login to verify the user. + if passwordLogin, ok := ctx.Data["IsPasswordLogin"].(bool); !ok || !passwordLogin { + ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth method not allowed") return } } diff --git a/services/auth/basic.go b/services/auth/basic.go index f259ad5f69..4ffe712744 100644 --- a/services/auth/basic.go +++ b/services/auth/basic.go @@ -151,6 +151,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore log.Trace("Basic Authorization: Logged in user %-v", u) + store.GetData()["IsPasswordLogin"] = true return u, nil } diff --git a/tests/integration/api_token_test.go b/tests/integration/api_token_test.go index 9d9a44b5d4..0899d0abfa 100644 --- a/tests/integration/api_token_test.go +++ b/tests/integration/api_token_test.go @@ -4,8 +4,10 @@ package integration import ( + "encoding/base64" "fmt" "net/http" + "net/url" "testing" auth_model "forgejo.org/models/auth" @@ -13,9 +15,11 @@ import ( user_model "forgejo.org/models/user" "forgejo.org/modules/log" api "forgejo.org/modules/structs" + "forgejo.org/modules/test" "forgejo.org/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TestAPICreateAndDeleteToken tests that token that was just created can be deleted @@ -580,3 +584,80 @@ func deleteAPIAccessToken(t *testing.T, accessToken api.AccessToken, user *user_ unittest.AssertNotExistsBean(t, &auth_model.AccessToken{ID: accessToken.ID}) } + +func TestAPITokenCreation(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user4") + t.Run("Via API token", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteUser) + + req := NewRequestWithJSON(t, "POST", "/api/v1/users/user4/tokens", map[string]any{ + "name": "new-new-token", + "scopes": []auth_model.AccessTokenScope{auth_model.AccessTokenScopeWriteUser}, + }) + req.Request.Header.Set("Authorization", "basic "+base64.StdEncoding.EncodeToString([]byte("user4:"+token))) + + resp := MakeRequest(t, req, http.StatusUnauthorized) + + respMsg := map[string]any{} + DecodeJSON(t, resp, &respMsg) + + assert.EqualValues(t, "auth method not allowed", respMsg["message"]) + }) + + t.Run("Via OAuth2", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{ + "_csrf": GetCSRF(t, session, "/login/oauth/authorize?client_id=ce5a1322-42a7-11ed-b878-0242ac120002&redirect_uri=b&response_type=code&code_challenge_method=plain&code_challenge=CODE&state=thestate"), + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "redirect_uri": "b", + "state": "thestate", + "granted": "true", + }) + resp := session.MakeRequest(t, req, http.StatusSeeOther) + + u, err := url.Parse(test.RedirectURL(resp)) + require.NoError(t, err) + + req = NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{ + "client_id": "ce5a1322-42a7-11ed-b878-0242ac120002", + "code": u.Query().Get("code"), + "code_verifier": "CODE", + "grant_type": "authorization_code", + "redirect_uri": "b", + }) + resp = MakeRequest(t, req, http.StatusOK) + + var respBody map[string]any + DecodeJSON(t, resp, &respBody) + + req = NewRequestWithJSON(t, "POST", "/api/v1/users/user4/tokens", map[string]any{ + "name": "new-new-token", + "scopes": []auth_model.AccessTokenScope{auth_model.AccessTokenScopeWriteUser}, + }) + req.Request.Header.Set("Authorization", "basic "+base64.StdEncoding.EncodeToString([]byte("user4:"+respBody["access_token"].(string)))) + + resp = MakeRequest(t, req, http.StatusUnauthorized) + + respMsg := map[string]any{} + DecodeJSON(t, resp, &respMsg) + + assert.EqualValues(t, "auth method not allowed", respMsg["message"]) + }) + + t.Run("Via password", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithJSON(t, "POST", "/api/v1/users/user4/tokens", map[string]any{ + "name": "new-new-token", + "scopes": []auth_model.AccessTokenScope{auth_model.AccessTokenScopeWriteUser}, + }) + req.Request.Header.Set("Authorization", "basic "+base64.StdEncoding.EncodeToString([]byte("user4:"+userPassword))) + + MakeRequest(t, req, http.StatusCreated) + }) +} From 48505123c774b1f3da26cbd6a573247a208e2919 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 30 Aug 2025 18:44:17 +0200 Subject: [PATCH 69/80] [v12.0/forgejo] fix: delete old auth token upon replacing primary email (#9087) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9076 When the primary email is changed before it is validated, the URL sent for validation purposes must be invalidated. It was previously possible use to delay use of the URL to validate the primary email and modify the primary email in the meantime. It allowed to validate the newer primary email using the older primary email, effectively bypassing validation. ## Release notes - Security bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9076): delete old auth token upon replacing primary email Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9087 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- .../forgejo_auth_token.yml | 5 +++ services/user/email.go | 6 +++ services/user/email_test.go | 40 ++++++++++++------- 3 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 services/user/TestReplaceInactivePrimaryEmail/forgejo_auth_token.yml diff --git a/services/user/TestReplaceInactivePrimaryEmail/forgejo_auth_token.yml b/services/user/TestReplaceInactivePrimaryEmail/forgejo_auth_token.yml new file mode 100644 index 0000000000..93a6c184b6 --- /dev/null +++ b/services/user/TestReplaceInactivePrimaryEmail/forgejo_auth_token.yml @@ -0,0 +1,5 @@ +- + id: 1001 + uid: 10 + lookup_key: unique + purpose: user_activation diff --git a/services/user/email.go b/services/user/email.go index 36a1145aec..cc38dacf95 100644 --- a/services/user/email.go +++ b/services/user/email.go @@ -9,6 +9,7 @@ import ( "errors" "strings" + auth_model "forgejo.org/models/auth" "forgejo.org/models/db" user_model "forgejo.org/models/user" "forgejo.org/modules/setting" @@ -171,6 +172,11 @@ func ReplaceInactivePrimaryEmail(ctx context.Context, oldEmail string, email *us return err } + // Delete previous activation token. + if err := auth_model.DeleteAuthTokenByUser(ctx, user.ID); err != nil { + return err + } + return DeleteEmailAddresses(ctx, user, []string{oldEmail}) } diff --git a/services/user/email_test.go b/services/user/email_test.go index b48936a27e..d2cff22696 100644 --- a/services/user/email_test.go +++ b/services/user/email_test.go @@ -6,6 +6,7 @@ package user import ( "testing" + auth_model "forgejo.org/models/auth" "forgejo.org/models/db" organization_model "forgejo.org/models/organization" "forgejo.org/models/unittest" @@ -123,25 +124,34 @@ func TestAddEmailAddresses(t *testing.T) { } func TestReplaceInactivePrimaryEmail(t *testing.T) { + defer unittest.OverrideFixtures("services/user/TestReplaceInactivePrimaryEmail/")() require.NoError(t, unittest.PrepareTestDatabase()) - email := &user_model.EmailAddress{ - Email: "user9999999@example.com", - UID: 9999999, - } - err := ReplaceInactivePrimaryEmail(db.DefaultContext, "user10@example.com", email) - require.Error(t, err) - assert.True(t, user_model.IsErrUserNotExist(err)) + t.Run("User doesn't exist", func(t *testing.T) { + email := &user_model.EmailAddress{ + Email: "user9999999@example.com", + UID: 9999999, + } + err := ReplaceInactivePrimaryEmail(db.DefaultContext, "user10@example.com", email) + require.Error(t, err) + assert.True(t, user_model.IsErrUserNotExist(err)) + }) - email = &user_model.EmailAddress{ - Email: "user201@example.com", - UID: 10, - } - err = ReplaceInactivePrimaryEmail(db.DefaultContext, "user10@example.com", email) - require.NoError(t, err) + t.Run("Normal", func(t *testing.T) { + unittest.AssertExistsIf(t, true, &auth_model.AuthorizationToken{UID: 10}) - user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) - assert.Equal(t, "user201@example.com", user.Email) + email := &user_model.EmailAddress{ + Email: "user201@example.com", + UID: 10, + } + err := ReplaceInactivePrimaryEmail(db.DefaultContext, "user10@example.com", email) + require.NoError(t, err) + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}) + assert.Equal(t, "user201@example.com", user.Email) + + unittest.AssertExistsIf(t, false, &auth_model.AuthorizationToken{UID: 10}) + }) } func TestDeleteEmailAddresses(t *testing.T) { From 192018324f8e0728c9da977b50dbad552ddfac79 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 30 Aug 2025 18:45:00 +0200 Subject: [PATCH 70/80] [v12.0/forgejo] fix: consistently enforce 2FA on OpenID 2.0 (#9097) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9073 ## Release notes - Security bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9073): consistently enforce 2FA on OpenID 2.0 Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9097 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/user/openid.go | 6 ++-- routers/web/auth/2fa.go | 16 +++++++++ routers/web/auth/auth.go | 1 + routers/web/auth/openid.go | 63 ++++++++++++++++++++++++++++-------- routers/web/auth/webauthn.go | 8 +++++ services/auth/auth.go | 1 + 6 files changed, 79 insertions(+), 16 deletions(-) diff --git a/models/user/openid.go b/models/user/openid.go index 96b00255a3..b4d4f175b2 100644 --- a/models/user/openid.go +++ b/models/user/openid.go @@ -40,8 +40,8 @@ func GetUserOpenIDs(ctx context.Context, uid int64) ([]*UserOpenID, error) { return openids, nil } -// isOpenIDUsed returns true if the openid has been used. -func isOpenIDUsed(ctx context.Context, uri string) (bool, error) { +// IsOpenIDUsed returns true if the openid has been used. +func IsOpenIDUsed(ctx context.Context, uri string) (bool, error) { if len(uri) == 0 { return true, nil } @@ -71,7 +71,7 @@ func (err ErrOpenIDAlreadyUsed) Unwrap() error { // AddUserOpenID adds an pre-verified/normalized OpenID URI to given user. // NOTE: make sure openid.URI is normalized already func AddUserOpenID(ctx context.Context, openid *UserOpenID) error { - used, err := isOpenIDUsed(ctx, openid.URI) + used, err := IsOpenIDUsed(ctx, openid.URI) if err != nil { return err } else if used { diff --git a/routers/web/auth/2fa.go b/routers/web/auth/2fa.go index ff769ffd5d..6f2f8239f1 100644 --- a/routers/web/auth/2fa.go +++ b/routers/web/auth/2fa.go @@ -81,6 +81,14 @@ func TwoFactorPost(ctx *context.Context) { } } + // Handle OpenID linking to user. + if oid, ok := ctx.Session.Get("twofaOpenID").(string); ok { + if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: u.ID, URI: oid}); err != nil { + ctx.ServerError("AddUserOpenID", err) + return + } + } + twofa.LastUsedPasscode = form.Passcode if err = auth.UpdateTwoFactor(ctx, twofa); err != nil { ctx.ServerError("UserSignIn", err) @@ -146,6 +154,14 @@ func TwoFactorScratchPost(ctx *context.Context) { return } + // Handle OpenID linking to user. + if oid, ok := ctx.Session.Get("twofaOpenID").(string); ok { + if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: u.ID, URI: oid}); err != nil { + ctx.ServerError("AddUserOpenID", err) + return + } + } + handleSignInFull(ctx, u, remember, false) if ctx.Written() { return diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index dbb6665398..daf3373dec 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -310,6 +310,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe "openid_determined_username", "twofaUid", "twofaRemember", + "twofaOpenID", "linkAccount", }, map[string]any{ "uid": u.ID, diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index fcb2155953..1d7bdcd1ce 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" + auth_model "forgejo.org/models/auth" user_model "forgejo.org/models/user" "forgejo.org/modules/auth/openid" "forgejo.org/modules/base" @@ -253,6 +254,7 @@ func ConnectOpenID(ctx *context.Context) { // ConnectOpenIDPost handles submission of a form to connect an OpenID URI to an existing account func ConnectOpenIDPost(ctx *context.Context) { form := web.GetForm(ctx).(*forms.ConnectOpenIDForm) + remember, _ := ctx.Session.Get("openid_signin_remember").(bool) oid, _ := ctx.Session.Get("openid_verified_uri").(string) if oid == "" { ctx.Redirect(setting.AppSubURL + "/user/login/openid") @@ -264,28 +266,63 @@ func ConnectOpenIDPost(ctx *context.Context) { ctx.Data["EnableOpenIDSignUp"] = setting.Service.EnableOpenIDSignUp ctx.Data["OpenID"] = oid - u, _, err := auth.UserSignIn(ctx, form.UserName, form.Password) + u, source, err := auth.UserSignIn(ctx, form.UserName, form.Password) if err != nil { handleSignInError(ctx, form.UserName, &form, tplConnectOID, "ConnectOpenIDPost", err) return } - // add OpenID for the user - userOID := &user_model.UserOpenID{UID: u.ID, URI: oid} - if err = user_model.AddUserOpenID(ctx, userOID); err != nil { - if user_model.IsErrOpenIDAlreadyUsed(err) { - ctx.RenderWithErr(ctx.Tr("form.openid_been_used", oid), tplConnectOID, &form) - return - } - ctx.ServerError("AddUserOpenID", err) + // Check if OID is already in use. + if used, err := user_model.IsOpenIDUsed(ctx, oid); err != nil { + ctx.ServerError("IsOpenIDUsed", err) + return + } else if used { + ctx.RenderWithErr(ctx.Tr("form.openid_been_used", oid), tplConnectOID, &form) return } - ctx.Flash.Success(ctx.Tr("settings.add_openid_success")) + // Check if 2FA needs to be done. + has2FA, err := auth_model.HasTwoFactorByUID(ctx, u.ID) + if err != nil { + ctx.ServerError("HasTwoFactorByUID", err) + return + } + if skipper, ok := source.Cfg.(auth.LocalTwoFASkipper); !has2FA || (ok && skipper.IsSkipLocalTwoFA()) { + // Link this OID to the user. + if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: u.ID, URI: oid}); err != nil { + ctx.ServerError("AddUserOpenID", err) + return + } - remember, _ := ctx.Session.Get("openid_signin_remember").(bool) - log.Trace("Session stored openid-remember: %t", remember) - handleSignIn(ctx, u, remember) + ctx.Flash.Success(ctx.Tr("settings.add_openid_success")) + handleSignIn(ctx, u, remember) + return + } + + // Check if the user has webauthn registration. + hasWebAuthnTwofa, err := auth_model.HasWebAuthnRegistrationsByUID(ctx, u.ID) + if err != nil { + ctx.ServerError("HasWebAuthnRegistrationsByUID", err) + return + } + + if err := updateSession(ctx, nil, map[string]any{ + "twofaUid": u.ID, + "twofaRemember": remember, + "twofaOpenID": oid, + }); err != nil { + ctx.ServerError("Unable to update session", err) + return + } + + // If we have WebAuthn, redirect there first. + if hasWebAuthnTwofa { + ctx.Redirect(setting.AppSubURL + "/user/webauthn") + return + } + + // Fallback to TOTP. + ctx.Redirect(setting.AppSubURL + "/user/two_factor") } // RegisterOpenID shows a form to create a new user authenticated via an OpenID URI diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go index 3da6199b6e..35bc1e8bf5 100644 --- a/routers/web/auth/webauthn.go +++ b/routers/web/auth/webauthn.go @@ -166,6 +166,14 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) { } } + // Handle OpenID linking to user. + if oid, ok := ctx.Session.Get("twofaOpenID").(string); ok { + if err := user_model.AddUserOpenID(ctx, &user_model.UserOpenID{UID: user.ID, URI: oid}); err != nil { + ctx.ServerError("AddUserOpenID", err) + return + } + } + remember := ctx.Session.Get("twofaRemember").(bool) redirect := handleSignInFull(ctx, user, remember, false) if redirect == "" { diff --git a/services/auth/auth.go b/services/auth/auth.go index 85c9296ced..85121b2d7f 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -77,6 +77,7 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore _ = sess.Delete("openid_determined_username") _ = sess.Delete("twofaUid") _ = sess.Delete("twofaRemember") + _ = sess.Delete("twofaOpenID") _ = sess.Delete("webauthnAssertion") _ = sess.Delete("linkAccount") err = sess.Set("uid", user.ID) From 1bc42842ba3d457f63a155063932e0b97d2a1b2b Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 30 Aug 2025 18:45:30 +0200 Subject: [PATCH 71/80] [v12.0/forgejo] fix: email comments are removed from email addresses (#9083) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9074 When registering with an email account including a comment (e.g. `me@example.com (a comment here)`), the comment is removed from the email address. It was possible to include an email address in the comment to bypass the block list. For instance if registering with `me@evilcorp.com (me@example.com)` the mail would incorrectly be verified against the block list using the comment instead of `@evilcorp.com`. This is a regression introduced in Forgejo v12. ## Release notes - Security bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9074): email comments are removed from email addresses Co-authored-by: famfo Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9083 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- modules/validation/email.go | 15 +++-- modules/validation/email_test.go | 5 -- routers/api/v1/admin/user.go | 4 +- routers/web/admin/users.go | 4 +- routers/web/auth/auth.go | 5 +- routers/web/auth/linkaccount.go | 5 +- services/forms/user_form.go | 2 +- services/forms/user_form_test.go | 14 ++-- .../integration/email_block_allowlist_test.go | 66 +++++++++++++++++++ 9 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 tests/integration/email_block_allowlist_test.go diff --git a/modules/validation/email.go b/modules/validation/email.go index 8e1ffc203d..7960a80a1f 100644 --- a/modules/validation/email.go +++ b/modules/validation/email.go @@ -72,16 +72,23 @@ func validateEmailBasic(email string) error { } func validateEmailDomain(email string) error { - if !IsEmailDomainAllowed(email) { + if _, ok := IsEmailDomainAllowed(email); !ok { return ErrEmailInvalid{email} } return nil } -func IsEmailDomainAllowed(email string) bool { - return isEmailDomainAllowedInternal( - email, +func IsEmailDomainAllowed(email string) (validEmail, ok bool) { + // Normalized the address. This strips for example comments which could be + // used to smuggle a different domain + parsedAddress, err := mail.ParseAddress(email) + if err != nil { + return false, false + } + + return true, isEmailDomainAllowedInternal( + parsedAddress.Address, setting.Service.EmailDomainAllowList, setting.Service.EmailDomainBlockList) } diff --git a/modules/validation/email_test.go b/modules/validation/email_test.go index b7ee766ddb..28158cae53 100644 --- a/modules/validation/email_test.go +++ b/modules/validation/email_test.go @@ -67,8 +67,3 @@ func TestEmailAddressValidate(t *testing.T) { }) } } - -func TestEmailDomainAllowList(t *testing.T) { - res := IsEmailDomainAllowed("someuser@localhost.localdomain") - assert.True(t, res) -} diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 8aa67b3b0a..6741f5bfc0 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -149,7 +149,7 @@ func CreateUser(ctx *context.APIContext) { return } - if !validation.IsEmailDomainAllowed(u.Email) { + if _, ok := validation.IsEmailDomainAllowed(u.Email); !ok { ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", u.Email)) } @@ -235,7 +235,7 @@ func EditUser(ctx *context.APIContext) { return } - if !validation.IsEmailDomainAllowed(*form.Email) { + if _, ok := validation.IsEmailDomainAllowed(*form.Email); !ok { ctx.Resp.Header().Add("X-Gitea-Warning", fmt.Sprintf("the domain of user email %s conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", *form.Email)) } } diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 964326291e..c4727177ba 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -201,7 +201,7 @@ func NewUserPost(ctx *context.Context) { return } - if !validation.IsEmailDomainAllowed(u.Email) { + if _, ok := validation.IsEmailDomainAllowed(u.Email); !ok { ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", u.Email)) } @@ -421,7 +421,7 @@ func EditUserPost(ctx *context.Context) { } return } - if !validation.IsEmailDomainAllowed(form.Email) { + if _, ok := validation.IsEmailDomainAllowed(form.Email); !ok { ctx.Flash.Warning(ctx.Tr("form.email_domain_is_not_allowed", form.Email)) } } diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index daf3373dec..3b4e62c429 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -453,7 +453,10 @@ func SignUpPost(ctx *context.Context) { return } - if !form.IsEmailDomainAllowed() { + if emailValid, ok := form.IsEmailDomainAllowed(); !emailValid { + ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSignUp, form) + return + } else if !ok { ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplSignUp, &form) return } diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index 2bba614d8c..c1fc6ebb69 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -244,7 +244,10 @@ func LinkAccountPostRegister(ctx *context.Context) { } } - if !form.IsEmailDomainAllowed() { + if emailValid, ok := form.IsEmailDomainAllowed(); !emailValid { + ctx.RenderWithErr(ctx.Tr("form.email_invalid"), tplSignUp, form) + return + } else if !ok { ctx.RenderWithErr(ctx.Tr("auth.email_domain_blacklisted"), tplLinkAccount, &form) return } diff --git a/services/forms/user_form.go b/services/forms/user_form.go index dfd5b3da9b..8c95139e2c 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -109,7 +109,7 @@ func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding. // The email is marked as allowed if it matches any of the // domains in the whitelist or if it doesn't match any of // domains in the blocklist, if any such list is not empty. -func (f *RegisterForm) IsEmailDomainAllowed() bool { +func (f *RegisterForm) IsEmailDomainAllowed() (validEmail, ok bool) { return validation.IsEmailDomainAllowed(f.Email) } diff --git a/services/forms/user_form_test.go b/services/forms/user_form_test.go index ae08f65f23..4ce63ab552 100644 --- a/services/forms/user_form_test.go +++ b/services/forms/user_form_test.go @@ -20,7 +20,9 @@ func TestRegisterForm_IsDomainAllowed_Empty(t *testing.T) { form := RegisterForm{} - assert.True(t, form.IsEmailDomainAllowed()) + emailValid, ok := form.IsEmailDomainAllowed() + assert.False(t, emailValid) + assert.False(t, ok) } func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) { @@ -36,7 +38,8 @@ func TestRegisterForm_IsDomainAllowed_InvalidEmail(t *testing.T) { for _, v := range tt { form := RegisterForm{Email: v.email} - assert.False(t, form.IsEmailDomainAllowed()) + _, ok := form.IsEmailDomainAllowed() + assert.False(t, ok) } } @@ -59,7 +62,8 @@ func TestRegisterForm_IsDomainAllowed_AllowedEmail(t *testing.T) { for _, v := range tt { form := RegisterForm{Email: v.email} - assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) + _, ok := form.IsEmailDomainAllowed() + assert.Equal(t, v.valid, ok) } } @@ -72,7 +76,6 @@ func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) { }{ {"security@gitea.io", false}, {"security@gitea.example", true}, - {"invalid", true}, {"user@my.block", false}, {"user@my.block1", true}, @@ -81,7 +84,8 @@ func TestRegisterForm_IsDomainAllowed_BlockedEmail(t *testing.T) { for _, v := range tt { form := RegisterForm{Email: v.email} - assert.Equal(t, v.valid, form.IsEmailDomainAllowed()) + _, ok := form.IsEmailDomainAllowed() + assert.Equal(t, v.valid, ok) } } diff --git a/tests/integration/email_block_allowlist_test.go b/tests/integration/email_block_allowlist_test.go new file mode 100644 index 0000000000..449a5cbf0c --- /dev/null +++ b/tests/integration/email_block_allowlist_test.go @@ -0,0 +1,66 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "testing" + + "forgejo.org/modules/setting" + "forgejo.org/modules/test" + "forgejo.org/modules/validation" + "forgejo.org/tests" + + "github.com/gobwas/glob" + "github.com/stretchr/testify/assert" +) + +func TestEmailBlocklist(t *testing.T) { + defer test.MockVariableValue( + &setting.Service.EmailDomainBlockList, + []glob.Glob{glob.MustCompile("evil")}, + )() + + defer tests.PrepareTestEnv(t)() + + emailValid, ok := validation.IsEmailDomainAllowed("🐸@pond") + assert.True(t, emailValid) + assert.True(t, ok) + + emailValid, ok = validation.IsEmailDomainAllowed("🐸@pond (what-is-this@evil)") + assert.True(t, emailValid) + assert.True(t, ok) + + emailValid, ok = validation.IsEmailDomainAllowed("jomo@evil") + assert.True(t, emailValid) + assert.False(t, ok) + + emailValid, ok = validation.IsEmailDomainAllowed("jomo@evil (but-does-it@break)") + assert.True(t, emailValid) + assert.False(t, ok) +} + +func TestEmailAllowlist(t *testing.T) { + defer test.MockVariableValue( + &setting.Service.EmailDomainAllowList, + []glob.Glob{glob.MustCompile("pond")}, + )() + + defer tests.PrepareTestEnv(t)() + + emailValid, ok := validation.IsEmailDomainAllowed("🐸@pond") + assert.True(t, emailValid) + assert.True(t, ok) + + emailValid, ok = validation.IsEmailDomainAllowed("🐸@pond (what-is-this@evil)") + assert.True(t, emailValid) + assert.True(t, ok) + + emailValid, ok = validation.IsEmailDomainAllowed("jomo@evil") + assert.True(t, emailValid) + assert.False(t, ok) + + emailValid, ok = validation.IsEmailDomainAllowed("jomo@evil (but-does-it@break)") + assert.True(t, emailValid) + assert.False(t, ok) +} From b98109ee6989a26a9bc153bd422a0dce2ec35051 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 30 Aug 2025 18:45:56 +0200 Subject: [PATCH 72/80] [v12.0/forgejo] fix: use credentials helpers for git clones (#9068) **Backport**: https://codeberg.org/forgejo/forgejo/pulls/9067 When performing a `git clone` that requires credentials, they are temporarily stored in files and used with [Git credential](https://git-scm.com/docs/gitcredentials/2.50.0#_requesting_credentials). They were previously included in the URL that were readable by a user with shell access to the host running the Forgejo instance when, for instance, they ask for the list of process (`ps`). Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9068 --- modules/git/repo.go | 88 ++++++++++++++--- modules/git/repo_test.go | 85 +++++++++++++++++ modules/util/shellquote_test.go | 22 ++++- tests/integration/codeowner_test.go | 1 + .../git_helper_for_declarative_test.go | 12 +++ tests/integration/patch_status_test.go | 1 + tests/integration/pull_review_test.go | 2 + .../repo_migrate_credentials_test.go | 95 +++++++++++++++++++ 8 files changed, 291 insertions(+), 15 deletions(-) create mode 100644 tests/integration/repo_migrate_credentials_test.go diff --git a/modules/git/repo.go b/modules/git/repo.go index 23e9337615..4f2b05bca5 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -18,6 +18,7 @@ import ( "strings" "time" + "forgejo.org/modules/log" "forgejo.org/modules/proxy" "forgejo.org/modules/setting" "forgejo.org/modules/util" @@ -160,24 +161,89 @@ func CloneWithArgs(ctx context.Context, args TrustedCmdArgs, from, to string, op if len(opts.Branch) > 0 { cmd.AddArguments("-b").AddDynamicArguments(opts.Branch) } - cmd.AddDashesAndList(from, to) - if strings.Contains(from, "://") && strings.Contains(from, "@") { - cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.SanitizeCredentialURLs(from), to, opts.Shared, opts.Mirror, opts.Depth)) - } else { - cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, from, to, opts.Shared, opts.Mirror, opts.Depth)) + envs := os.Environ() + parsedFromURL, err := url.Parse(from) + if err == nil { + envs = proxy.EnvWithProxy(parsedFromURL) } + fromURL := from + sanitizedFrom := from + + // If the clone URL has credentials, sanitize it and store the credentials in + // a temporary file that git will access. + if strings.Contains(from, "://") && strings.Contains(from, "@") { + sanitizedFrom = util.SanitizeCredentialURLs(from) + if parsedFromURL != nil { + if pwd, has := parsedFromURL.User.Password(); has { + parsedFromURL.User = url.User(parsedFromURL.User.Username()) + fromURL = parsedFromURL.String() + + credentialsFile, err := os.CreateTemp(os.TempDir(), "forgejo-clone-credentials") + if err != nil { + return err + } + credentialsPath := credentialsFile.Name() + + defer func() { + _ = credentialsFile.Close() + if err := util.Remove(credentialsPath); err != nil { + log.Warn("Unable to remove temporary file %q: %v", credentialsPath, err) + } + }() + + // Make it read-write. + if err := credentialsFile.Chmod(0o600); err != nil { + return err + } + + // Write the password. + if _, err := fmt.Fprint(credentialsFile, pwd); err != nil { + return err + } + + askpassFile, err := os.CreateTemp(os.TempDir(), "forgejo-askpass") + if err != nil { + return err + } + askpassPath := askpassFile.Name() + + defer func() { + _ = askpassFile.Close() + if err := util.Remove(askpassPath); err != nil { + log.Warn("Unable to remove temporary file %q: %v", askpassPath, err) + } + }() + + // Make it executable. + if err := askpassFile.Chmod(0o700); err != nil { + return err + } + + // Write the password script. + if _, err := fmt.Fprintf(askpassFile, "exec cat %s", credentialsPath); err != nil { + return err + } + + // Close it, so that Git can use it and no busy errors arise. + _ = askpassFile.Close() + _ = credentialsFile.Close() + + // Use environments to specify that git should ask for credentials, this + // takes precedences over anything else https://git-scm.com/docs/gitcredentials#_requesting_credentials. + envs = append(envs, "GIT_ASKPASS="+askpassPath) + } + } + } + + cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, sanitizedFrom, to, opts.Shared, opts.Mirror, opts.Depth)) + cmd.AddDashesAndList(fromURL, to) + if opts.Timeout <= 0 { opts.Timeout = -1 } - envs := os.Environ() - u, err := url.Parse(from) - if err == nil { - envs = proxy.EnvWithProxy(u) - } - stderr := new(bytes.Buffer) if err = cmd.Run(&RunOpts{ Timeout: opts.Timeout, diff --git a/modules/git/repo_test.go b/modules/git/repo_test.go index c3971b2a5f..b07fc3732d 100644 --- a/modules/git/repo_test.go +++ b/modules/git/repo_test.go @@ -4,7 +4,15 @@ package git import ( + "bytes" + "encoding/base64" + "io/fs" + "net/http" + "net/http/httptest" + "net/url" + "os" "path/filepath" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -54,3 +62,80 @@ func TestRepoGetDivergingCommits(t *testing.T) { Behind: 2, }, do) } + +func TestCloneCredentials(t *testing.T) { + calledWithoutPassword := false + askpassFile := "" + credentialsFile := "" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + if req.URL.Path != "/info/refs" { + return + } + + // Get basic authorization. + auth, ok := strings.CutPrefix(req.Header.Get("Authorization"), "Basic ") + if !ok { + w.Header().Set("WWW-Authenticate", `Basic realm="Forgejo"`) + http.Error(w, "require credentials", http.StatusUnauthorized) + return + } + + rawAuth, err := base64.StdEncoding.DecodeString(auth) + require.NoError(t, err) + + user, password, ok := bytes.Cut(rawAuth, []byte{':'}) + assert.True(t, ok) + + // First time around Git tries without password (that's specified in the clone URL). + // It demonstrates it doesn't immediately uses askpass. + if len(password) == 0 { + assert.EqualValues(t, "oauth2", user) + calledWithoutPassword = true + + w.Header().Set("WWW-Authenticate", `Basic realm="Forgejo"`) + http.Error(w, "require credentials", http.StatusUnauthorized) + return + } + + assert.EqualValues(t, "oauth2", user) + assert.EqualValues(t, "some_token", password) + + tmpDir := os.TempDir() + + // Verify that the askpass implementation was used. + files, err := fs.Glob(os.DirFS(tmpDir), "forgejo-askpass*") + require.NoError(t, err) + for _, fileName := range files { + fileContent, err := os.ReadFile(filepath.Join(tmpDir, fileName)) + require.NoError(t, err) + + credentialsPath, ok := bytes.CutPrefix(fileContent, []byte(`exec cat `)) + assert.True(t, ok) + + fileContent, err = os.ReadFile(string(credentialsPath)) + require.NoError(t, err) + assert.EqualValues(t, "some_token", fileContent) + + askpassFile = filepath.Join(tmpDir, fileName) + credentialsFile = string(credentialsPath) + } + })) + + serverURL, err := url.Parse(server.URL) + require.NoError(t, err) + + serverURL.User = url.UserPassword("oauth2", "some_token") + + require.NoError(t, Clone(t.Context(), serverURL.String(), t.TempDir(), CloneRepoOptions{})) + + assert.True(t, calledWithoutPassword) + assert.NotEmpty(t, askpassFile) + assert.NotEmpty(t, credentialsFile) + + // Check that the helper files are gone. + _, err = os.Stat(askpassFile) + require.ErrorIs(t, err, fs.ErrNotExist) + _, err = os.Stat(credentialsFile) + require.ErrorIs(t, err, fs.ErrNotExist) +} diff --git a/modules/util/shellquote_test.go b/modules/util/shellquote_test.go index 969998c592..6c1b778a08 100644 --- a/modules/util/shellquote_test.go +++ b/modules/util/shellquote_test.go @@ -3,7 +3,11 @@ package util -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/assert" +) func TestShellEscape(t *testing.T) { tests := []struct { @@ -79,13 +83,23 @@ func TestShellEscape(t *testing.T) { "Single quotes don't need to escape except for '...", "~/ ${gitea} `gitea` (gitea) !gitea! \"gitea\" \\gitea\\ 'gitea'", "~/' ${gitea} `gitea` (gitea) !gitea! \"gitea\" \\gitea\\ '\\''gitea'\\'", + }, { + "Inline command", + "some`echo foo`thing", + "\"some\\`echo foo\\`thing\"", + }, { + "Substitution", + `;${HOME}`, + `";\${HOME}"`, + }, { + "ANSI Escape codes (not escaped)", + "\033[31;1;4mHello\033[0m", + "\"\x1b[31;1;4mHello\x1b[0m\"", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := ShellEscape(tt.toEscape); got != tt.want { - t.Errorf("ShellEscape(%q):\nGot: %s\nWanted: %s", tt.toEscape, got, tt.want) - } + assert.Equal(t, tt.want, ShellEscape(tt.toEscape)) }) } } diff --git a/tests/integration/codeowner_test.go b/tests/integration/codeowner_test.go index b85a5f213d..51468ffe09 100644 --- a/tests/integration/codeowner_test.go +++ b/tests/integration/codeowner_test.go @@ -47,6 +47,7 @@ func CodeOwnerTestCommon(t *testing.T, u *url.URL, codeownerTest CodeownerTest) cloneURL, _ := url.Parse(r) cloneURL.User = url.UserPassword("user2", userPassword) require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + doGitSetRemoteURL(dstPath, "origin", cloneURL)(t) t.Run("Normal", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/git_helper_for_declarative_test.go b/tests/integration/git_helper_for_declarative_test.go index 89453296ca..5be5c6088c 100644 --- a/tests/integration/git_helper_for_declarative_test.go +++ b/tests/integration/git_helper_for_declarative_test.go @@ -105,6 +105,8 @@ func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md")) require.NoError(t, err) assert.True(t, exist) + // Set user:password + doGitSetRemoteURL(dstLocalPath, "origin", u)(t) } } @@ -117,6 +119,8 @@ func doPartialGitClone(dstLocalPath string, u *url.URL) func(*testing.T) { exist, err := util.IsExist(filepath.Join(dstLocalPath, "README.md")) require.NoError(t, err) assert.True(t, exist) + // Set user:password + doGitSetRemoteURL(dstLocalPath, "origin", u)(t) } } @@ -162,6 +166,14 @@ func doGitAddRemote(dstPath, remoteName string, u *url.URL) func(*testing.T) { } } +func doGitSetRemoteURL(dstPath, remoteName string, u *url.URL) func(*testing.T) { + return func(t *testing.T) { + t.Helper() + _, _, err := git.NewCommand(git.DefaultContext, "remote", "set-url").AddDynamicArguments(remoteName, u.String()).RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + } +} + func doGitPushTestRepository(dstPath string, args ...string) func(*testing.T) { return func(t *testing.T) { t.Helper() diff --git a/tests/integration/patch_status_test.go b/tests/integration/patch_status_test.go index 3ce1dc4cb9..4a5edccf59 100644 --- a/tests/integration/patch_status_test.go +++ b/tests/integration/patch_status_test.go @@ -78,6 +78,7 @@ func TestPatchStatus(t *testing.T) { // Clone repository. dstPath := t.TempDir() require.NoError(t, git.Clone(t.Context(), u.String(), dstPath, git.CloneRepoOptions{})) + doGitSetRemoteURL(dstPath, "origin", u)(t) // Add `fork` remote. u.Path = forkRepo.FullName() diff --git a/tests/integration/pull_review_test.go b/tests/integration/pull_review_test.go index 4af0a1100e..39e9cb7007 100644 --- a/tests/integration/pull_review_test.go +++ b/tests/integration/pull_review_test.go @@ -791,6 +791,8 @@ func TestPullRequestStaleReview(t *testing.T) { cloneURL.User = url.UserPassword("user2", userPassword) require.NoError(t, git.CloneWithArgs(t.Context(), nil, cloneURL.String(), dstPath, git.CloneRepoOptions{})) + doGitSetRemoteURL(dstPath, "origin", cloneURL)(t) + // Create first commit. require.NoError(t, os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o600)) require.NoError(t, git.AddChanges(dstPath, true)) diff --git a/tests/integration/repo_migrate_credentials_test.go b/tests/integration/repo_migrate_credentials_test.go new file mode 100644 index 0000000000..b63ca9b29e --- /dev/null +++ b/tests/integration/repo_migrate_credentials_test.go @@ -0,0 +1,95 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "net/url" + "testing" + + "forgejo.org/models/admin" + repo_model "forgejo.org/models/repo" + "forgejo.org/models/unittest" + user_model "forgejo.org/models/user" + "forgejo.org/modules/setting" + "forgejo.org/modules/structs" + "forgejo.org/modules/test" + "forgejo.org/services/migrations" + + "github.com/stretchr/testify/require" +) + +func TestRepoMigrateWithCredentials(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)() + require.NoError(t, migrations.Init()) + + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + session := loginUser(t, "user2") + + t.Run("Incorrect credentials", func(t *testing.T) { + session.MakeRequest(t, NewRequestWithValues(t, "POST", "/repo/migrate", map[string]string{ + "_csrf": GetCSRF(t, session, "/repo/migrate?service_type=1"), + "clone_addr": u.JoinPath("/user2/repo2").String(), + "auth_username": "user2", + "auth_password": userPassword + "1", + "uid": "2", + "repo_name": "migrating-with-credentials", + "service": "1", + }), http.StatusSeeOther) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "migrating-with-credentials"}, "is_empty = true") + unittest.AssertExistsAndLoadBean(t, &admin.Task{ + RepoID: repo.ID, + Type: structs.TaskTypeMigrateRepo, + Status: structs.TaskStatusFailed, + }) + }) + + t.Run("Normal", func(t *testing.T) { + session.MakeRequest(t, NewRequestWithValues(t, "POST", "/repo/migrate", map[string]string{ + "_csrf": GetCSRF(t, session, "/repo/migrate?service_type=1"), + "clone_addr": u.JoinPath("/user2/repo2").String(), + "auth_username": "user2", + "auth_password": userPassword, + "uid": "2", + "repo_name": "migrating-with-credentials-2", + "service": "1", + }), http.StatusSeeOther) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "migrating-with-credentials-2"}, "is_empty = false") + unittest.AssertExistsAndLoadBean(t, &admin.Task{ + RepoID: repo.ID, + Type: structs.TaskTypeMigrateRepo, + Status: structs.TaskStatusFinished, + }) + }) + + t.Run("Dangerous credential", func(t *testing.T) { + // Temporaily change the password + dangerousPassword := "some`echo foo`thing" + require.NoError(t, user2.SetPassword(dangerousPassword)) + require.NoError(t, user_model.UpdateUserCols(t.Context(), user2, "passwd", "passwd_hash_algo", "salt")) + + session = loginUserWithPassword(t, "user2", dangerousPassword) + + session.MakeRequest(t, NewRequestWithValues(t, "POST", "/repo/migrate", map[string]string{ + "_csrf": GetCSRF(t, session, "/repo/migrate?service_type=1"), + "clone_addr": u.JoinPath("/user2/repo2").String(), + "auth_username": "user2", + "auth_password": dangerousPassword, + "uid": "2", + "repo_name": "migrating-with-credentials-3", + "service": "1", + }), http.StatusSeeOther) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "migrating-with-credentials-3"}, "is_empty = false") + unittest.AssertExistsAndLoadBean(t, &admin.Task{ + RepoID: repo.ID, + Type: structs.TaskTypeMigrateRepo, + Status: structs.TaskStatusFinished, + }) + }) + }) +} From 5be039310c8f9f8a125c6f6517bfb6ffc654e31e Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Sat, 30 Aug 2025 18:46:31 +0200 Subject: [PATCH 73/80] [v12.0/forgejo] fix: validate CSRF on non-safe methods (#9082) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/9071 All PUT/DELETE routes in the web UI are validated to prevent a [cross site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery). Although all POST routes are validated with a CSRF token, some of the PUT/DELETE routes were missing this validation. ## Release notes - Security bug fixes - [PR](https://codeberg.org/forgejo/forgejo/pulls/9071): validate CSRF on non-safe methods Co-authored-by: Gusted Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9082 Reviewed-by: 0ko <0ko@noreply.codeberg.org> Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/web/web.go | 3 ++- tests/integration/csrf_test.go | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/routers/web/web.go b/routers/web/web.go index 6cca2a9f2e..4dff09c846 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -177,7 +177,8 @@ func verifyAuthWithOptions(options *common.VerifyOptions) func(ctx *context.Cont return } - if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" { + safeMethod := ctx.Req.Method == "GET" || ctx.Req.Method == "HEAD" || ctx.Req.Method == "OPTIONS" + if !options.SignOutRequired && !options.DisableCSRF && !safeMethod { ctx.Csrf.Validate(ctx) if ctx.Written() { return diff --git a/tests/integration/csrf_test.go b/tests/integration/csrf_test.go index 100614cbb4..f548faeda6 100644 --- a/tests/integration/csrf_test.go +++ b/tests/integration/csrf_test.go @@ -1,4 +1,5 @@ // Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2025 The Forgejo Authors. All rights reserved. // SPDX-License-Identifier: MIT package integration @@ -32,3 +33,23 @@ func TestCsrfProtection(t *testing.T) { resp = session.MakeRequest(t, req, http.StatusBadRequest) assert.Contains(t, resp.Body.String(), "Invalid CSRF token") } + +func TestCSRFSafeMethods(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + t.Run("DELETE", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user2") + resp := session.MakeRequest(t, NewRequest(t, "DELETE", "/user2/repo1/projects/1/2"), http.StatusBadRequest) + assert.Equal(t, "Invalid CSRF token.\n", resp.Body.String()) + }) + + t.Run("PUT", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user2") + resp := session.MakeRequest(t, NewRequest(t, "PUT", "/user2/repo1/projects/1/2"), http.StatusBadRequest) + assert.Equal(t, "Invalid CSRF token.\n", resp.Body.String()) + }) +} From c897e40d4f2e59e647ce99789c50584eb2db5750 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Tue, 2 Sep 2025 08:00:46 +0200 Subject: [PATCH 74/80] [v12.0/forgejo] fix(ui): clear fields when canceling adding ssh key (#9112) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8990 - Add javascript to clear fields upon clicking the cancel button inside the panel for adding new SSH keys. - Add E2E test. - Resolves #8915 Co-authored-by: dawe Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9112 Reviewed-by: Gusted Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- tests/e2e/user-settings.test.e2e.ts | 18 ++++++++++++++++++ web_src/js/features/sshkey-helper.js | 7 +++++++ web_src/js/index.js | 3 ++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/e2e/user-settings.test.e2e.ts b/tests/e2e/user-settings.test.e2e.ts index 726c7b4f4a..008548be9f 100644 --- a/tests/e2e/user-settings.test.e2e.ts +++ b/tests/e2e/user-settings.test.e2e.ts @@ -78,3 +78,21 @@ test('User: Storage overview', async ({browser}, workerInfo) => { await expect(page.getByText('Git LFS')).toBeVisible(); await save_visual(page); }); + +test('User: Canceling adding SSH key clears inputs', async ({browser}, workerInfo) => { + const page = await login({browser}, workerInfo); + await page.goto('/user/settings/keys'); + await page.locator('#add-ssh-button').click(); + + await page.getByLabel('Key name').fill('MyAwesomeKey'); + await page.locator('#ssh-key-content').fill('Wront key material'); + + await page.getByRole('button', {name: 'Cancel'}).click(); + await page.locator('#add-ssh-button').click(); + + const keyName = page.getByLabel('Key name'); + await expect(keyName).toHaveValue(''); + + const content = page.locator('#ssh-key-content'); + await expect(content).toHaveValue(''); +}); diff --git a/web_src/js/features/sshkey-helper.js b/web_src/js/features/sshkey-helper.js index 3960eefe8e..71caadd5b1 100644 --- a/web_src/js/features/sshkey-helper.js +++ b/web_src/js/features/sshkey-helper.js @@ -8,3 +8,10 @@ export function initSshKeyFormParser() { } }); } + +export function initSshKeyCancelButton() { + document.getElementById('cancel-ssh-button')?.addEventListener('click', () => { + document.getElementById('ssh-key-title').value = ''; + document.getElementById('ssh-key-content').value = ''; + }); +} diff --git a/web_src/js/index.js b/web_src/js/index.js index 1dab9ae292..6cc9eb49df 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -51,7 +51,7 @@ import {initAdminEmails} from './features/admin/emails.js'; import {initAdminCommon} from './features/admin/common.js'; import {initRepoTemplateSearch} from './features/repo-template.js'; import {initRepoCodeView} from './features/repo-code.js'; -import {initSshKeyFormParser} from './features/sshkey-helper.js'; +import {initSshKeyFormParser, initSshKeyCancelButton} from './features/sshkey-helper.js'; import {initRepoArchiveLinks} from './features/repo-common.js'; import {initRepoMigrationStatusChecker} from './features/repo-migrate.js'; import { @@ -124,6 +124,7 @@ onDomReady(() => { initMarkupAnchors(); initMarkupContent(); initSshKeyFormParser(); + initSshKeyCancelButton(); initStopwatch(); initTableSort(); initAutoFocusEnd(); From 19d920931d17705fa7d72249f3390ffb615569f8 Mon Sep 17 00:00:00 2001 From: forgejo-backport-action Date: Tue, 2 Sep 2025 10:31:10 +0200 Subject: [PATCH 75/80] [v12.0/forgejo] fix: migration failing when importing either issues or PRs but not the other (#9121) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8892 Related to https://codeberg.org/Codeberg/Community/issues/1944 ## Checklist The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org). ### Tests - I added test coverage for Go changes... - [x] in their respective `*_test.go` for unit tests. - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server. - I added test coverage for JavaScript changes... - [ ] in `web_src/js/*.test.js` if it can be unit tested. - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)). ### Documentation - [ ] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change. - A place for fix architecture is still in discussion with @fnetX - [x] I did not document these changes and I do not expect someone else to do it. ### Release notes - [ ] I do not want this change to show in the release notes. - [x] I want the title to show in the release notes with a link to this pull request. - [ ] I want the content of the `release-notes/.md` to be be used for the release notes instead of the title. ## What was done * Allowed the githubdownloaderv3 to know whether issues and, or PRs are requested to migrate * Used this information to decide to filter for "/pulls/" or "/issues" * Or not to filter at all if issues == true && prs == true * Added isolated test for the downloader and for the uploader * Created a new test_repo in github.com/forgejo and set it up properly together with @Gusted * Updated github_downloader_test with the new URLs and test data from the repo * Recorded the API calls for local testing * Added a minimal gitbucket test (which uses the github downloader under the hood) Co-authored-by: patdyn Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9121 Reviewed-by: Earl Warren Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- models/unittest/mock_http.go | 4 + services/migrations/gitbucket.go | 2 +- services/migrations/gitbucket_test.go | 24 + services/migrations/gitea_uploader_test.go | 129 ++++- services/migrations/github.go | 70 ++- services/migrations/github_test.go | 466 +++++++++--------- .../github/full_download/GET_%2Frate_limit | 35 +- .../GET_%2Frepos%2Fforgejo%2Ftest_repo | 26 + ...ssues%2F1%2Freactions%3Fpage=1&per_page=2} | 35 +- ...%3Fdirection=asc&per_page=100&sort=created | 25 + ...issues%2F2%2Freactions%3Fpage=1&per_page=2 | 26 + ...issues%2F3%2Freactions%3Fpage=1&per_page=2 | 26 + ...ssues%2F3%2Freactions%3Fpage=2&per_page=2} | 37 +- ...issues%2F7%2Freactions%3Fpage=1&per_page=2 | 26 + ...4123494%2Freactions%3Fpage=1&per_page=100} | 39 +- ...c&page=1&per_page=2&sort=created&state=all | 26 + ...2Ftest_repo%2Flabels%3Fpage=1&per_page=100 | 25 + ...milestones%3Fpage=1&per_page=100&state=all | 25 + ...%2F3%2Frequested_reviewers%3Fper_page=100} | 39 +- ...ews%2F3096999684%2Fcomments%3Fper_page=100 | 25 + ...ews%2F3097007243%2Fcomments%3Fper_page=100 | 25 + ..._repo%2Fpulls%2F3%2Freviews%3Fper_page=100 | 25 + ...repo%2Fpulls%2F4%2Freviews%3Fper_page=100} | 34 +- ...%2F7%2Frequested_reviewers%3Fper_page=100} | 37 +- ...repo%2Fpulls%2F7%2Freviews%3Fper_page=100} | 35 +- ...0216729%2Freactions%3Fpage=1&per_page=100} | 35 +- ...0221159%2Freactions%3Fpage=1&per_page=100} | 35 +- ...c&page=1&per_page=2&sort=created&state=all | 25 + ...test_repo%2Freleases%3Fpage=1&per_page=100 | 25 + .../GET_%2Frepos%2Fgo-gitea%2Ftest_repo | 25 - ...issues%2F1%2Freactions%3Fpage=1&per_page=2 | 24 - ...issues%2F1%2Freactions%3Fpage=2&per_page=2 | 26 - ...%3Fdirection=asc&per_page=100&sort=created | 24 - ...issues%2F2%2Freactions%3Fpage=1&per_page=2 | 25 - ...issues%2F2%2Freactions%3Fpage=2&per_page=2 | 25 - ...issues%2F2%2Freactions%3Fpage=3&per_page=2 | 25 - ...issues%2F4%2Freactions%3Fpage=1&per_page=2 | 24 - ...issues%2F4%2Freactions%3Fpage=2&per_page=2 | 26 - ...53111966%2Freactions%3Fpage=1&per_page=100 | 24 - ...53111966%2Freactions%3Fpage=2&per_page=100 | 26 - ...c&page=1&per_page=2&sort=created&state=all | 25 - ...2Ftest_repo%2Flabels%3Fpage=1&per_page=100 | 24 - ...milestones%3Fpage=1&per_page=100&state=all | 24 - ...iews%2F315859956%2Fcomments%3Fper_page=100 | 25 - ..._repo%2Fpulls%2F3%2Freviews%3Fper_page=100 | 24 - ...iews%2F338338740%2Fcomments%3Fper_page=100 | 24 - ...iews%2F338339651%2Fcomments%3Fper_page=100 | 25 - ...iews%2F338349019%2Fcomments%3Fper_page=100 | 24 - ..._repo%2Fpulls%2F4%2Freviews%3Fper_page=100 | 24 - ...c&page=1&per_page=2&sort=created&state=all | 24 - ...test_repo%2Freleases%3Fpage=1&per_page=100 | 24 - .../full_download/forgejo/test_repo.git/HEAD | 1 + .../forgejo/test_repo.git/config | 4 + .../forgejo/test_repo.git/description | 1 + .../forgejo/test_repo.git/info/exclude | 6 + .../forgejo/test_repo.git/info/refs | 1 + .../17/58ff42c9ab82988ad33d211ef5433afd779a9e | Bin 0 -> 884 bytes .../1a/0f6de7bb300de4a569371cfedf1c6ff189d374 | Bin 0 -> 85 bytes .../44/2d28a55b842472c95bead51a4c61f209ac1636 | Bin 0 -> 790 bytes .../56/38cb8f3278e467fc1eefcac14d3c0d5d91601f | Bin 0 -> 827 bytes .../59/93b8814449c4cc660132613c12cf6b26c9824c | Bin 0 -> 54 bytes .../6d/d0c6801ddbb7333787e73e99581279492ff449 | Bin 0 -> 827 bytes .../76/5f7381bf10911971845cc881fa54a93389a270 | Bin 0 -> 85 bytes .../7c/93d105d3ee6af50705c4d2cc8dd548c2f9edc9 | Bin 0 -> 85 bytes .../7d/c3f365f3bc83011f0df5ee8637de7dd9487c04 | 3 + .../9b/fcee0cb75322db96a76b49faa222e2c59f485d | Bin 0 -> 85 bytes .../ba/96372ae89cfd798c343542d898da029e2d8a02 | 2 + .../bf/2e0fd70c6ef1ab5240a340ba38eb47b3e8409f | Bin 0 -> 69 bytes .../ca/43b48ca2c461f9a5cb66500a154b23d07c9f90 | Bin 0 -> 901 bytes .../forgejo/test_repo.git/refs/heads/main | 1 + 70 files changed, 966 insertions(+), 980 deletions(-) create mode 100644 services/migrations/gitbucket_test.go create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2} (76%) create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=4&per_page=2 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=2&per_page=2} (61%) create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F7%2Freactions%3Fpage=1&per_page=2 rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553138856%2Freactions%3Fpage=1&per_page=100 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2Fcomments%2F3164123494%2Freactions%3Fpage=1&per_page=100} (76%) create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100} (75%) create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3096999684%2Fcomments%3Fper_page=100 create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3097007243%2Fcomments%3Fper_page=100 create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315860062%2Fcomments%3Fper_page=100 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100} (63%) rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Frequested_reviewers%3Fper_page=100 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Frequested_reviewers%3Fper_page=100} (75%) rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315861440%2Fcomments%3Fper_page=100 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Freviews%3Fper_page=100} (75%) rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363017488%2Freactions%3Fpage=1&per_page=100 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260216729%2Freactions%3Fpage=1&per_page=100} (76%) rename services/migrations/testdata/github/full_download/{GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363029944%2Freactions%3Fpage=1&per_page=100 => GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260221159%2Freactions%3Fpage=1&per_page=100} (76%) create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all create mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=2&per_page=2 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=2&per_page=2 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=3&per_page=2 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=1&per_page=2 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=2&per_page=2 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=1&per_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=2&per_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315859956%2Fcomments%3Fper_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338338740%2Fcomments%3Fper_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338339651%2Fcomments%3Fper_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338349019%2Fcomments%3Fper_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100 delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all delete mode 100644 services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/HEAD create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/config create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/description create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/exclude create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/refs create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/17/58ff42c9ab82988ad33d211ef5433afd779a9e create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/1a/0f6de7bb300de4a569371cfedf1c6ff189d374 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/44/2d28a55b842472c95bead51a4c61f209ac1636 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/56/38cb8f3278e467fc1eefcac14d3c0d5d91601f create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/59/93b8814449c4cc660132613c12cf6b26c9824c create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/6d/d0c6801ddbb7333787e73e99581279492ff449 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/76/5f7381bf10911971845cc881fa54a93389a270 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/7c/93d105d3ee6af50705c4d2cc8dd548c2f9edc9 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/7d/c3f365f3bc83011f0df5ee8637de7dd9487c04 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/9b/fcee0cb75322db96a76b49faa222e2c59f485d create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/ba/96372ae89cfd798c343542d898da029e2d8a02 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/bf/2e0fd70c6ef1ab5240a340ba38eb47b3e8409f create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/ca/43b48ca2c461f9a5cb66500a154b23d07c9f90 create mode 100644 services/migrations/testdata/github/full_download/forgejo/test_repo.git/refs/heads/main diff --git a/models/unittest/mock_http.go b/models/unittest/mock_http.go index 6064e07e9b..c2c12e55ee 100644 --- a/models/unittest/mock_http.go +++ b/models/unittest/mock_http.go @@ -41,6 +41,10 @@ func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveM log.Info("Mock HTTP Server: got request for path %s", r.URL.Path) // TODO check request method (support POST?) fixturePath := fmt.Sprintf("%s/%s_%s", testDataDir, r.Method, url.PathEscape(path)) + if strings.Contains(path, "test_repo.git") { + // We got a git clone request against our mock server + fixturePath = fmt.Sprintf("%s/%s", testDataDir, strings.TrimLeft(r.URL.Path, "/")) + } if liveMode { liveURL := fmt.Sprintf("%s%s", liveServerBaseURL, path) diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go index b68fc01083..a9bd2dafb0 100644 --- a/services/migrations/gitbucket.go +++ b/services/migrations/gitbucket.go @@ -71,7 +71,7 @@ func (g *GitBucketDownloader) LogString() string { // NewGitBucketDownloader creates a GitBucket downloader func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader { - githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName) + githubDownloader := NewGithubDownloaderV3(ctx, baseURL, true, true, userName, password, token, repoOwner, repoName) // Gitbucket 4.40 uses different internal hard-coded perPage values. // Issues, PRs, and other major parts use 25. Release page uses 10. // Some API doesn't support paging yet. Sounds difficult, but using diff --git a/services/migrations/gitbucket_test.go b/services/migrations/gitbucket_test.go new file mode 100644 index 0000000000..3cb69d65c9 --- /dev/null +++ b/services/migrations/gitbucket_test.go @@ -0,0 +1,24 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package migrations + +import ( + "os" + "testing" + + "forgejo.org/models/unittest" + + "github.com/stretchr/testify/require" +) + +func TestGitbucketDownloaderCreation(t *testing.T) { + token := os.Getenv("GITHUB_READ_TOKEN") + fixturePath := "./testdata/github/full_download" + server := unittest.NewMockWebServer(t, "https://api.github.com", fixturePath, false) + defer server.Close() + + downloader := NewGitBucketDownloader(t.Context(), server.URL, "", "", token, "forgejo", "test_repo") + err := downloader.RefreshRate() + require.NoError(t, err) +} diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go index 85e733cc51..e33d597cdc 100644 --- a/services/migrations/gitea_uploader_test.go +++ b/services/migrations/gitea_uploader_test.go @@ -19,7 +19,6 @@ import ( user_model "forgejo.org/models/user" "forgejo.org/modules/git" "forgejo.org/modules/gitrepo" - "forgejo.org/modules/graceful" "forgejo.org/modules/log" base "forgejo.org/modules/migration" "forgejo.org/modules/optional" @@ -30,6 +29,130 @@ import ( "github.com/stretchr/testify/require" ) +func TestCommentUpload(t *testing.T) { + unittest.PrepareTestEnv(t) + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + var ( + opts = base.MigrateOptions{ + Issues: true, + } + repoName = "test_repo" + uploader = NewGiteaLocalUploader(t.Context(), user, user.Name, repoName) + ) + defer uploader.Close() + + fixturePath := "./testdata/github/full_download" + server := unittest.NewMockWebServer(t, "https://api.github.com", fixturePath, false) + defer server.Close() + + // Mock Data + repoMock := &base.Repository{ + Name: repoName, + Owner: "forgejo", + Description: "Some mock repo", + CloneURL: server.URL + "/forgejo/test_repo.git", + OriginalURL: server.URL + "/forgejo/test_repo", + DefaultBranch: "master", + Website: "https://codeberg.org/forgejo/forgejo/", + } + + // Create Repo + require.NoError(t, uploader.CreateRepo(repoMock, opts)) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName}) + + // Create and Test Issues Uploading + issueA := &base.Issue{ + Title: "First issue", + Number: 0, + PosterID: 37243484, + PosterName: "PatDyn", + PosterEmail: "", + Content: "Mock Content", + Milestone: "Mock Milestone", + State: "open", + Created: time.Date(2025, 8, 7, 12, 44, 7, 0, time.UTC), + Updated: time.Date(2025, 8, 7, 12, 44, 47, 0, time.UTC), + Labels: nil, + Reactions: nil, + Closed: nil, + IsLocked: false, + Assignees: nil, + ForeignIndex: 0, + } + + issueB := &base.Issue{ + Title: "Second Issue", + Number: 1, + PosterID: 37243484, + PosterName: "PatDyn", + PosterEmail: "", + Content: "Mock Content", + Milestone: "Mock Milestone", + State: "open", + Created: time.Date(2025, 8, 7, 12, 45, 44, 0, time.UTC), + Updated: time.Date(2025, 8, 7, 13, 7, 25, 0, time.UTC), + Labels: nil, + Reactions: nil, + Closed: nil, + IsLocked: false, + Assignees: nil, + ForeignIndex: 1, + } + + err := uploader.CreateIssues(issueA, issueB) + require.NoError(t, err) + + issues, err := issues_model.Issues(db.DefaultContext, &issues_model.IssuesOptions{ + RepoIDs: []int64{repo.ID}, + IsPull: optional.Some(false), + SortType: "newest", + }) + require.NoError(t, err) + assert.Len(t, issues, 2) + + // Create and Test Comment Uploading + issueAComment := &base.Comment{ + IssueIndex: 0, + Index: 0, + CommentType: "comment", + PosterID: 37243484, + PosterName: "PatDyn", + PosterEmail: "", + Created: time.Date(2025, 8, 7, 12, 44, 24, 0, time.UTC), + Updated: time.Date(2025, 8, 7, 12, 44, 24, 0, time.UTC), + Content: "First Mock Comment", + Reactions: nil, + Meta: nil, + } + issueBComment := &base.Comment{ + IssueIndex: 1, + Index: 1, + CommentType: "comment", + PosterID: 37243484, + PosterName: "PatDyn", + PosterEmail: "", + Created: time.Date(2025, 8, 7, 13, 7, 25, 0, time.UTC), + Updated: time.Date(2025, 8, 7, 13, 7, 25, 0, time.UTC), + Content: "Second Mock Comment", + Reactions: nil, + Meta: nil, + } + require.NoError(t, uploader.CreateComments(issueBComment, issueAComment)) + + issues, err = issues_model.Issues(db.DefaultContext, &issues_model.IssuesOptions{ + RepoIDs: []int64{repo.ID}, + IsPull: optional.Some(false), + SortType: "newest", + }) + require.NoError(t, err) + assert.Len(t, issues, 2) + require.NoError(t, issues[0].LoadDiscussComments(db.DefaultContext)) + require.NoError(t, issues[1].LoadDiscussComments(db.DefaultContext)) + assert.Len(t, issues[0].Comments, 1) + assert.Len(t, issues[1].Comments, 1) +} + func TestGiteaUploadRepo(t *testing.T) { // FIXME: Since no accesskey or user/password will trigger rate limit of github, just skip t.Skip() @@ -40,9 +163,9 @@ func TestGiteaUploadRepo(t *testing.T) { var ( ctx = t.Context() - downloader = NewGithubDownloaderV3(ctx, "https://github.com", "", "", "", "go-xorm", "builder") + downloader = NewGithubDownloaderV3(ctx, "https://github.com", true, true, "", "", "", "go-xorm", "builder") repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05") - uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName) + uploader = NewGiteaLocalUploader(t.Context(), user, user.Name, repoName) ) err := migrateRepository(db.DefaultContext, user, downloader, uploader, base.MigrateOptions{ diff --git a/services/migrations/github.go b/services/migrations/github.go index 317eb568b5..1fe5d2cc8e 100644 --- a/services/migrations/github.go +++ b/services/migrations/github.go @@ -57,7 +57,7 @@ func (f *GithubDownloaderV3Factory) New(ctx context.Context, opts base.MigrateOp log.Trace("Create github downloader BaseURL: %s %s/%s", baseURL, oldOwner, oldName) - return NewGithubDownloaderV3(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil + return NewGithubDownloaderV3(ctx, baseURL, opts.PullRequests, opts.Issues, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil } // GitServiceType returns the type of git service @@ -69,30 +69,34 @@ func (f *GithubDownloaderV3Factory) GitServiceType() structs.GitServiceType { // from github via APIv3 type GithubDownloaderV3 struct { base.NullDownloader - ctx context.Context - clients []*github.Client - baseURL string - repoOwner string - repoName string - userName string - password string - rates []*github.Rate - curClientIdx int - maxPerPage int - SkipReactions bool - SkipReviews bool + ctx context.Context + clients []*github.Client + baseURL string + repoOwner string + repoName string + userName string + password string + getPullRequests bool + getIssues bool + rates []*github.Rate + curClientIdx int + maxPerPage int + SkipReactions bool + SkipReviews bool } // NewGithubDownloaderV3 creates a github Downloader via github v3 API -func NewGithubDownloaderV3(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 { +func NewGithubDownloaderV3(ctx context.Context, baseURL string, getPullRequests, getIssues bool, userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 { downloader := GithubDownloaderV3{ - userName: userName, - baseURL: baseURL, - password: password, - ctx: ctx, - repoOwner: repoOwner, - repoName: repoName, - maxPerPage: 100, + userName: userName, + baseURL: baseURL, + password: password, + ctx: ctx, + repoOwner: repoOwner, + repoName: repoName, + maxPerPage: 100, + getPullRequests: getPullRequests, + getIssues: getIssues, } if token != "" { @@ -582,6 +586,24 @@ func (g *GithubDownloaderV3) getComments(commentable base.Commentable) ([]*base. return allComments, nil } +func (g *GithubDownloaderV3) filterByHTMLURL(comments []*github.IssueComment, filterBy string) []*github.IssueComment { + var result []*github.IssueComment + for _, val := range comments { + if !strings.Contains(*val.HTMLURL, filterBy) { + result = append(result, val) + } + } + return result +} + +func (g *GithubDownloaderV3) filterPRComments(comments []*github.IssueComment) []*github.IssueComment { + return g.filterByHTMLURL(comments, "/pull/") +} + +func (g *GithubDownloaderV3) filterIssueComments(comments []*github.IssueComment) []*github.IssueComment { + return g.filterByHTMLURL(comments, "/issues/") +} + // GetAllComments returns repository comments according page and perPageSize func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment, bool, error) { var ( @@ -608,6 +630,12 @@ func (g *GithubDownloaderV3) GetAllComments(page, perPage int) ([]*base.Comment, } isEnd := resp.NextPage == 0 + if g.getIssues && !g.getPullRequests { + comments = g.filterPRComments(comments) + } else if !g.getIssues && g.getPullRequests { + comments = g.filterIssueComments(comments) + } + log.Trace("Request get comments %d/%d, but in fact get %d, next page is %d", perPage, page, len(comments), resp.NextPage) g.setRate(&resp.Rate) for _, comment := range comments { diff --git a/services/migrations/github_test.go b/services/migrations/github_test.go index c5e24ebbcd..ef2850d2d2 100644 --- a/services/migrations/github_test.go +++ b/services/migrations/github_test.go @@ -12,19 +12,100 @@ import ( "forgejo.org/models/unittest" base "forgejo.org/modules/migration" + "github.com/google/go-github/v64/github" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +func TestGithubDownloaderFilterComments(t *testing.T) { + GithubLimitRateRemaining = 3 // Wait at 3 remaining since we could have 3 CI in // + + token := os.Getenv("GITHUB_READ_TOKEN") + fixturePath := "./testdata/github/full_download" + server := unittest.NewMockWebServer(t, "https://api.github.com", fixturePath, false) + defer server.Close() + + downloader := NewGithubDownloaderV3(t.Context(), server.URL, true, true, "", "", token, "forgejo", "test_repo") + err := downloader.RefreshRate() + require.NoError(t, err) + + var githubComments []*github.IssueComment + issueID := int64(7) + iNodeID := "MDEyOklzc3VlQ29tbWVudDE=" // "IssueComment1" + iBody := "Hello" + iCreated := new(github.Timestamp) + iUpdated := new(github.Timestamp) + iCreated.Time = time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC) + iUpdated.Time = time.Date(2025, 1, 1, 12, 1, 0, 0, time.UTC) + iAssociation := "COLLABORATOR" + iURL := "https://api.github.com/repos/forgejo/test_repo/issues/comments/3164032267" + iHTMLURL := "https://github.com/forgejo/test_repo/issues/1#issuecomment-3164032267" + iIssueURL := "https://api.github.com/repos/forgejo/test_repo/issues/1" + + githubComments = append(githubComments, + &github.IssueComment{ + ID: &issueID, + NodeID: &iNodeID, + Body: &iBody, + Reactions: nil, + CreatedAt: iCreated, + UpdatedAt: iUpdated, + AuthorAssociation: &iAssociation, + URL: &iURL, + HTMLURL: &iHTMLURL, + IssueURL: &iIssueURL, + }, + ) + + prID := int64(4) + pNodeID := "IC_kwDOPQx9Mc65LHhx" + pBody := "Hello" + pCreated := new(github.Timestamp) + pUpdated := new(github.Timestamp) + pCreated.Time = time.Date(2025, 1, 1, 11, 0, 0, 0, time.UTC) + pUpdated.Time = time.Date(2025, 1, 1, 11, 1, 0, 0, time.UTC) + pAssociation := "COLLABORATOR" + pURL := "https://api.github.com/repos/forgejo/test_repo/issues/comments/3164118916" + pHTMLURL := "https://github.com/forgejo/test_repo/pull/3#issuecomment-3164118916" + pIssueURL := "https://api.github.com/repos/forgejo/test_repo/issues/3" + + githubComments = append(githubComments, &github.IssueComment{ + ID: &prID, + NodeID: &pNodeID, + Body: &pBody, + Reactions: nil, + CreatedAt: pCreated, + UpdatedAt: pUpdated, + AuthorAssociation: &pAssociation, + URL: &pURL, + HTMLURL: &pHTMLURL, + IssueURL: &pIssueURL, + }) + + filteredComments := downloader.filterPRComments(githubComments) + + // Check each issue index not being from the PR + for _, comment := range filteredComments { + assert.NotEqual(t, *comment.ID, prID) + } + + filteredComments = downloader.filterIssueComments(githubComments) + + // Check each issue index not being from the issue + for _, comment := range filteredComments { + assert.NotEqual(t, *comment.ID, issueID) + } +} + func TestGitHubDownloadRepo(t *testing.T) { GithubLimitRateRemaining = 3 // Wait at 3 remaining since we could have 3 CI in // token := os.Getenv("GITHUB_READ_TOKEN") fixturePath := "./testdata/github/full_download" - server := unittest.NewMockWebServer(t, "https://api.github.com", fixturePath, token != "") + server := unittest.NewMockWebServer(t, "https://api.github.com", fixturePath, false) defer server.Close() - downloader := NewGithubDownloaderV3(t.Context(), server.URL, "", "", token, "go-gitea", "test_repo") + downloader := NewGithubDownloaderV3(t.Context(), server.URL, true, true, "", "", token, "forgejo", "test_repo") err := downloader.RefreshRate() require.NoError(t, err) @@ -32,38 +113,44 @@ func TestGitHubDownloadRepo(t *testing.T) { require.NoError(t, err) assertRepositoryEqual(t, &base.Repository{ Name: "test_repo", - Owner: "go-gitea", - Description: "Test repository for testing migration from github to gitea", - CloneURL: server.URL + "/go-gitea/test_repo.git", - OriginalURL: server.URL + "/go-gitea/test_repo", - DefaultBranch: "master", + Owner: "forgejo", + Description: "Exclusively used for testing Github->Forgejo migration", + CloneURL: server.URL + "/forgejo/test_repo.git", + OriginalURL: server.URL + "/forgejo/test_repo", + DefaultBranch: "main", Website: "https://codeberg.org/forgejo/forgejo/", }, repo) topics, err := downloader.GetTopics() require.NoError(t, err) - assert.Contains(t, topics, "gitea") + assert.Contains(t, topics, "forgejo") milestones, err := downloader.GetMilestones() require.NoError(t, err) assertMilestonesEqual(t, []*base.Milestone{ { Title: "1.0.0", - Description: "Milestone 1.0.0", - Deadline: timePtr(time.Date(2019, 11, 11, 8, 0, 0, 0, time.UTC)), - Created: time.Date(2019, 11, 12, 19, 37, 8, 0, time.UTC), - Updated: timePtr(time.Date(2019, 11, 12, 21, 56, 17, 0, time.UTC)), - Closed: timePtr(time.Date(2019, 11, 12, 19, 45, 49, 0, time.UTC)), + Description: "Version 1", + Created: time.Date(2025, 8, 7, 12, 48, 56, 0, time.UTC), + Updated: timePtr(time.Date(2025, time.August, 12, 12, 34, 20, 0, time.UTC)), + State: "open", + }, + { + Title: "0.9.0", + Description: "A milestone", + Deadline: timePtr(time.Date(2025, 8, 1, 7, 0, 0, 0, time.UTC)), + Created: time.Date(2025, 8, 7, 12, 54, 20, 0, time.UTC), + Updated: timePtr(time.Date(2025, 8, 12, 11, 29, 52, 0, time.UTC)), + Closed: timePtr(time.Date(2025, 8, 7, 12, 54, 38, 0, time.UTC)), State: "closed", }, { Title: "1.1.0", - Description: "Milestone 1.1.0", - Deadline: timePtr(time.Date(2019, 11, 12, 8, 0, 0, 0, time.UTC)), - Created: time.Date(2019, 11, 12, 19, 37, 25, 0, time.UTC), - Updated: timePtr(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), - Closed: timePtr(time.Date(2019, 11, 12, 19, 45, 46, 0, time.UTC)), - State: "closed", + Description: "We can do that", + Deadline: timePtr(time.Date(2025, 8, 31, 7, 0, 0, 0, time.UTC)), + Created: time.Date(2025, 8, 7, 12, 50, 58, 0, time.UTC), + Updated: timePtr(time.Date(2025, 8, 7, 12, 53, 15, 0, time.UTC)), + State: "open", }, }, milestones) @@ -117,18 +204,34 @@ func TestGitHubDownloadRepo(t *testing.T) { }, }, labels) + id := int64(280443629) + ct := "application/pdf" + size := 550175 + dc := 0 + releases, err := downloader.GetReleases() require.NoError(t, err) assertReleasesEqual(t, []*base.Release{ { - TagName: "v0.9.99", - TargetCommitish: "master", + TagName: "v1.0", + TargetCommitish: "main", Name: "First Release", - Body: "A test release", - Created: time.Date(2019, 11, 9, 16, 49, 21, 0, time.UTC), - Published: time.Date(2019, 11, 12, 20, 12, 10, 0, time.UTC), - PublisherID: 1669571, - PublisherName: "mrsdizzie", + Body: "Hi, this is the first release! The asset contains the wireguard whitepaper, amazing read for such a simple protocol.", + Created: time.Date(2025, time.August, 7, 13, 2, 19, 0, time.UTC), + Published: time.Date(2025, time.August, 7, 13, 7, 49, 0, time.UTC), + PublisherID: 25481501, + PublisherName: "Gusted", + Assets: []*base.ReleaseAsset{ + { + ID: id, + Name: "wireguard.pdf", + ContentType: &ct, + Size: &size, + DownloadCount: &dc, + Created: time.Date(2025, time.August, 7, 23, 39, 27, 0, time.UTC), + Updated: time.Date(2025, time.August, 7, 23, 39, 29, 0, time.UTC), + }, + }, }, }, releases) @@ -139,85 +242,41 @@ func TestGitHubDownloadRepo(t *testing.T) { assertIssuesEqual(t, []*base.Issue{ { Number: 1, - Title: "Please add an animated gif icon to the merge button", - Content: "I just want the merge button to hurt my eyes a little. \xF0\x9F\x98\x9D ", - Milestone: "1.0.0", - PosterID: 18600385, - PosterName: "guillep2k", - State: "closed", - Created: time.Date(2019, 11, 9, 17, 0, 29, 0, time.UTC), - Updated: time.Date(2019, 11, 12, 20, 29, 53, 0, time.UTC), - Labels: []*base.Label{ - { - Name: "bug", - Color: "d73a4a", - Description: "Something isn't working", - }, - { - Name: "good first issue", - Color: "7057ff", - Description: "Good for newcomers", - }, - }, - Reactions: []*base.Reaction{ - { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "+1", - }, - }, - Closed: timePtr(time.Date(2019, 11, 12, 20, 22, 22, 0, time.UTC)), + Title: "First issue", + Content: "This is an issue.", + PosterID: 37243484, + PosterName: "PatDyn", + State: "open", + Created: time.Date(2025, time.August, 7, 12, 44, 7, 0, time.UTC), + Updated: time.Date(2025, time.August, 7, 12, 44, 47, 0, time.UTC), }, { Number: 2, - Title: "Test issue", - Content: "This is test issue 2, do not touch!", + Title: "Second Issue", + Content: "Mentioning #1 ", Milestone: "1.1.0", - PosterID: 1669571, - PosterName: "mrsdizzie", - State: "closed", - Created: time.Date(2019, 11, 12, 21, 0, 6, 0, time.UTC), - Updated: time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), + PosterID: 37243484, + PosterName: "PatDyn", + State: "open", + Created: time.Date(2025, 8, 7, 12, 45, 44, 0, time.UTC), + Updated: time.Date(2025, 8, 7, 13, 7, 25, 0, time.UTC), Labels: []*base.Label{ { Name: "duplicate", Color: "cfd3d7", Description: "This issue or pull request already exists", }, - }, - Reactions: []*base.Reaction{ { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "heart", + Name: "good first issue", + Color: "7057ff", + Description: "Good for newcomers", }, { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "laugh", - }, - { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "-1", - }, - { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "confused", - }, - { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "hooray", - }, - { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "+1", + Name: "help wanted", + Color: "008672", + Description: "Extra attention is needed", }, }, - Closed: timePtr(time.Date(2019, 11, 12, 21, 1, 31, 0, time.UTC)), }, }, issues) @@ -227,26 +286,11 @@ func TestGitHubDownloadRepo(t *testing.T) { assertCommentsEqual(t, []*base.Comment{ { IssueIndex: 2, - PosterID: 1669571, - PosterName: "mrsdizzie", - Created: time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), - Updated: time.Date(2019, 11, 12, 21, 0, 13, 0, time.UTC), - Content: "This is a comment", - Reactions: []*base.Reaction{ - { - UserID: 1669571, - UserName: "mrsdizzie", - Content: "+1", - }, - }, - }, - { - IssueIndex: 2, - PosterID: 1669571, - PosterName: "mrsdizzie", - Created: time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), - Updated: time.Date(2019, 11, 12, 22, 7, 14, 0, time.UTC), - Content: "A second comment", + PosterID: 37243484, + PosterName: "PatDyn", + Created: time.Date(2025, time.August, 7, 13, 7, 25, 0, time.UTC), + Updated: time.Date(2025, time.August, 7, 13, 7, 25, 0, time.UTC), + Content: "Mentioning #3 \nWith some **bold** *statement*", Reactions: nil, }, }, comments) @@ -257,52 +301,50 @@ func TestGitHubDownloadRepo(t *testing.T) { assertPullRequestsEqual(t, []*base.PullRequest{ { Number: 3, - Title: "Update README.md", - Content: "add warning to readme", - Milestone: "1.1.0", - PosterID: 1669571, - PosterName: "mrsdizzie", - State: "closed", - Created: time.Date(2019, 11, 12, 21, 21, 43, 0, time.UTC), - Updated: time.Date(2019, 11, 12, 21, 39, 28, 0, time.UTC), + Title: "Update readme.md", + Content: "Added a feature description", + Milestone: "1.0.0", + PosterID: 37243484, + PosterName: "PatDyn", + State: "open", + Created: time.Date(2025, time.August, 7, 12, 47, 6, 0, time.UTC), + Updated: time.Date(2025, time.August, 12, 13, 16, 49, 0, time.UTC), Labels: []*base.Label{ { - Name: "documentation", - Color: "0075ca", - Description: "Improvements or additions to documentation", + Name: "enhancement", + Color: "a2eeef", + Description: "New feature or request", }, }, - PatchURL: server.URL + "/go-gitea/test_repo/pull/3.patch", + PatchURL: server.URL + "/forgejo/test_repo/pull/3.patch", Head: base.PullRequestBranch{ - Ref: "master", - CloneURL: server.URL + "/mrsdizzie/test_repo.git", - SHA: "076160cf0b039f13e5eff19619932d181269414b", + Ref: "some-feature", + CloneURL: server.URL + "/forgejo/test_repo.git", + SHA: "c608ab3997349219e1510cdb5ddd1e5e82897dfa", RepoName: "test_repo", - OwnerName: "mrsdizzie", + OwnerName: "forgejo", }, Base: base.PullRequestBranch{ - Ref: "master", - SHA: "72866af952e98d02a73003501836074b286a78f6", - OwnerName: "go-gitea", + Ref: "main", + SHA: "442d28a55b842472c95bead51a4c61f209ac1636", + OwnerName: "forgejo", RepoName: "test_repo", }, - Closed: timePtr(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), - Merged: true, - MergedTime: timePtr(time.Date(2019, 11, 12, 21, 39, 27, 0, time.UTC)), - MergeCommitSHA: "f32b0a9dfd09a60f616f29158f772cedd89942d2", - ForeignIndex: 3, + ForeignIndex: 3, }, { - Number: 4, - Title: "Test branch", - Content: "do not merge this PR", + Number: 7, + Title: "Update readme.md", + Content: "Adding some text to the readme", Milestone: "1.0.0", - PosterID: 1669571, - PosterName: "mrsdizzie", - State: "open", - Created: time.Date(2019, 11, 12, 21, 54, 18, 0, time.UTC), - Updated: time.Date(2020, 1, 4, 11, 30, 1, 0, time.UTC), + PosterID: 37243484, + PosterName: "PatDyn", + State: "closed", + Created: time.Date(2025, time.August, 7, 13, 1, 36, 0, time.UTC), + Updated: time.Date(2025, time.August, 12, 12, 47, 35, 0, time.UTC), + Closed: timePtr(time.Date(2025, time.August, 7, 13, 2, 19, 0, time.UTC)), + MergedTime: timePtr(time.Date(2025, time.August, 7, 13, 2, 19, 0, time.UTC)), Labels: []*base.Label{ { Name: "bug", @@ -310,35 +352,23 @@ func TestGitHubDownloadRepo(t *testing.T) { Description: "Something isn't working", }, }, - PatchURL: server.URL + "/go-gitea/test_repo/pull/4.patch", + PatchURL: server.URL + "/forgejo/test_repo/pull/7.patch", Head: base.PullRequestBranch{ - Ref: "test-branch", - SHA: "2be9101c543658591222acbee3eb799edfc3853d", + Ref: "another-feature", + SHA: "5638cb8f3278e467fc1eefcac14d3c0d5d91601f", RepoName: "test_repo", - OwnerName: "mrsdizzie", - CloneURL: server.URL + "/mrsdizzie/test_repo.git", + OwnerName: "forgejo", + CloneURL: server.URL + "/forgejo/test_repo.git", }, Base: base.PullRequestBranch{ - Ref: "master", - SHA: "f32b0a9dfd09a60f616f29158f772cedd89942d2", - OwnerName: "go-gitea", + Ref: "main", + SHA: "6dd0c6801ddbb7333787e73e99581279492ff449", + OwnerName: "forgejo", RepoName: "test_repo", }, - Merged: false, - MergeCommitSHA: "565d1208f5fffdc1c5ae1a2436491eb9a5e4ebae", - Reactions: []*base.Reaction{ - { - UserID: 81045, - UserName: "lunny", - Content: "heart", - }, - { - UserID: 81045, - UserName: "lunny", - Content: "+1", - }, - }, - ForeignIndex: 4, + Merged: true, + MergeCommitSHA: "ca43b48ca2c461f9a5cb66500a154b23d07c9f90", + ForeignIndex: 7, }, }, prs) @@ -346,88 +376,50 @@ func TestGitHubDownloadRepo(t *testing.T) { require.NoError(t, err) assertReviewsEqual(t, []*base.Review{ { - ID: 315859956, + ID: 3096999684, IssueIndex: 3, - ReviewerID: 42128690, - ReviewerName: "jolheiser", - CommitID: "076160cf0b039f13e5eff19619932d181269414b", - CreatedAt: time.Date(2019, 11, 12, 21, 35, 24, 0, time.UTC), - State: base.ReviewStateApproved, - }, - { - ID: 315860062, - IssueIndex: 3, - ReviewerID: 1824502, - ReviewerName: "zeripath", - CommitID: "076160cf0b039f13e5eff19619932d181269414b", - CreatedAt: time.Date(2019, 11, 12, 21, 35, 36, 0, time.UTC), - State: base.ReviewStateApproved, - }, - { - ID: 315861440, - IssueIndex: 3, - ReviewerID: 165205, - ReviewerName: "lafriks", - CommitID: "076160cf0b039f13e5eff19619932d181269414b", - CreatedAt: time.Date(2019, 11, 12, 21, 38, 0, 0, time.UTC), - State: base.ReviewStateApproved, - }, - }, reviews) - - reviews, err = downloader.GetReviews(&base.PullRequest{Number: 4, ForeignIndex: 4}) - require.NoError(t, err) - assertReviewsEqual(t, []*base.Review{ - { - ID: 338338740, - IssueIndex: 4, - ReviewerID: 81045, - ReviewerName: "lunny", - CommitID: "2be9101c543658591222acbee3eb799edfc3853d", - CreatedAt: time.Date(2020, 1, 4, 5, 33, 18, 0, time.UTC), - State: base.ReviewStateApproved, + ReviewerID: 37243484, + ReviewerName: "PatDyn", + CommitID: "c608ab3997349219e1510cdb5ddd1e5e82897dfa", + CreatedAt: time.Date(2025, 8, 7, 12, 47, 55, 0, time.UTC), + State: base.ReviewStateCommented, Comments: []*base.ReviewComment{ { - ID: 363017488, - Content: "This is a good pull request.", - TreePath: "README.md", - DiffHunk: "@@ -1,2 +1,4 @@\n # test_repo\n Test repository for testing migration from github to gitea\n+", - Position: 3, - CommitID: "2be9101c543658591222acbee3eb799edfc3853d", - PosterID: 81045, - CreatedAt: time.Date(2020, 1, 4, 5, 33, 6, 0, time.UTC), - UpdatedAt: time.Date(2020, 1, 4, 5, 33, 18, 0, time.UTC), + ID: 2260216729, + InReplyTo: 0, + Content: "May want to write more", + TreePath: "readme.md", + DiffHunk: "@@ -1,3 +1,5 @@\n # Forgejo Test Repo\n \n This repo is used to test migrations\n+\n+Add some feature description.", + Position: 5, + Line: 0, + CommitID: "c608ab3997349219e1510cdb5ddd1e5e82897dfa", + PosterID: 37243484, + CreatedAt: time.Date(2025, 8, 7, 12, 47, 50, 0, time.UTC), + UpdatedAt: time.Date(2025, 8, 7, 12, 47, 55, 0, time.UTC), }, }, }, { - ID: 338339651, - IssueIndex: 4, - ReviewerID: 81045, - ReviewerName: "lunny", - CommitID: "2be9101c543658591222acbee3eb799edfc3853d", - CreatedAt: time.Date(2020, 1, 4, 6, 7, 6, 0, time.UTC), - State: base.ReviewStateChangesRequested, - Content: "Don't add more reviews", - }, - { - ID: 338349019, - IssueIndex: 4, - ReviewerID: 81045, - ReviewerName: "lunny", - CommitID: "2be9101c543658591222acbee3eb799edfc3853d", - CreatedAt: time.Date(2020, 1, 4, 11, 21, 41, 0, time.UTC), + ID: 3097007243, + IssueIndex: 3, + ReviewerID: 37243484, + ReviewerName: "PatDyn", + CommitID: "c608ab3997349219e1510cdb5ddd1e5e82897dfa", + CreatedAt: time.Date(2025, 8, 7, 12, 49, 36, 0, time.UTC), State: base.ReviewStateCommented, Comments: []*base.ReviewComment{ { - ID: 363029944, - Content: "test a single comment.", - TreePath: "LICENSE", - DiffHunk: "@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n+", - Position: 4, - CommitID: "2be9101c543658591222acbee3eb799edfc3853d", - PosterID: 81045, - CreatedAt: time.Date(2020, 1, 4, 11, 21, 41, 0, time.UTC), - UpdatedAt: time.Date(2020, 1, 4, 11, 21, 41, 0, time.UTC), + ID: 2260221159, + InReplyTo: 0, + Content: "Comment", + TreePath: "readme.md", + DiffHunk: "@@ -1,3 +1,5 @@\n # Forgejo Test Repo\n \n This repo is used to test migrations", + Position: 3, + Line: 0, + CommitID: "c608ab3997349219e1510cdb5ddd1e5e82897dfa", + PosterID: 37243484, + CreatedAt: time.Date(2025, 8, 7, 12, 49, 36, 0, time.UTC), + UpdatedAt: time.Date(2025, 8, 7, 12, 49, 36, 0, time.UTC), }, }, }, diff --git a/services/migrations/testdata/github/full_download/GET_%2Frate_limit b/services/migrations/testdata/github/full_download/GET_%2Frate_limit index 74e43a0765..f3a1c10f1d 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frate_limit +++ b/services/migrations/testdata/github/full_download/GET_%2Frate_limit @@ -1,23 +1,24 @@ Cache-Control: no-cache +X-Ratelimit-Used: 136 +X-Ratelimit-Resource: core Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Ratelimit-Reset: 1730800941 -Access-Control-Allow-Origin: * -Content-Type: application/json; charset=utf-8 -X-Oauth-Scopes: -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Remaining: 4899 -X-Xss-Protection: 0 -Content-Security-Policy: default-src 'none' -X-Accepted-Oauth-Scopes: -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset X-Frame-Options: deny -X-Content-Type-Options: nosniff -Vary: Accept-Encoding, Accept, X-Requested-With -X-Github-Request-Id: C7CC:3118FC:3F6234D:4038C5B:6729E6C0 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Github-Request-Id: E80A:118F3A:208966:1EA2B8:689B4023 X-Github-Api-Version-Selected: 2022-11-28 X-Ratelimit-Limit: 5000 -X-Ratelimit-Used: 101 -X-Ratelimit-Resource: core +Vary: Accept-Encoding, Accept, X-Requested-With +Content-Type: application/json; charset=utf-8 +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Remaining: 4864 +X-Ratelimit-Reset: 1755007969 +Access-Control-Allow-Origin: * +X-Content-Type-Options: nosniff +X-Github-Media-Type: github.v3; format=json +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Xss-Protection: 0 +Content-Security-Policy: default-src 'none' -{"resources":{"core":{"limit":5000,"used":101,"remaining":4899,"reset":1730800941},"search":{"limit":30,"used":0,"remaining":30,"reset":1730799356},"graphql":{"limit":5000,"used":162,"remaining":4838,"reset":1730801064},"integration_manifest":{"limit":5000,"used":0,"remaining":5000,"reset":1730802896},"source_import":{"limit":100,"used":0,"remaining":100,"reset":1730799356},"code_scanning_upload":{"limit":1000,"used":0,"remaining":1000,"reset":1730802896},"actions_runner_registration":{"limit":10000,"used":0,"remaining":10000,"reset":1730802896},"scim":{"limit":15000,"used":0,"remaining":15000,"reset":1730802896},"dependency_snapshots":{"limit":100,"used":0,"remaining":100,"reset":1730799356},"audit_log":{"limit":1750,"used":0,"remaining":1750,"reset":1730802896},"audit_log_streaming":{"limit":15,"used":0,"remaining":15,"reset":1730802896},"code_search":{"limit":10,"used":0,"remaining":10,"reset":1730799356}},"rate":{"limit":5000,"used":101,"remaining":4899,"reset":1730800941}} \ No newline at end of file +{"resources":{"core":{"limit":5000,"used":136,"remaining":4864,"reset":1755007969},"search":{"limit":30,"used":0,"remaining":30,"reset":1755005023},"graphql":{"limit":5000,"used":0,"remaining":5000,"reset":1755008563},"integration_manifest":{"limit":5000,"used":0,"remaining":5000,"reset":1755008563},"source_import":{"limit":100,"used":0,"remaining":100,"reset":1755005023},"code_scanning_upload":{"limit":5000,"used":136,"remaining":4864,"reset":1755007969},"code_scanning_autofix":{"limit":10,"used":0,"remaining":10,"reset":1755005023},"actions_runner_registration":{"limit":10000,"used":0,"remaining":10000,"reset":1755008563},"scim":{"limit":15000,"used":0,"remaining":15000,"reset":1755008563},"dependency_snapshots":{"limit":100,"used":0,"remaining":100,"reset":1755005023},"dependency_sbom":{"limit":100,"used":0,"remaining":100,"reset":1755005023},"audit_log":{"limit":1750,"used":0,"remaining":1750,"reset":1755008563},"audit_log_streaming":{"limit":15,"used":0,"remaining":15,"reset":1755008563},"code_search":{"limit":10,"used":0,"remaining":10,"reset":1755005023}},"rate":{"limit":5000,"used":136,"remaining":4864,"reset":1755007969}} \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo new file mode 100644 index 0000000000..57e87a4775 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo @@ -0,0 +1,26 @@ +X-Ratelimit-Remaining: 4880 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Content-Type: application/json; charset=utf-8 +Etag: W/"86b7478cb9d7f78696810f3292315b35ab47125e71dd2315dfd41626383fc081" +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Resource: core +Access-Control-Allow-Origin: * +X-Content-Type-Options: nosniff +Content-Security-Policy: default-src 'none' +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Accepted-Oauth-Scopes: repo +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Used: 120 +X-Frame-Options: deny +Last-Modified: Tue, 12 Aug 2025 11:21:45 GMT +X-Oauth-Scopes: public_repo, repo:status +X-Xss-Protection: 0 +X-Github-Request-Id: E80A:118F3A:20561B:1E7279:689B401B +Cache-Control: private, max-age=60, s-maxage=60 +X-Github-Media-Type: github.v3; param=scarlet-witch-preview; format=json, github.mercy-preview; param=baptiste-preview.nebula-preview; format=json +X-Ratelimit-Limit: 5000 + +{"id":1033841924,"node_id":"R_kgDOPZ8tBA","name":"test_repo","full_name":"forgejo/test_repo","private":false,"owner":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/forgejo/test_repo","description":"Exclusively used for testing Github->Forgejo migration","fork":false,"url":"https://api.github.com/repos/forgejo/test_repo","forks_url":"https://api.github.com/repos/forgejo/test_repo/forks","keys_url":"https://api.github.com/repos/forgejo/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/forgejo/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/forgejo/test_repo/teams","hooks_url":"https://api.github.com/repos/forgejo/test_repo/hooks","issue_events_url":"https://api.github.com/repos/forgejo/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/forgejo/test_repo/events","assignees_url":"https://api.github.com/repos/forgejo/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/forgejo/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/forgejo/test_repo/tags","blobs_url":"https://api.github.com/repos/forgejo/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/forgejo/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/forgejo/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/forgejo/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/forgejo/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/forgejo/test_repo/languages","stargazers_url":"https://api.github.com/repos/forgejo/test_repo/stargazers","contributors_url":"https://api.github.com/repos/forgejo/test_repo/contributors","subscribers_url":"https://api.github.com/repos/forgejo/test_repo/subscribers","subscription_url":"https://api.github.com/repos/forgejo/test_repo/subscription","commits_url":"https://api.github.com/repos/forgejo/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/forgejo/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/forgejo/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/forgejo/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/forgejo/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/forgejo/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/forgejo/test_repo/merges","archive_url":"https://api.github.com/repos/forgejo/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/forgejo/test_repo/downloads","issues_url":"https://api.github.com/repos/forgejo/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/forgejo/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/forgejo/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/forgejo/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/forgejo/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/forgejo/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/forgejo/test_repo/deployments","created_at":"2025-08-07T12:34:21Z","updated_at":"2025-08-12T11:21:45Z","pushed_at":"2025-08-07T13:07:49Z","git_url":"git://github.com/forgejo/test_repo.git","ssh_url":"git@github.com:forgejo/test_repo.git","clone_url":"https://github.com/forgejo/test_repo.git","svn_url":"https://github.com/forgejo/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":6,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":1,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":5,"license":{"key":"0bsd","name":"BSD Zero Clause License","spdx_id":"0BSD","url":"https://api.github.com/licenses/0bsd","node_id":"MDc6TGljZW5zZTM1"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["forgejo"],"visibility":"public","forks":1,"open_issues":5,"watchers":1,"default_branch":"main","permissions":{"admin":true,"maintain":true,"push":true,"triage":true,"pull":true},"temp_clone_token":"","allow_squash_merge":true,"allow_merge_commit":true,"allow_rebase_merge":true,"allow_auto_merge":false,"delete_branch_on_merge":false,"allow_update_branch":false,"use_squash_pr_title_as_default":false,"squash_merge_commit_message":"COMMIT_MESSAGES","squash_merge_commit_title":"COMMIT_OR_PR_TITLE","merge_commit_message":"PR_TITLE","merge_commit_title":"MERGE_MESSAGE","custom_properties":{},"organization":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"security_and_analysis":{"secret_scanning":{"status":"disabled"},"secret_scanning_push_protection":{"status":"disabled"},"dependabot_security_updates":{"status":"disabled"},"secret_scanning_non_provider_patterns":{"status":"disabled"},"secret_scanning_validity_checks":{"status":"disabled"}},"network_count":1,"subscribers_count":0} \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2 similarity index 76% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2 index a7a105b3e7..2af575abc9 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2 @@ -1,25 +1,26 @@ -X-Ratelimit-Used: 88 -X-Ratelimit-Resource: core -X-Content-Type-Options: nosniff +X-Github-Api-Version-Selected: 2022-11-28 Content-Security-Policy: default-src 'none' -X-Github-Request-Id: C7CC:3118FC:3F60B4F:403741E:6729E6BA Content-Type: application/json; charset=utf-8 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Ratelimit-Reset: 1730800941 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Cache-Control: private, max-age=60, s-maxage=60 -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -Access-Control-Allow-Origin: * +Content-Length: 2 +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" +X-Ratelimit-Remaining: 4875 Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Github-Request-Id: E80A:118F3A:20652B:1E80D9:689B401D +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: repo +X-Ratelimit-Limit: 5000 +X-Ratelimit-Reset: 1755007969 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset X-Frame-Options: deny +Cache-Control: private, max-age=60, s-maxage=60 +X-Ratelimit-Used: 125 +X-Ratelimit-Resource: core +Access-Control-Allow-Origin: * +X-Content-Type-Options: nosniff X-Xss-Protection: 0 Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: repo -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Remaining: 4912 -Content-Length: 2 +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; param=squirrel-girl-preview [] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created new file mode 100644 index 0000000000..5a45f886a9 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created @@ -0,0 +1,25 @@ +X-Content-Type-Options: nosniff +X-Xss-Protection: 0 +Cache-Control: private, max-age=60, s-maxage=60 +X-Oauth-Scopes: public_repo, repo:status +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +X-Ratelimit-Reset: 1755007969 +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Content-Security-Policy: default-src 'none' +X-Github-Request-Id: E80A:118F3A:206AFD:1E8691:689B401E +Content-Type: application/json; charset=utf-8 +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Remaining: 4873 +X-Ratelimit-Used: 127 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Accepted-Oauth-Scopes: +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +Etag: W/"efada731cc49c5e9612cad9e891f65f645857f94ff3ea109dc80927064a5978c" +X-Ratelimit-Limit: 5000 +X-Ratelimit-Resource: core +Access-Control-Allow-Origin: * +X-Frame-Options: deny + +[{"url":"https://api.github.com/repos/forgejo/test_repo/issues/comments/3164123494","html_url":"https://github.com/forgejo/test_repo/issues/2#issuecomment-3164123494","issue_url":"https://api.github.com/repos/forgejo/test_repo/issues/2","id":3164123494,"node_id":"IC_kwDOPZ8tBM68mLFm","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2025-08-07T13:07:25Z","updated_at":"2025-08-07T13:07:25Z","author_association":"COLLABORATOR","body":"Mentioning #3 \nWith some **bold** *statement*","reactions":{"url":"https://api.github.com/repos/forgejo/test_repo/issues/comments/3164123494/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 new file mode 100644 index 0000000000..ecaafa82e4 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 @@ -0,0 +1,26 @@ +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Frame-Options: deny +X-Content-Type-Options: nosniff +X-Accepted-Oauth-Scopes: repo +X-Ratelimit-Reset: 1755007969 +Access-Control-Allow-Origin: * +Content-Type: application/json; charset=utf-8 +Content-Length: 2 +Cache-Control: private, max-age=60, s-maxage=60 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Oauth-Scopes: public_repo, repo:status +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +X-Ratelimit-Limit: 5000 +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" +X-Ratelimit-Remaining: 4874 +X-Ratelimit-Used: 126 +X-Ratelimit-Resource: core +X-Xss-Protection: 0 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Content-Security-Policy: default-src 'none' +X-Github-Request-Id: E80A:118F3A:206814:1E83B2:689B401E +X-Github-Api-Version-Selected: 2022-11-28 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset + +[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 new file mode 100644 index 0000000000..a900075c9b --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=1&per_page=2 @@ -0,0 +1,26 @@ +Content-Type: application/json; charset=utf-8 +Content-Length: 2 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +X-Ratelimit-Resource: core +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Github-Request-Id: E80A:118F3A:20758C:1E9080:689B401F +X-Oauth-Scopes: public_repo, repo:status +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Limit: 5000 +X-Ratelimit-Reset: 1755007969 +Access-Control-Allow-Origin: * +Content-Security-Policy: default-src 'none' +Cache-Control: private, max-age=60, s-maxage=60 +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Remaining: 4870 +X-Ratelimit-Used: 130 +X-Content-Type-Options: nosniff +X-Xss-Protection: 0 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" +X-Accepted-Oauth-Scopes: repo +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Frame-Options: deny + +[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=4&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=2&per_page=2 similarity index 61% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=4&per_page=2 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=2&per_page=2 index f14d4ba904..7d978a2584 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=4&per_page=2 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F3%2Freactions%3Fpage=2&per_page=2 @@ -1,26 +1,27 @@ -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Oauth-Scopes: X-Ratelimit-Limit: 5000 -X-Ratelimit-Remaining: 4918 -X-Ratelimit-Reset: 1730800941 Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Content-Type-Options: nosniff -X-Github-Request-Id: C7CC:3118FC:3F60229:4036AE8:6729E6B8 -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -Link: ; rel="prev", ; rel="last", ; rel="first" -X-Accepted-Oauth-Scopes: repo -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Ratelimit-Used: 82 -X-Frame-Options: deny -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -Cache-Control: private, max-age=60, s-maxage=60 -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Resource: core -Access-Control-Allow-Origin: * X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Github-Request-Id: F458:8333F:84A571:7C4748:689B3396 Content-Type: application/json; charset=utf-8 Content-Length: 2 +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: repo +X-Ratelimit-Reset: 1755004338 +X-Content-Type-Options: nosniff +Cache-Control: private, max-age=60, s-maxage=60 +X-Ratelimit-Used: 56 +X-Ratelimit-Resource: core +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Access-Control-Allow-Origin: * +X-Frame-Options: deny +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +X-Ratelimit-Remaining: 4944 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin Content-Security-Policy: default-src 'none' +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" +Link: ; rel="prev", ; rel="last", ; rel="first" +X-Github-Api-Version-Selected: 2022-11-28 [] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F7%2Freactions%3Fpage=1&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F7%2Freactions%3Fpage=1&per_page=2 new file mode 100644 index 0000000000..694c612697 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2F7%2Freactions%3Fpage=1&per_page=2 @@ -0,0 +1,26 @@ +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Accepted-Oauth-Scopes: repo +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Resource: core +Access-Control-Allow-Origin: * +X-Frame-Options: deny +Cache-Control: private, max-age=60, s-maxage=60 +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" +X-Ratelimit-Reset: 1755007969 +X-Xss-Protection: 0 +X-Github-Request-Id: E80A:118F3A:207936:1E936E:689B4020 +X-Oauth-Scopes: public_repo, repo:status +X-Ratelimit-Remaining: 4869 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Content-Type-Options: nosniff +Content-Security-Policy: default-src 'none' +Content-Type: application/json; charset=utf-8 +Content-Length: 2 +X-Ratelimit-Limit: 5000 +X-Ratelimit-Used: 131 +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin + +[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553138856%2Freactions%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2Fcomments%2F3164123494%2Freactions%3Fpage=1&per_page=100 similarity index 76% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553138856%2Freactions%3Fpage=1&per_page=100 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2Fcomments%2F3164123494%2Freactions%3Fpage=1&per_page=100 index ac446b3586..4ee1c6bc52 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553138856%2Freactions%3Fpage=1&per_page=100 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%2Fcomments%2F3164123494%2Freactions%3Fpage=1&per_page=100 @@ -1,25 +1,26 @@ -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Ratelimit-Remaining: 4914 -X-Frame-Options: deny -X-Xss-Protection: 0 -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Resource: core -X-Content-Type-Options: nosniff -Content-Type: application/json; charset=utf-8 -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -X-Accepted-Oauth-Scopes: -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -Content-Length: 2 -Access-Control-Allow-Origin: * Strict-Transport-Security: max-age=31536000; includeSubdomains; preload Content-Security-Policy: default-src 'none' -X-Github-Request-Id: C7CC:3118FC:3F60858:4037116:6729E6B9 -Cache-Control: private, max-age=60, s-maxage=60 +Content-Type: application/json; charset=utf-8 +X-Oauth-Scopes: public_repo, repo:status +X-Ratelimit-Remaining: 4872 +X-Frame-Options: deny +X-Xss-Protection: 0 Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Oauth-Scopes: -X-Ratelimit-Used: 86 +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Limit: 5000 +X-Ratelimit-Used: 128 +X-Content-Type-Options: nosniff +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Cache-Control: private, max-age=60, s-maxage=60 +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Resource: core Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Github-Request-Id: E80A:118F3A:206DA5:1E890E:689B401E +Content-Length: 2 +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" +X-Accepted-Oauth-Scopes: +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +Access-Control-Allow-Origin: * [] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all new file mode 100644 index 0000000000..6e74d44a20 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all @@ -0,0 +1,26 @@ +Link: ; rel="next" +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Reset: 1755007969 +Access-Control-Allow-Origin: * +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Used: 124 +X-Frame-Options: deny +X-Xss-Protection: 0 +Content-Security-Policy: default-src 'none' +X-Github-Request-Id: E80A:118F3A:206102:1E7D47:689B401C +Etag: W/"cc2f0f6e0b3c82ac10b12bf3ad5d74d62a575371f88fc689a7af78408e34eeb5" +X-Ratelimit-Limit: 5000 +X-Ratelimit-Remaining: 4876 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Content-Type: application/json; charset=utf-8 +Cache-Control: private, max-age=60, s-maxage=60 +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: repo +X-Ratelimit-Resource: core +X-Content-Type-Options: nosniff +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Github-Media-Type: github.v3; param=squirrel-girl-preview + +[{"url":"https://api.github.com/repos/forgejo/test_repo/issues/1","repository_url":"https://api.github.com/repos/forgejo/test_repo","labels_url":"https://api.github.com/repos/forgejo/test_repo/issues/1/labels{/name}","comments_url":"https://api.github.com/repos/forgejo/test_repo/issues/1/comments","events_url":"https://api.github.com/repos/forgejo/test_repo/issues/1/events","html_url":"https://github.com/forgejo/test_repo/issues/1","id":3300365512,"node_id":"I_kwDOPZ8tBM7Et5TI","number":1,"title":"First issue","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2025-08-07T12:44:07Z","updated_at":"2025-08-07T12:44:47Z","closed_at":null,"author_association":"COLLABORATOR","type":null,"active_lock_reason":null,"sub_issues_summary":{"total":0,"completed":0,"percent_completed":0},"body":"This is an issue.","closed_by":null,"reactions":{"url":"https://api.github.com/repos/forgejo/test_repo/issues/1/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/forgejo/test_repo/issues/1/timeline","performed_via_github_app":null,"state_reason":null},{"url":"https://api.github.com/repos/forgejo/test_repo/issues/2","repository_url":"https://api.github.com/repos/forgejo/test_repo","labels_url":"https://api.github.com/repos/forgejo/test_repo/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/forgejo/test_repo/issues/2/comments","events_url":"https://api.github.com/repos/forgejo/test_repo/issues/2/events","html_url":"https://github.com/forgejo/test_repo/issues/2","id":3300370333,"node_id":"I_kwDOPZ8tBM7Et6ed","number":2,"title":"Second Issue","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":9072229377,"node_id":"LA_kwDOPZ8tBM8AAAACHL88AQ","url":"https://api.github.com/repos/forgejo/test_repo/labels/duplicate","name":"duplicate","color":"cfd3d7","default":true,"description":"This issue or pull request already exists"},{"id":9072229408,"node_id":"LA_kwDOPZ8tBM8AAAACHL88IA","url":"https://api.github.com/repos/forgejo/test_repo/labels/good%20first%20issue","name":"good first issue","color":"7057ff","default":true,"description":"Good for newcomers"},{"id":9072229417,"node_id":"LA_kwDOPZ8tBM8AAAACHL88KQ","url":"https://api.github.com/repos/forgejo/test_repo/labels/help%20wanted","name":"help wanted","color":"008672","default":true,"description":"Extra attention is needed"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/forgejo/test_repo/milestones/2","html_url":"https://github.com/forgejo/test_repo/milestone/2","labels_url":"https://api.github.com/repos/forgejo/test_repo/milestones/2/labels","id":13445587,"node_id":"MI_kwDOPZ8tBM4AzSnT","number":2,"title":"1.1.0","description":"We can do that","creator":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":0,"state":"open","created_at":"2025-08-07T12:50:58Z","updated_at":"2025-08-07T12:53:15Z","due_on":"2025-08-31T07:00:00Z","closed_at":null},"comments":1,"created_at":"2025-08-07T12:45:44Z","updated_at":"2025-08-07T13:07:25Z","closed_at":null,"author_association":"COLLABORATOR","type":null,"active_lock_reason":null,"sub_issues_summary":{"total":0,"completed":0,"percent_completed":0},"body":"Mentioning #1 ","closed_by":null,"reactions":{"url":"https://api.github.com/repos/forgejo/test_repo/issues/2/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/forgejo/test_repo/issues/2/timeline","performed_via_github_app":null,"state_reason":null}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 new file mode 100644 index 0000000000..ef1aebf7a8 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 @@ -0,0 +1,25 @@ +Cache-Control: private, max-age=60, s-maxage=60 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Accepted-Oauth-Scopes: repo +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Reset: 1755007969 +X-Xss-Protection: 0 +X-Github-Request-Id: E80A:118F3A:205BDE:1E77C9:689B401C +Content-Type: application/json; charset=utf-8 +Etag: W/"fc30186df2be93c9dbcbb326f3d0249e5d618f924528d71af1d11941dc37f8b0" +X-Oauth-Scopes: public_repo, repo:status +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Github-Media-Type: github.v3; format=json +X-Ratelimit-Limit: 5000 +Access-Control-Allow-Origin: * +X-Frame-Options: deny +X-Content-Type-Options: nosniff +X-Ratelimit-Remaining: 4878 +X-Ratelimit-Used: 122 +X-Ratelimit-Resource: core +Content-Security-Policy: default-src 'none' + +[{"id":9072229344,"node_id":"LA_kwDOPZ8tBM8AAAACHL874A","url":"https://api.github.com/repos/forgejo/test_repo/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"},{"id":9072229361,"node_id":"LA_kwDOPZ8tBM8AAAACHL878Q","url":"https://api.github.com/repos/forgejo/test_repo/labels/documentation","name":"documentation","color":"0075ca","default":true,"description":"Improvements or additions to documentation"},{"id":9072229377,"node_id":"LA_kwDOPZ8tBM8AAAACHL88AQ","url":"https://api.github.com/repos/forgejo/test_repo/labels/duplicate","name":"duplicate","color":"cfd3d7","default":true,"description":"This issue or pull request already exists"},{"id":9072229390,"node_id":"LA_kwDOPZ8tBM8AAAACHL88Dg","url":"https://api.github.com/repos/forgejo/test_repo/labels/enhancement","name":"enhancement","color":"a2eeef","default":true,"description":"New feature or request"},{"id":9072229408,"node_id":"LA_kwDOPZ8tBM8AAAACHL88IA","url":"https://api.github.com/repos/forgejo/test_repo/labels/good%20first%20issue","name":"good first issue","color":"7057ff","default":true,"description":"Good for newcomers"},{"id":9072229417,"node_id":"LA_kwDOPZ8tBM8AAAACHL88KQ","url":"https://api.github.com/repos/forgejo/test_repo/labels/help%20wanted","name":"help wanted","color":"008672","default":true,"description":"Extra attention is needed"},{"id":9072229426,"node_id":"LA_kwDOPZ8tBM8AAAACHL88Mg","url":"https://api.github.com/repos/forgejo/test_repo/labels/invalid","name":"invalid","color":"e4e669","default":true,"description":"This doesn't seem right"},{"id":9072229435,"node_id":"LA_kwDOPZ8tBM8AAAACHL88Ow","url":"https://api.github.com/repos/forgejo/test_repo/labels/question","name":"question","color":"d876e3","default":true,"description":"Further information is requested"},{"id":9072229442,"node_id":"LA_kwDOPZ8tBM8AAAACHL88Qg","url":"https://api.github.com/repos/forgejo/test_repo/labels/wontfix","name":"wontfix","color":"ffffff","default":true,"description":"This will not be worked on"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all new file mode 100644 index 0000000000..7dba1e4aac --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all @@ -0,0 +1,25 @@ +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Github-Request-Id: E80A:118F3A:205928:1E754F:689B401B +Etag: W/"39c17013c8c7b0f5abf91f15e52496c38730029baf6f37b5c3f3e40eb9886aab" +X-Ratelimit-Remaining: 4879 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Xss-Protection: 0 +Content-Type: application/json; charset=utf-8 +Cache-Control: private, max-age=60, s-maxage=60 +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Used: 121 +X-Ratelimit-Resource: core +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; format=json +X-Github-Api-Version-Selected: 2022-11-28 +X-Frame-Options: deny +X-Content-Type-Options: nosniff +Content-Security-Policy: default-src 'none' +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: repo +X-Ratelimit-Limit: 5000 +Access-Control-Allow-Origin: * + +[{"url":"https://api.github.com/repos/forgejo/test_repo/milestones/1","html_url":"https://github.com/forgejo/test_repo/milestone/1","labels_url":"https://api.github.com/repos/forgejo/test_repo/milestones/1/labels","id":13445581,"node_id":"MI_kwDOPZ8tBM4AzSnN","number":1,"title":"1.0.0","description":"Version 1","creator":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":2,"state":"open","created_at":"2025-08-07T12:48:56Z","updated_at":"2025-08-12T12:34:20Z","due_on":null,"closed_at":null},{"url":"https://api.github.com/repos/forgejo/test_repo/milestones/3","html_url":"https://github.com/forgejo/test_repo/milestone/3","labels_url":"https://api.github.com/repos/forgejo/test_repo/milestones/3/labels","id":13445604,"node_id":"MI_kwDOPZ8tBM4AzSnk","number":3,"title":"0.9.0","description":"A milestone","creator":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":0,"state":"closed","created_at":"2025-08-07T12:54:20Z","updated_at":"2025-08-12T11:29:52Z","due_on":"2025-08-01T07:00:00Z","closed_at":"2025-08-07T12:54:38Z"},{"url":"https://api.github.com/repos/forgejo/test_repo/milestones/2","html_url":"https://github.com/forgejo/test_repo/milestone/2","labels_url":"https://api.github.com/repos/forgejo/test_repo/milestones/2/labels","id":13445587,"node_id":"MI_kwDOPZ8tBM4AzSnT","number":2,"title":"1.1.0","description":"We can do that","creator":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":0,"state":"open","created_at":"2025-08-07T12:50:58Z","updated_at":"2025-08-07T12:53:15Z","due_on":"2025-08-31T07:00:00Z","closed_at":null}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100 similarity index 75% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100 index 1b4481f890..5d382a0ed1 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Frequested_reviewers%3Fper_page=100 @@ -1,24 +1,25 @@ -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Etag: W/"21730161122bd4f229e886dd4a85b45fa575182d6dcef7aa0016a5d21353c9ab" -X-Oauth-Scopes: -X-Github-Media-Type: github.v3; format=json -Access-Control-Allow-Origin: * -X-Frame-Options: deny -X-Content-Type-Options: nosniff -X-Xss-Protection: 0 -X-Github-Request-Id: C7CC:3118FC:3F6187A:4038171:6729E6BD -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Content-Security-Policy: default-src 'none' Content-Type: application/json; charset=utf-8 +X-Oauth-Scopes: public_repo, repo:status +Access-Control-Allow-Origin: * +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Content-Security-Policy: default-src 'none' Cache-Control: private, max-age=60, s-maxage=60 X-Accepted-Oauth-Scopes: -X-Ratelimit-Remaining: 4905 -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Used: 95 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Resource: core +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Frame-Options: deny +X-Content-Type-Options: nosniff +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Github-Media-Type: github.v3; format=json +X-Ratelimit-Limit: 5000 +X-Ratelimit-Remaining: 4863 +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Used: 137 +X-Xss-Protection: 0 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Etag: W/"bf72ef7e37aa7c7a418e89035db9d7f23b969af0705f1e9c7ee692aad6c272f7" +X-Github-Request-Id: E80A:118F3A:208A8A:1EA3C0:689B4023 {"users":[],"teams":[]} \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3096999684%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3096999684%2Fcomments%3Fper_page=100 new file mode 100644 index 0000000000..51f70d2a81 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3096999684%2Fcomments%3Fper_page=100 @@ -0,0 +1,25 @@ +Content-Type: application/json; charset=utf-8 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +Etag: W/"9f6ff2d639a65dc556bded7ae25926c53ac6673582b68d1ebbbe3ecb1c375e3b" +X-Ratelimit-Limit: 5000 +Access-Control-Allow-Origin: * +X-Content-Type-Options: nosniff +X-Xss-Protection: 0 +Content-Security-Policy: default-src 'none' +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Remaining: 4867 +X-Ratelimit-Resource: core +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Github-Request-Id: E80A:118F3A:207FBB:1E9958:689B4021 +Cache-Control: private, max-age=60, s-maxage=60 +X-Github-Media-Type: github.v3; format=json +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Used: 133 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Frame-Options: deny + +[{"id":2260216729,"node_id":"PRRC_kwDOPZ8tBM6GuCuZ","url":"https://api.github.com/repos/forgejo/test_repo/pulls/comments/2260216729","pull_request_review_id":3096999684,"diff_hunk":"@@ -1,3 +1,5 @@\n # Forgejo Test Repo\n \n This repo is used to test migrations\n+\n+Add some feature description.","path":"readme.md","position":5,"original_position":5,"commit_id":"c608ab3997349219e1510cdb5ddd1e5e82897dfa","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"May want to write more","created_at":"2025-08-07T12:47:50Z","updated_at":"2025-08-07T12:47:55Z","html_url":"https://github.com/forgejo/test_repo/pull/3#discussion_r2260216729","pull_request_url":"https://api.github.com/repos/forgejo/test_repo/pulls/3","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/comments/2260216729"},"html":{"href":"https://github.com/forgejo/test_repo/pull/3#discussion_r2260216729"},"pull_request":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/3"}},"original_commit_id":"c608ab3997349219e1510cdb5ddd1e5e82897dfa","reactions":{"url":"https://api.github.com/repos/forgejo/test_repo/pulls/comments/2260216729/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0}}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3097007243%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3097007243%2Fcomments%3Fper_page=100 new file mode 100644 index 0000000000..cd5fbdaf9d --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%2F3097007243%2Fcomments%3Fper_page=100 @@ -0,0 +1,25 @@ +Etag: W/"17a2560299e1dbb8e8b83de09a8a409cd7b735baeebea469a041311a71a948d0" +X-Accepted-Oauth-Scopes: +X-Ratelimit-Remaining: 4865 +X-Ratelimit-Resource: core +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +Content-Security-Policy: default-src 'none' +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Used: 135 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Github-Request-Id: E80A:118F3A:20854A:1E9EB1:689B4022 +Content-Type: application/json; charset=utf-8 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Github-Media-Type: github.v3; format=json +X-Ratelimit-Limit: 5000 +X-Frame-Options: deny +X-Content-Type-Options: nosniff +X-Xss-Protection: 0 +X-Oauth-Scopes: public_repo, repo:status +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Reset: 1755007969 +Access-Control-Allow-Origin: * +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Cache-Control: private, max-age=60, s-maxage=60 + +[{"id":2260221159,"node_id":"PRRC_kwDOPZ8tBM6GuDzn","url":"https://api.github.com/repos/forgejo/test_repo/pulls/comments/2260221159","pull_request_review_id":3097007243,"diff_hunk":"@@ -1,3 +1,5 @@\n # Forgejo Test Repo\n \n This repo is used to test migrations","path":"readme.md","position":3,"original_position":3,"commit_id":"c608ab3997349219e1510cdb5ddd1e5e82897dfa","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Comment","created_at":"2025-08-07T12:49:36Z","updated_at":"2025-08-07T12:49:36Z","html_url":"https://github.com/forgejo/test_repo/pull/3#discussion_r2260221159","pull_request_url":"https://api.github.com/repos/forgejo/test_repo/pulls/3","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/comments/2260221159"},"html":{"href":"https://github.com/forgejo/test_repo/pull/3#discussion_r2260221159"},"pull_request":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/3"}},"original_commit_id":"c608ab3997349219e1510cdb5ddd1e5e82897dfa","reactions":{"url":"https://api.github.com/repos/forgejo/test_repo/pulls/comments/2260221159/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0}}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 new file mode 100644 index 0000000000..82970c7a67 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 @@ -0,0 +1,25 @@ +Content-Type: application/json; charset=utf-8 +X-Accepted-Oauth-Scopes: +X-Github-Media-Type: github.v3; format=json +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Content-Type-Options: nosniff +Content-Security-Policy: default-src 'none' +Cache-Control: private, max-age=60, s-maxage=60 +Etag: W/"d3281ac301ca94c8125009c335025cae84e5af242cb315bb975afad875b825e2" +X-Oauth-Scopes: public_repo, repo:status +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Remaining: 4868 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Github-Request-Id: E80A:118F3A:207BE7:1E9600:689B4020 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Limit: 5000 +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Resource: core +Access-Control-Allow-Origin: * +X-Frame-Options: deny +X-Xss-Protection: 0 +X-Ratelimit-Used: 132 +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload + +[{"id":3096999684,"node_id":"PRR_kwDOPZ8tBM64mHcE","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?u=c4857996d3079f843b3eb2a7dcb347698d817034&v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"","state":"COMMENTED","html_url":"https://github.com/forgejo/test_repo/pull/3#pullrequestreview-3096999684","pull_request_url":"https://api.github.com/repos/forgejo/test_repo/pulls/3","author_association":"COLLABORATOR","_links":{"html":{"href":"https://github.com/forgejo/test_repo/pull/3#pullrequestreview-3096999684"},"pull_request":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/3"}},"submitted_at":"2025-08-07T12:47:55Z","commit_id":"c608ab3997349219e1510cdb5ddd1e5e82897dfa"},{"id":3097007243,"node_id":"PRR_kwDOPZ8tBM64mJSL","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?u=c4857996d3079f843b3eb2a7dcb347698d817034&v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"","state":"COMMENTED","html_url":"https://github.com/forgejo/test_repo/pull/3#pullrequestreview-3097007243","pull_request_url":"https://api.github.com/repos/forgejo/test_repo/pulls/3","author_association":"COLLABORATOR","_links":{"html":{"href":"https://github.com/forgejo/test_repo/pull/3#pullrequestreview-3097007243"},"pull_request":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/3"}},"submitted_at":"2025-08-07T12:49:36Z","commit_id":"c608ab3997349219e1510cdb5ddd1e5e82897dfa"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315860062%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100 similarity index 63% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315860062%2Fcomments%3Fper_page=100 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100 index 389c1b7567..eea4344781 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315860062%2Fcomments%3Fper_page=100 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100 @@ -1,25 +1,23 @@ -Content-Length: 2 -X-Frame-Options: deny -X-Github-Api-Version-Selected: 2022-11-28 X-Ratelimit-Limit: 5000 -X-Ratelimit-Used: 93 -X-Ratelimit-Resource: core +X-Ratelimit-Remaining: 4856 +X-Ratelimit-Used: 144 +X-Accepted-Oauth-Scopes: repo +X-Github-Media-Type: github.v3; format=json +X-Github-Api-Version-Selected: 2022-11-28 Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Content-Type: application/json; charset=utf-8 -X-Accepted-Oauth-Scopes: X-Xss-Protection: 0 -X-Oauth-Scopes: -X-Ratelimit-Remaining: 4907 -X-Ratelimit-Reset: 1730800941 +Content-Security-Policy: default-src 'none' +Vary: Accept-Encoding, Accept, X-Requested-With +X-Github-Request-Id: EDE1:32C4F6:A76DB4:9D7693:689B37A6 +X-Ratelimit-Reset: 1755004338 +X-Ratelimit-Resource: core +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Frame-Options: deny +Content-Type: application/json; charset=utf-8 +X-Oauth-Scopes: public_repo, repo:status +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC Access-Control-Allow-Origin: * X-Content-Type-Options: nosniff -Content-Security-Policy: default-src 'none' -Cache-Control: private, max-age=60, s-maxage=60 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Github-Request-Id: C7CC:3118FC:3F614AA:4037DB7:6729E6BD -X-Github-Media-Type: github.v3; format=json Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Etag: "450a1c087fec81e5b86092ff5372c3db8ca834c1e23c03c6b06ecca33cefd665" -[] \ No newline at end of file +{"message":"Not Found","documentation_url":"https://docs.github.com/rest/pulls/reviews#list-reviews-for-a-pull-request","status":"404"} \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Frequested_reviewers%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Frequested_reviewers%3Fper_page=100 similarity index 75% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Frequested_reviewers%3Fper_page=100 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Frequested_reviewers%3Fper_page=100 index 676e326094..f3bef5c3bd 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Frequested_reviewers%3Fper_page=100 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Frequested_reviewers%3Fper_page=100 @@ -1,24 +1,25 @@ -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Oauth-Scopes: -X-Ratelimit-Limit: 5000 -X-Ratelimit-Remaining: 4898 +X-Ratelimit-Remaining: 4958 +X-Content-Type-Options: nosniff +Content-Security-Policy: default-src 'none' +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; format=json +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Used: 42 +X-Ratelimit-Resource: core +Etag: W/"bf72ef7e37aa7c7a418e89035db9d7f23b969af0705f1e9c7ee692aad6c272f7" Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Access-Control-Allow-Origin: * +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Frame-Options: deny X-Xss-Protection: 0 Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Security-Policy: default-src 'none' -X-Github-Request-Id: C7CC:3118FC:3F623DF:4038CEB:6729E6C0 -Etag: W/"21730161122bd4f229e886dd4a85b45fa575182d6dcef7aa0016a5d21353c9ab" -X-Accepted-Oauth-Scopes: -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Resource: core -Access-Control-Allow-Origin: * -X-Frame-Options: deny -Content-Type: application/json; charset=utf-8 Cache-Control: private, max-age=60, s-maxage=60 -X-Ratelimit-Used: 102 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Reset: 1730800941 -X-Content-Type-Options: nosniff +X-Github-Request-Id: F852:1553BC:10F18A:FD450:689B3DFF +Content-Type: application/json; charset=utf-8 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Ratelimit-Limit: 5000 {"users":[],"teams":[]} \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315861440%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Freviews%3Fper_page=100 similarity index 75% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315861440%2Fcomments%3Fper_page=100 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Freviews%3Fper_page=100 index e52428a5af..fea2907aa4 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315861440%2Fcomments%3Fper_page=100 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2F7%2Freviews%3Fper_page=100 @@ -1,25 +1,26 @@ -Content-Type: application/json; charset=utf-8 -Cache-Control: private, max-age=60, s-maxage=60 -X-Ratelimit-Reset: 1730800941 -X-Content-Type-Options: nosniff -Content-Security-Policy: default-src 'none' -X-Accepted-Oauth-Scopes: X-Xss-Protection: 0 -X-Github-Request-Id: C7CC:3118FC:3F61690:4037F90:6729E6BD +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Oauth-Scopes: public_repo, repo:status +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; format=json +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +X-Github-Request-Id: F852:1553BC:10EEC0:FD190:689B3DFE +Content-Type: application/json; charset=utf-8 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +X-Frame-Options: deny +Content-Security-Policy: default-src 'none' Content-Length: 2 -X-Oauth-Scopes: +X-Accepted-Oauth-Scopes: X-Github-Api-Version-Selected: 2022-11-28 X-Ratelimit-Limit: 5000 -X-Frame-Options: deny -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Access-Control-Allow-Origin: * -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -Etag: "450a1c087fec81e5b86092ff5372c3db8ca834c1e23c03c6b06ecca33cefd665" -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Remaining: 4906 -X-Ratelimit-Used: 94 +X-Ratelimit-Remaining: 4959 +X-Ratelimit-Used: 41 X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Access-Control-Allow-Origin: * +Cache-Control: private, max-age=60, s-maxage=60 +Etag: "bb321a3b596949885b60e203a5ea335475c7ac9e2364d10f463cb934490c25a5" +X-Ratelimit-Reset: 1755007969 Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Content-Type-Options: nosniff [] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363017488%2Freactions%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260216729%2Freactions%3Fpage=1&per_page=100 similarity index 76% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363017488%2Freactions%3Fpage=1&per_page=100 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260216729%2Freactions%3Fpage=1&per_page=100 index 748ae93381..39a6e753b9 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363017488%2Freactions%3Fpage=1&per_page=100 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260216729%2Freactions%3Fpage=1&per_page=100 @@ -1,25 +1,26 @@ -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -X-Accepted-Oauth-Scopes: -X-Ratelimit-Remaining: 4902 -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Resource: core -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Request-Id: C7CC:3118FC:3F61D73:4038667:6729E6BF Content-Type: application/json; charset=utf-8 -X-Ratelimit-Limit: 5000 -Content-Length: 2 -Cache-Control: private, max-age=60, s-maxage=60 -X-Oauth-Scopes: +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Used: 98 +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload X-Frame-Options: deny +X-Github-Request-Id: E80A:118F3A:2082EC:1E9C92:689B4022 +Content-Length: 2 +X-Ratelimit-Remaining: 4866 +X-Ratelimit-Resource: core +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Cache-Control: private, max-age=60, s-maxage=60 +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +X-Ratelimit-Limit: 5000 +X-Ratelimit-Reset: 1755007969 +X-Ratelimit-Used: 134 +Access-Control-Allow-Origin: * +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" X-Content-Type-Options: nosniff X-Xss-Protection: 0 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin Content-Security-Policy: default-src 'none' [] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363029944%2Freactions%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260221159%2Freactions%3Fpage=1&per_page=100 similarity index 76% rename from services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363029944%2Freactions%3Fpage=1&per_page=100 rename to services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260221159%2Freactions%3Fpage=1&per_page=100 index 0b0ae88deb..1c80bb32d7 100644 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2Fcomments%2F363029944%2Freactions%3Fpage=1&per_page=100 +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%2Fcomments%2F2260221159%2Freactions%3Fpage=1&per_page=100 @@ -1,25 +1,26 @@ -X-Accepted-Oauth-Scopes: -X-Ratelimit-Remaining: 4899 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Length: 2 -X-Ratelimit-Used: 101 -Access-Control-Allow-Origin: * +X-Ratelimit-Used: 136 X-Frame-Options: deny -X-Content-Type-Options: nosniff -X-Xss-Protection: 0 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +X-Github-Request-Id: E80A:118F3A:20881D:1EA17C:689B4022 +Content-Type: application/json; charset=utf-8 +X-Github-Media-Type: github.v3; param=squirrel-girl-preview +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Access-Control-Allow-Origin: * Content-Security-Policy: default-src 'none' Cache-Control: private, max-age=60, s-maxage=60 -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -X-Oauth-Scopes: +X-Oauth-Scopes: public_repo, repo:status +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Reset: 1730800941 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Content-Type: application/json; charset=utf-8 +X-Content-Type-Options: nosniff +Content-Length: 2 Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Github-Media-Type: github.v3; param=squirrel-girl-preview +Etag: "578cfdf865ccd59faa414c655476c87af814e220755c938b9545967521b6f14b" +X-Ratelimit-Remaining: 4864 +X-Ratelimit-Reset: 1755007969 X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Github-Request-Id: C7CC:3118FC:3F622AC:4038BA5:6729E6C0 +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Xss-Protection: 0 +X-Accepted-Oauth-Scopes: +X-Ratelimit-Limit: 5000 [] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all new file mode 100644 index 0000000000..2835f22e9b --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all @@ -0,0 +1,25 @@ +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Frame-Options: deny +Cache-Control: private, max-age=60, s-maxage=60 +X-Accepted-Oauth-Scopes: +X-Ratelimit-Remaining: 4871 +X-Content-Type-Options: nosniff +Content-Security-Policy: default-src 'none' +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Github-Media-Type: github.v3; format=json +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Reset: 1755007969 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Access-Control-Allow-Origin: * +X-Xss-Protection: 0 +X-Github-Request-Id: E80A:118F3A:207219:1E8D78:689B401F +Content-Type: application/json; charset=utf-8 +X-Oauth-Scopes: public_repo, repo:status +X-Ratelimit-Limit: 5000 +X-Ratelimit-Used: 129 +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +Etag: W/"9669b72db2e9016fd408b8c554dc01719c614d29bb965b082a81ee0489fd0914" +X-Ratelimit-Resource: core + +[{"url":"https://api.github.com/repos/forgejo/test_repo/pulls/3","id":2727677736,"node_id":"PR_kwDOPZ8tBM6ilQ8o","html_url":"https://github.com/forgejo/test_repo/pull/3","diff_url":"https://github.com/forgejo/test_repo/pull/3.diff","patch_url":"https://github.com/forgejo/test_repo/pull/3.patch","issue_url":"https://api.github.com/repos/forgejo/test_repo/issues/3","number":3,"state":"open","locked":false,"title":"Update readme.md","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Added a feature description","created_at":"2025-08-07T12:47:06Z","updated_at":"2025-08-12T13:16:49Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":9072229390,"node_id":"LA_kwDOPZ8tBM8AAAACHL88Dg","url":"https://api.github.com/repos/forgejo/test_repo/labels/enhancement","name":"enhancement","color":"a2eeef","default":true,"description":"New feature or request"}],"milestone":{"url":"https://api.github.com/repos/forgejo/test_repo/milestones/1","html_url":"https://github.com/forgejo/test_repo/milestone/1","labels_url":"https://api.github.com/repos/forgejo/test_repo/milestones/1/labels","id":13445581,"node_id":"MI_kwDOPZ8tBM4AzSnN","number":1,"title":"1.0.0","description":"Version 1","creator":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":2,"state":"open","created_at":"2025-08-07T12:48:56Z","updated_at":"2025-08-12T12:34:20Z","due_on":null,"closed_at":null},"draft":false,"commits_url":"https://api.github.com/repos/forgejo/test_repo/pulls/3/commits","review_comments_url":"https://api.github.com/repos/forgejo/test_repo/pulls/3/comments","review_comment_url":"https://api.github.com/repos/forgejo/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/forgejo/test_repo/issues/3/comments","statuses_url":"https://api.github.com/repos/forgejo/test_repo/statuses/c608ab3997349219e1510cdb5ddd1e5e82897dfa","head":{"label":"forgejo:some-feature","ref":"some-feature","sha":"c608ab3997349219e1510cdb5ddd1e5e82897dfa","user":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":1033841924,"node_id":"R_kgDOPZ8tBA","name":"test_repo","full_name":"forgejo/test_repo","private":false,"owner":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/forgejo/test_repo","description":"Exclusively used for testing Github->Forgejo migration","fork":false,"url":"https://api.github.com/repos/forgejo/test_repo","forks_url":"https://api.github.com/repos/forgejo/test_repo/forks","keys_url":"https://api.github.com/repos/forgejo/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/forgejo/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/forgejo/test_repo/teams","hooks_url":"https://api.github.com/repos/forgejo/test_repo/hooks","issue_events_url":"https://api.github.com/repos/forgejo/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/forgejo/test_repo/events","assignees_url":"https://api.github.com/repos/forgejo/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/forgejo/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/forgejo/test_repo/tags","blobs_url":"https://api.github.com/repos/forgejo/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/forgejo/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/forgejo/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/forgejo/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/forgejo/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/forgejo/test_repo/languages","stargazers_url":"https://api.github.com/repos/forgejo/test_repo/stargazers","contributors_url":"https://api.github.com/repos/forgejo/test_repo/contributors","subscribers_url":"https://api.github.com/repos/forgejo/test_repo/subscribers","subscription_url":"https://api.github.com/repos/forgejo/test_repo/subscription","commits_url":"https://api.github.com/repos/forgejo/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/forgejo/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/forgejo/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/forgejo/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/forgejo/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/forgejo/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/forgejo/test_repo/merges","archive_url":"https://api.github.com/repos/forgejo/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/forgejo/test_repo/downloads","issues_url":"https://api.github.com/repos/forgejo/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/forgejo/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/forgejo/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/forgejo/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/forgejo/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/forgejo/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/forgejo/test_repo/deployments","created_at":"2025-08-07T12:34:21Z","updated_at":"2025-08-12T11:21:45Z","pushed_at":"2025-08-07T13:07:49Z","git_url":"git://github.com/forgejo/test_repo.git","ssh_url":"git@github.com:forgejo/test_repo.git","clone_url":"https://github.com/forgejo/test_repo.git","svn_url":"https://github.com/forgejo/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":6,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":1,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":5,"license":{"key":"0bsd","name":"BSD Zero Clause License","spdx_id":"0BSD","url":"https://api.github.com/licenses/0bsd","node_id":"MDc6TGljZW5zZTM1"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["forgejo"],"visibility":"public","forks":1,"open_issues":5,"watchers":1,"default_branch":"main"}},"base":{"label":"forgejo:main","ref":"main","sha":"442d28a55b842472c95bead51a4c61f209ac1636","user":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":1033841924,"node_id":"R_kgDOPZ8tBA","name":"test_repo","full_name":"forgejo/test_repo","private":false,"owner":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/forgejo/test_repo","description":"Exclusively used for testing Github->Forgejo migration","fork":false,"url":"https://api.github.com/repos/forgejo/test_repo","forks_url":"https://api.github.com/repos/forgejo/test_repo/forks","keys_url":"https://api.github.com/repos/forgejo/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/forgejo/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/forgejo/test_repo/teams","hooks_url":"https://api.github.com/repos/forgejo/test_repo/hooks","issue_events_url":"https://api.github.com/repos/forgejo/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/forgejo/test_repo/events","assignees_url":"https://api.github.com/repos/forgejo/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/forgejo/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/forgejo/test_repo/tags","blobs_url":"https://api.github.com/repos/forgejo/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/forgejo/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/forgejo/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/forgejo/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/forgejo/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/forgejo/test_repo/languages","stargazers_url":"https://api.github.com/repos/forgejo/test_repo/stargazers","contributors_url":"https://api.github.com/repos/forgejo/test_repo/contributors","subscribers_url":"https://api.github.com/repos/forgejo/test_repo/subscribers","subscription_url":"https://api.github.com/repos/forgejo/test_repo/subscription","commits_url":"https://api.github.com/repos/forgejo/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/forgejo/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/forgejo/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/forgejo/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/forgejo/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/forgejo/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/forgejo/test_repo/merges","archive_url":"https://api.github.com/repos/forgejo/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/forgejo/test_repo/downloads","issues_url":"https://api.github.com/repos/forgejo/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/forgejo/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/forgejo/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/forgejo/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/forgejo/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/forgejo/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/forgejo/test_repo/deployments","created_at":"2025-08-07T12:34:21Z","updated_at":"2025-08-12T11:21:45Z","pushed_at":"2025-08-07T13:07:49Z","git_url":"git://github.com/forgejo/test_repo.git","ssh_url":"git@github.com:forgejo/test_repo.git","clone_url":"https://github.com/forgejo/test_repo.git","svn_url":"https://github.com/forgejo/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":6,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":1,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":5,"license":{"key":"0bsd","name":"BSD Zero Clause License","spdx_id":"0BSD","url":"https://api.github.com/licenses/0bsd","node_id":"MDc6TGljZW5zZTM1"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["forgejo"],"visibility":"public","forks":1,"open_issues":5,"watchers":1,"default_branch":"main"}},"_links":{"self":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/3"},"html":{"href":"https://github.com/forgejo/test_repo/pull/3"},"issue":{"href":"https://api.github.com/repos/forgejo/test_repo/issues/3"},"comments":{"href":"https://api.github.com/repos/forgejo/test_repo/issues/3/comments"},"review_comments":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/3/comments"},"review_comment":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/3/commits"},"statuses":{"href":"https://api.github.com/repos/forgejo/test_repo/statuses/c608ab3997349219e1510cdb5ddd1e5e82897dfa"}},"author_association":"COLLABORATOR","auto_merge":null,"active_lock_reason":null},{"url":"https://api.github.com/repos/forgejo/test_repo/pulls/7","id":2727724283,"node_id":"PR_kwDOPZ8tBM6ilcT7","html_url":"https://github.com/forgejo/test_repo/pull/7","diff_url":"https://github.com/forgejo/test_repo/pull/7.diff","patch_url":"https://github.com/forgejo/test_repo/pull/7.patch","issue_url":"https://api.github.com/repos/forgejo/test_repo/issues/7","number":7,"state":"closed","locked":false,"title":"Update readme.md","user":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Adding some text to the readme","created_at":"2025-08-07T13:01:36Z","updated_at":"2025-08-12T12:47:35Z","closed_at":"2025-08-07T13:02:19Z","merged_at":"2025-08-07T13:02:19Z","merge_commit_sha":"ca43b48ca2c461f9a5cb66500a154b23d07c9f90","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":9072229344,"node_id":"LA_kwDOPZ8tBM8AAAACHL874A","url":"https://api.github.com/repos/forgejo/test_repo/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"}],"milestone":{"url":"https://api.github.com/repos/forgejo/test_repo/milestones/1","html_url":"https://github.com/forgejo/test_repo/milestone/1","labels_url":"https://api.github.com/repos/forgejo/test_repo/milestones/1/labels","id":13445581,"node_id":"MI_kwDOPZ8tBM4AzSnN","number":1,"title":"1.0.0","description":"Version 1","creator":{"login":"PatDyn","id":37243484,"node_id":"MDQ6VXNlcjM3MjQzNDg0","avatar_url":"https://avatars.githubusercontent.com/u/37243484?v=4","gravatar_id":"","url":"https://api.github.com/users/PatDyn","html_url":"https://github.com/PatDyn","followers_url":"https://api.github.com/users/PatDyn/followers","following_url":"https://api.github.com/users/PatDyn/following{/other_user}","gists_url":"https://api.github.com/users/PatDyn/gists{/gist_id}","starred_url":"https://api.github.com/users/PatDyn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PatDyn/subscriptions","organizations_url":"https://api.github.com/users/PatDyn/orgs","repos_url":"https://api.github.com/users/PatDyn/repos","events_url":"https://api.github.com/users/PatDyn/events{/privacy}","received_events_url":"https://api.github.com/users/PatDyn/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":2,"state":"open","created_at":"2025-08-07T12:48:56Z","updated_at":"2025-08-12T12:34:20Z","due_on":null,"closed_at":null},"draft":false,"commits_url":"https://api.github.com/repos/forgejo/test_repo/pulls/7/commits","review_comments_url":"https://api.github.com/repos/forgejo/test_repo/pulls/7/comments","review_comment_url":"https://api.github.com/repos/forgejo/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/forgejo/test_repo/issues/7/comments","statuses_url":"https://api.github.com/repos/forgejo/test_repo/statuses/5638cb8f3278e467fc1eefcac14d3c0d5d91601f","head":{"label":"forgejo:another-feature","ref":"another-feature","sha":"5638cb8f3278e467fc1eefcac14d3c0d5d91601f","user":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":1033841924,"node_id":"R_kgDOPZ8tBA","name":"test_repo","full_name":"forgejo/test_repo","private":false,"owner":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/forgejo/test_repo","description":"Exclusively used for testing Github->Forgejo migration","fork":false,"url":"https://api.github.com/repos/forgejo/test_repo","forks_url":"https://api.github.com/repos/forgejo/test_repo/forks","keys_url":"https://api.github.com/repos/forgejo/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/forgejo/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/forgejo/test_repo/teams","hooks_url":"https://api.github.com/repos/forgejo/test_repo/hooks","issue_events_url":"https://api.github.com/repos/forgejo/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/forgejo/test_repo/events","assignees_url":"https://api.github.com/repos/forgejo/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/forgejo/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/forgejo/test_repo/tags","blobs_url":"https://api.github.com/repos/forgejo/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/forgejo/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/forgejo/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/forgejo/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/forgejo/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/forgejo/test_repo/languages","stargazers_url":"https://api.github.com/repos/forgejo/test_repo/stargazers","contributors_url":"https://api.github.com/repos/forgejo/test_repo/contributors","subscribers_url":"https://api.github.com/repos/forgejo/test_repo/subscribers","subscription_url":"https://api.github.com/repos/forgejo/test_repo/subscription","commits_url":"https://api.github.com/repos/forgejo/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/forgejo/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/forgejo/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/forgejo/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/forgejo/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/forgejo/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/forgejo/test_repo/merges","archive_url":"https://api.github.com/repos/forgejo/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/forgejo/test_repo/downloads","issues_url":"https://api.github.com/repos/forgejo/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/forgejo/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/forgejo/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/forgejo/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/forgejo/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/forgejo/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/forgejo/test_repo/deployments","created_at":"2025-08-07T12:34:21Z","updated_at":"2025-08-12T11:21:45Z","pushed_at":"2025-08-07T13:07:49Z","git_url":"git://github.com/forgejo/test_repo.git","ssh_url":"git@github.com:forgejo/test_repo.git","clone_url":"https://github.com/forgejo/test_repo.git","svn_url":"https://github.com/forgejo/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":6,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":1,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":5,"license":{"key":"0bsd","name":"BSD Zero Clause License","spdx_id":"0BSD","url":"https://api.github.com/licenses/0bsd","node_id":"MDc6TGljZW5zZTM1"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["forgejo"],"visibility":"public","forks":1,"open_issues":5,"watchers":1,"default_branch":"main"}},"base":{"label":"forgejo:main","ref":"main","sha":"6dd0c6801ddbb7333787e73e99581279492ff449","user":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":1033841924,"node_id":"R_kgDOPZ8tBA","name":"test_repo","full_name":"forgejo/test_repo","private":false,"owner":{"login":"forgejo","id":118922216,"node_id":"O_kgDOBxab6A","avatar_url":"https://avatars.githubusercontent.com/u/118922216?v=4","gravatar_id":"","url":"https://api.github.com/users/forgejo","html_url":"https://github.com/forgejo","followers_url":"https://api.github.com/users/forgejo/followers","following_url":"https://api.github.com/users/forgejo/following{/other_user}","gists_url":"https://api.github.com/users/forgejo/gists{/gist_id}","starred_url":"https://api.github.com/users/forgejo/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/forgejo/subscriptions","organizations_url":"https://api.github.com/users/forgejo/orgs","repos_url":"https://api.github.com/users/forgejo/repos","events_url":"https://api.github.com/users/forgejo/events{/privacy}","received_events_url":"https://api.github.com/users/forgejo/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/forgejo/test_repo","description":"Exclusively used for testing Github->Forgejo migration","fork":false,"url":"https://api.github.com/repos/forgejo/test_repo","forks_url":"https://api.github.com/repos/forgejo/test_repo/forks","keys_url":"https://api.github.com/repos/forgejo/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/forgejo/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/forgejo/test_repo/teams","hooks_url":"https://api.github.com/repos/forgejo/test_repo/hooks","issue_events_url":"https://api.github.com/repos/forgejo/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/forgejo/test_repo/events","assignees_url":"https://api.github.com/repos/forgejo/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/forgejo/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/forgejo/test_repo/tags","blobs_url":"https://api.github.com/repos/forgejo/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/forgejo/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/forgejo/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/forgejo/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/forgejo/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/forgejo/test_repo/languages","stargazers_url":"https://api.github.com/repos/forgejo/test_repo/stargazers","contributors_url":"https://api.github.com/repos/forgejo/test_repo/contributors","subscribers_url":"https://api.github.com/repos/forgejo/test_repo/subscribers","subscription_url":"https://api.github.com/repos/forgejo/test_repo/subscription","commits_url":"https://api.github.com/repos/forgejo/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/forgejo/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/forgejo/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/forgejo/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/forgejo/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/forgejo/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/forgejo/test_repo/merges","archive_url":"https://api.github.com/repos/forgejo/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/forgejo/test_repo/downloads","issues_url":"https://api.github.com/repos/forgejo/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/forgejo/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/forgejo/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/forgejo/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/forgejo/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/forgejo/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/forgejo/test_repo/deployments","created_at":"2025-08-07T12:34:21Z","updated_at":"2025-08-12T11:21:45Z","pushed_at":"2025-08-07T13:07:49Z","git_url":"git://github.com/forgejo/test_repo.git","ssh_url":"git@github.com:forgejo/test_repo.git","clone_url":"https://github.com/forgejo/test_repo.git","svn_url":"https://github.com/forgejo/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":6,"stargazers_count":1,"watchers_count":1,"language":null,"has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":1,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":5,"license":{"key":"0bsd","name":"BSD Zero Clause License","spdx_id":"0BSD","url":"https://api.github.com/licenses/0bsd","node_id":"MDc6TGljZW5zZTM1"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["forgejo"],"visibility":"public","forks":1,"open_issues":5,"watchers":1,"default_branch":"main"}},"_links":{"self":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/7"},"html":{"href":"https://github.com/forgejo/test_repo/pull/7"},"issue":{"href":"https://api.github.com/repos/forgejo/test_repo/issues/7"},"comments":{"href":"https://api.github.com/repos/forgejo/test_repo/issues/7/comments"},"review_comments":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/7/comments"},"review_comment":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/forgejo/test_repo/pulls/7/commits"},"statuses":{"href":"https://api.github.com/repos/forgejo/test_repo/statuses/5638cb8f3278e467fc1eefcac14d3c0d5d91601f"}},"author_association":"COLLABORATOR","auto_merge":null,"active_lock_reason":null}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 new file mode 100644 index 0000000000..5fc5f2dc94 --- /dev/null +++ b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fforgejo%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 @@ -0,0 +1,25 @@ +X-Ratelimit-Remaining: 4877 +X-Ratelimit-Used: 123 +Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset +Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin +Content-Security-Policy: default-src 'none' +Content-Type: application/json; charset=utf-8 +Cache-Control: private, max-age=60, s-maxage=60 +Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With +Github-Authentication-Token-Expiration: 2025-09-11 10:37:11 UTC +X-Ratelimit-Resource: core +Strict-Transport-Security: max-age=31536000; includeSubdomains; preload +X-Xss-Protection: 0 +Etag: W/"da1b952735d448e2474ba8dc4ba5b9c2a1989fb8bf0002dff577ae452601af43" +X-Github-Media-Type: github.v3; format=json +X-Github-Api-Version-Selected: 2022-11-28 +X-Ratelimit-Reset: 1755007969 +Access-Control-Allow-Origin: * +X-Frame-Options: deny +X-Content-Type-Options: nosniff +X-Github-Request-Id: E80A:118F3A:205E1D:1E7A62:689B401C +X-Oauth-Scopes: public_repo, repo:status +X-Accepted-Oauth-Scopes: repo +X-Ratelimit-Limit: 5000 + +[{"url":"https://api.github.com/repos/forgejo/test_repo/releases/238309022","assets_url":"https://api.github.com/repos/forgejo/test_repo/releases/238309022/assets","upload_url":"https://uploads.github.com/repos/forgejo/test_repo/releases/238309022/assets{?name,label}","html_url":"https://github.com/forgejo/test_repo/releases/tag/v1.0","id":238309022,"author":{"login":"Gusted","id":25481501,"node_id":"MDQ6VXNlcjI1NDgxNTAx","avatar_url":"https://avatars.githubusercontent.com/u/25481501?v=4","gravatar_id":"","url":"https://api.github.com/users/Gusted","html_url":"https://github.com/Gusted","followers_url":"https://api.github.com/users/Gusted/followers","following_url":"https://api.github.com/users/Gusted/following{/other_user}","gists_url":"https://api.github.com/users/Gusted/gists{/gist_id}","starred_url":"https://api.github.com/users/Gusted/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Gusted/subscriptions","organizations_url":"https://api.github.com/users/Gusted/orgs","repos_url":"https://api.github.com/users/Gusted/repos","events_url":"https://api.github.com/users/Gusted/events{/privacy}","received_events_url":"https://api.github.com/users/Gusted/received_events","type":"User","user_view_type":"public","site_admin":false},"node_id":"RE_kwDOPZ8tBM4ONE6e","tag_name":"v1.0","target_commitish":"main","name":"First Release","draft":false,"immutable":false,"prerelease":false,"created_at":"2025-08-07T13:02:19Z","updated_at":"2025-08-12T11:24:30Z","published_at":"2025-08-07T13:07:49Z","assets":[{"url":"https://api.github.com/repos/forgejo/test_repo/releases/assets/280443629","id":280443629,"node_id":"RA_kwDOPZ8tBM4Qtzrt","name":"wireguard.pdf","label":null,"uploader":{"login":"Gusted","id":25481501,"node_id":"MDQ6VXNlcjI1NDgxNTAx","avatar_url":"https://avatars.githubusercontent.com/u/25481501?v=4","gravatar_id":"","url":"https://api.github.com/users/Gusted","html_url":"https://github.com/Gusted","followers_url":"https://api.github.com/users/Gusted/followers","following_url":"https://api.github.com/users/Gusted/following{/other_user}","gists_url":"https://api.github.com/users/Gusted/gists{/gist_id}","starred_url":"https://api.github.com/users/Gusted/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Gusted/subscriptions","organizations_url":"https://api.github.com/users/Gusted/orgs","repos_url":"https://api.github.com/users/Gusted/repos","events_url":"https://api.github.com/users/Gusted/events{/privacy}","received_events_url":"https://api.github.com/users/Gusted/received_events","type":"User","user_view_type":"public","site_admin":false},"content_type":"application/pdf","state":"uploaded","size":550175,"digest":"sha256:b4cf398c21d054e8774af568395b0f7cd0e2b01322809c5dbd66c4135021ca57","download_count":0,"created_at":"2025-08-07T23:39:27Z","updated_at":"2025-08-07T23:39:29Z","browser_download_url":"https://github.com/forgejo/test_repo/releases/download/v1.0/wireguard.pdf"}],"tarball_url":"https://api.github.com/repos/forgejo/test_repo/tarball/v1.0","zipball_url":"https://api.github.com/repos/forgejo/test_repo/zipball/v1.0","body":"Hi, this is the first release! The asset contains the wireguard whitepaper, amazing read for such a simple protocol."}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo deleted file mode 100644 index 78fde4d424..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo +++ /dev/null @@ -1,25 +0,0 @@ -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Resource: core -Access-Control-Allow-Origin: * -X-Github-Request-Id: C7CC:3118FC:3F5EFD7:403585D:6729E6B3 -Cache-Control: private, max-age=60, s-maxage=60 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Media-Type: github.v3; param=scarlet-witch-preview; format=json, github.mercy-preview; param=baptiste-preview.nebula-preview; format=json -Etag: W/"bb1c9e0186e52dbd9f2c34aaf0827517384a15fd0cee7b81ad13784901db15c0" -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: repo -X-Ratelimit-Limit: 5000 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Content-Type-Options: nosniff -Content-Type: application/json; charset=utf-8 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -Last-Modified: Thu, 02 Mar 2023 14:02:26 GMT -X-Ratelimit-Remaining: 4928 -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Used: 72 -X-Frame-Options: deny -Content-Security-Policy: default-src 'none' - -{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master","permissions":{"admin":false,"maintain":false,"push":false,"triage":false,"pull":true},"temp_clone_token":"","custom_properties":{},"organization":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"network_count":6,"subscribers_count":6} diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2 deleted file mode 100644 index f1f9afee15..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=1&per_page=2 +++ /dev/null @@ -1,24 +0,0 @@ -Cache-Control: private, max-age=60, s-maxage=60 -X-Ratelimit-Remaining: 4923 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Accepted-Oauth-Scopes: repo -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Resource: core -Content-Security-Policy: default-src 'none' -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Ratelimit-Used: 77 -Access-Control-Allow-Origin: * -X-Xss-Protection: 0 -X-Github-Request-Id: C7CC:3118FC:3F5F8DA:403618E:6729E6B6 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Type: application/json; charset=utf-8 -Etag: W/"07b6d56c5fdc728f96fceef3d45d26b4ebac96ef5138156668055f7d496c9a75" -X-Oauth-Scopes: -X-Ratelimit-Limit: 5000 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Frame-Options: deny -X-Content-Type-Options: nosniff - -[{"id":55441655,"node_id":"MDEzOklzc3VlUmVhY3Rpb241NTQ0MTY1NQ==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?u=7c1ba931adbdd9bab5be1a41d244425d463568cd&v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"+1","created_at":"2019-11-12T20:22:13Z"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=2&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=2&per_page=2 deleted file mode 100644 index fe993d3c3b..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F1%2Freactions%3Fpage=2&per_page=2 +++ /dev/null @@ -1,26 +0,0 @@ -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Github-Api-Version-Selected: 2022-11-28 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Content-Type-Options: nosniff -Content-Type: application/json; charset=utf-8 -Content-Length: 2 -Cache-Control: private, max-age=60, s-maxage=60 -Link: ; rel="prev", ; rel="last", ; rel="first" -X-Ratelimit-Limit: 5000 -X-Ratelimit-Remaining: 4922 -X-Ratelimit-Reset: 1730800941 -X-Xss-Protection: 0 -X-Github-Request-Id: C7CC:3118FC:3F5FA7C:403633C:6729E6B6 -X-Oauth-Scopes: -X-Ratelimit-Used: 78 -X-Frame-Options: deny -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Accepted-Oauth-Scopes: repo -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -Content-Security-Policy: default-src 'none' - -[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created deleted file mode 100644 index 61867c5ae6..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Fcomments%3Fdirection=asc&per_page=100&sort=created +++ /dev/null @@ -1,24 +0,0 @@ -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Ratelimit-Remaining: 4917 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Content-Type: application/json; charset=utf-8 -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Used: 83 -X-Content-Type-Options: nosniff -X-Github-Request-Id: C7CC:3118FC:3F60409:4036CD0:6729E6B8 -Etag: W/"5f4d715f2578719997e324fe7b29e7eeeec048288237b44d4f320666514813ad" -X-Accepted-Oauth-Scopes: -X-Frame-Options: deny -X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -Content-Security-Policy: default-src 'none' -Cache-Control: private, max-age=60, s-maxage=60 -X-Oauth-Scopes: - -[{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments/553111966","html_url":"https://github.com/go-gitea/test_repo/issues/2#issuecomment-553111966","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/2","id":553111966,"node_id":"MDEyOklzc3VlQ29tbWVudDU1MzExMTk2Ng==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2019-11-12T21:00:13Z","updated_at":"2019-11-12T21:00:13Z","author_association":"MEMBER","body":"This is a comment","reactions":{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments/553111966/reactions","total_count":1,"+1":1,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null},{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments/553138856","html_url":"https://github.com/go-gitea/test_repo/issues/2#issuecomment-553138856","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/2","id":553138856,"node_id":"MDEyOklzc3VlQ29tbWVudDU1MzEzODg1Ng==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2019-11-12T22:07:14Z","updated_at":"2019-11-12T22:07:14Z","author_association":"MEMBER","body":"A second comment","reactions":{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments/553138856/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"performed_via_github_app":null}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 deleted file mode 100644 index bb9dea395c..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=1&per_page=2 +++ /dev/null @@ -1,25 +0,0 @@ -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Xss-Protection: 0 -X-Accepted-Oauth-Scopes: repo -Link: ; rel="next", ; rel="last" -X-Ratelimit-Remaining: 4921 -X-Ratelimit-Reset: 1730800941 -X-Github-Request-Id: C7CC:3118FC:3F5FC1A:40364D4:6729E6B6 -Content-Security-Policy: default-src 'none' -X-Oauth-Scopes: -X-Ratelimit-Used: 79 -X-Frame-Options: deny -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Api-Version-Selected: 2022-11-28 -Content-Type: application/json; charset=utf-8 -Etag: W/"27408cb5dd95878d6267de226341c84fd1d2c49695867baecf930579608e16a5" -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Content-Type-Options: nosniff -Cache-Control: private, max-age=60, s-maxage=60 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Ratelimit-Limit: 5000 -Access-Control-Allow-Origin: * - -[{"id":55445108,"node_id":"MDEzOklzc3VlUmVhY3Rpb241NTQ0NTEwOA==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?u=7c1ba931adbdd9bab5be1a41d244425d463568cd&v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"heart","created_at":"2019-11-12T21:02:05Z"},{"id":55445150,"node_id":"MDEzOklzc3VlUmVhY3Rpb241NTQ0NTE1MA==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?u=7c1ba931adbdd9bab5be1a41d244425d463568cd&v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"laugh","created_at":"2019-11-12T21:02:35Z"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=2&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=2&per_page=2 deleted file mode 100644 index e59fc93546..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=2&per_page=2 +++ /dev/null @@ -1,25 +0,0 @@ -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Resource: core -X-Frame-Options: deny -Etag: W/"cb64a4e91ab70a1ab9fe2f77cdbf7452120169f4c2cce397014efe94cc0d60bb" -Link: ; rel="prev", ; rel="next", ; rel="last", ; rel="first" -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Oauth-Scopes: -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Content-Type-Options: nosniff -Content-Security-Policy: default-src 'none' -Content-Type: application/json; charset=utf-8 -Cache-Control: private, max-age=60, s-maxage=60 -X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Request-Id: C7CC:3118FC:3F5FDBD:4036670:6729E6B7 -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -Access-Control-Allow-Origin: * -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Remaining: 4920 -X-Ratelimit-Used: 80 -X-Accepted-Oauth-Scopes: repo - -[{"id":55445169,"node_id":"MDEzOklzc3VlUmVhY3Rpb241NTQ0NTE2OQ==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?u=7c1ba931adbdd9bab5be1a41d244425d463568cd&v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"-1","created_at":"2019-11-12T21:02:47Z"},{"id":55445177,"node_id":"MDEzOklzc3VlUmVhY3Rpb241NTQ0NTE3Nw==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?u=7c1ba931adbdd9bab5be1a41d244425d463568cd&v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"confused","created_at":"2019-11-12T21:02:52Z"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=3&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=3&per_page=2 deleted file mode 100644 index 57f03c8a64..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F2%2Freactions%3Fpage=3&per_page=2 +++ /dev/null @@ -1,25 +0,0 @@ -Etag: W/"17b0dca978a885d2234548248a7d5c22264c161b73e28c5cd144e33a24dd9ed4" -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Reset: 1730800941 -X-Ratelimit-Used: 81 -Content-Security-Policy: default-src 'none' -Content-Type: application/json; charset=utf-8 -X-Oauth-Scopes: -Link: ; rel="prev", ; rel="first" -X-Ratelimit-Limit: 5000 -X-Frame-Options: deny -X-Content-Type-Options: nosniff -X-Accepted-Oauth-Scopes: repo -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Xss-Protection: 0 -Access-Control-Allow-Origin: * -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Cache-Control: private, max-age=60, s-maxage=60 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Ratelimit-Remaining: 4919 -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Github-Request-Id: C7CC:3118FC:3F5FF70:403681F:6729E6B7 - -[{"id":55445188,"node_id":"MDEzOklzc3VlUmVhY3Rpb241NTQ0NTE4OA==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?u=7c1ba931adbdd9bab5be1a41d244425d463568cd&v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"hooray","created_at":"2019-11-12T21:02:58Z"},{"id":55445441,"node_id":"MDEzOklzc3VlUmVhY3Rpb241NTQ0NTQ0MQ==","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?u=7c1ba931adbdd9bab5be1a41d244425d463568cd&v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"+1","created_at":"2019-11-12T21:06:04Z"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=1&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=1&per_page=2 deleted file mode 100644 index f5398c3a9f..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=1&per_page=2 +++ /dev/null @@ -1,24 +0,0 @@ -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -Etag: W/"dc9d10e1714eadc1507466c7d11d2dd84ae539a378835f8763b9948da44b22e1" -X-Accepted-Oauth-Scopes: repo -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -X-Frame-Options: deny -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Reset: 1730800941 -Content-Type: application/json; charset=utf-8 -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Ratelimit-Remaining: 4911 -X-Ratelimit-Used: 89 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Content-Type-Options: nosniff -X-Xss-Protection: 0 -Content-Security-Policy: default-src 'none' -X-Github-Request-Id: C7CC:3118FC:3F60D0F:40375F2:6729E6BB -Cache-Control: private, max-age=60, s-maxage=60 -X-Oauth-Scopes: -X-Ratelimit-Limit: 5000 -X-Ratelimit-Resource: core - -[{"id":59496724,"node_id":"MDEzOklzc3VlUmVhY3Rpb241OTQ5NjcyNA==","user":{"login":"lunny","id":81045,"node_id":"MDQ6VXNlcjgxMDQ1","avatar_url":"https://avatars.githubusercontent.com/u/81045?u=99b64f0ca6ef63643c7583ab87dd31c52d28e673&v=4","gravatar_id":"","url":"https://api.github.com/users/lunny","html_url":"https://github.com/lunny","followers_url":"https://api.github.com/users/lunny/followers","following_url":"https://api.github.com/users/lunny/following{/other_user}","gists_url":"https://api.github.com/users/lunny/gists{/gist_id}","starred_url":"https://api.github.com/users/lunny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lunny/subscriptions","organizations_url":"https://api.github.com/users/lunny/orgs","repos_url":"https://api.github.com/users/lunny/repos","events_url":"https://api.github.com/users/lunny/events{/privacy}","received_events_url":"https://api.github.com/users/lunny/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"heart","created_at":"2020-01-10T08:31:30Z"},{"id":59496731,"node_id":"MDEzOklzc3VlUmVhY3Rpb241OTQ5NjczMQ==","user":{"login":"lunny","id":81045,"node_id":"MDQ6VXNlcjgxMDQ1","avatar_url":"https://avatars.githubusercontent.com/u/81045?u=99b64f0ca6ef63643c7583ab87dd31c52d28e673&v=4","gravatar_id":"","url":"https://api.github.com/users/lunny","html_url":"https://github.com/lunny","followers_url":"https://api.github.com/users/lunny/followers","following_url":"https://api.github.com/users/lunny/following{/other_user}","gists_url":"https://api.github.com/users/lunny/gists{/gist_id}","starred_url":"https://api.github.com/users/lunny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lunny/subscriptions","organizations_url":"https://api.github.com/users/lunny/orgs","repos_url":"https://api.github.com/users/lunny/repos","events_url":"https://api.github.com/users/lunny/events{/privacy}","received_events_url":"https://api.github.com/users/lunny/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"+1","created_at":"2020-01-10T08:31:39Z"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=2&per_page=2 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=2&per_page=2 deleted file mode 100644 index 79b506ea55..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2F4%2Freactions%3Fpage=2&per_page=2 +++ /dev/null @@ -1,26 +0,0 @@ -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: repo -X-Ratelimit-Limit: 5000 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Content-Length: 2 -Cache-Control: private, max-age=60, s-maxage=60 -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -X-Content-Type-Options: nosniff -Content-Security-Policy: default-src 'none' -X-Ratelimit-Remaining: 4910 -X-Ratelimit-Reset: 1730800941 -X-Frame-Options: deny -Content-Type: application/json; charset=utf-8 -Link: ; rel="prev", ; rel="last", ; rel="first" -X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Request-Id: C7CC:3118FC:3F60EE6:40377B6:6729E6BB -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Ratelimit-Used: 90 -X-Ratelimit-Resource: core -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Github-Api-Version-Selected: 2022-11-28 -Access-Control-Allow-Origin: * - -[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=1&per_page=100 deleted file mode 100644 index b55068a718..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=1&per_page=100 +++ /dev/null @@ -1,24 +0,0 @@ -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Accepted-Oauth-Scopes: -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Used: 84 -X-Ratelimit-Resource: core -X-Content-Type-Options: nosniff -Cache-Control: private, max-age=60, s-maxage=60 -Etag: W/"d2410fc0792a61666c06ed757aa53a273165d4f14f7d5259095b7a4f3a959121" -X-Xss-Protection: 0 -Content-Security-Policy: default-src 'none' -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Oauth-Scopes: -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -X-Ratelimit-Remaining: 4916 -X-Frame-Options: deny -X-Github-Request-Id: C7CC:3118FC:3F60583:4036E51:6729E6B9 -Content-Type: application/json; charset=utf-8 -X-Ratelimit-Reset: 1730800941 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload - -[{"id":55446208,"node_id":"MDIwOklzc3VlQ29tbWVudFJlYWN0aW9uNTU0NDYyMDg=","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"content":"+1","created_at":"2019-11-12T21:13:22Z"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=2&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=2&per_page=100 deleted file mode 100644 index 1e46f438e5..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%2Fcomments%2F553111966%2Freactions%3Fpage=2&per_page=100 +++ /dev/null @@ -1,26 +0,0 @@ -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -Cache-Control: private, max-age=60, s-maxage=60 -X-Ratelimit-Used: 85 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Github-Request-Id: C7CC:3118FC:3F606F2:4036FB5:6729E6B9 -X-Xss-Protection: 0 -Content-Type: application/json; charset=utf-8 -Etag: "f87b0fd59e59458a9a808311324b873c6baf3fde861298a99de9986270dd4d79" -X-Oauth-Scopes: -Link: ; rel="prev", ; rel="last", ; rel="first" -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Resource: core -X-Frame-Options: deny -Content-Length: 2 -X-Accepted-Oauth-Scopes: -X-Ratelimit-Limit: 5000 -X-Ratelimit-Remaining: 4915 -X-Ratelimit-Reset: 1730800941 -Access-Control-Allow-Origin: * -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Content-Type-Options: nosniff -Content-Security-Policy: default-src 'none' - -[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all deleted file mode 100644 index 80d2f90dbc..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fissues%3Fdirection=asc&page=1&per_page=2&sort=created&state=all +++ /dev/null @@ -1,25 +0,0 @@ -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Type: application/json; charset=utf-8 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Ratelimit-Limit: 5000 -X-Ratelimit-Resource: core -Access-Control-Allow-Origin: * -X-Frame-Options: deny -X-Xss-Protection: 0 -Cache-Control: private, max-age=60, s-maxage=60 -Etag: W/"40580a89c26a3f7793cea4e59315e52e1106be32ded27b3ab822b7d7f74a1ecf" -X-Github-Media-Type: github.v3; param=squirrel-girl-preview -Link: ; rel="next", ; rel="last" -X-Ratelimit-Reset: 1730800941 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Content-Type-Options: nosniff -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: repo -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Remaining: 4924 -X-Ratelimit-Used: 76 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Content-Security-Policy: default-src 'none' -X-Github-Request-Id: C7CC:3118FC:3F5F5B9:4035E6C:6729E6B5 - -[{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/1","repository_url":"https://api.github.com/repos/go-gitea/test_repo","labels_url":"https://api.github.com/repos/go-gitea/test_repo/issues/1/labels{/name}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/1/comments","events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/1/events","html_url":"https://github.com/go-gitea/test_repo/issues/1","id":520479843,"node_id":"MDU6SXNzdWU1MjA0Nzk4NDM=","number":1,"title":"Please add an animated gif icon to the merge button","user":{"login":"guillep2k","id":18600385,"node_id":"MDQ6VXNlcjE4NjAwMzg1","avatar_url":"https://avatars.githubusercontent.com/u/18600385?v=4","gravatar_id":"","url":"https://api.github.com/users/guillep2k","html_url":"https://github.com/guillep2k","followers_url":"https://api.github.com/users/guillep2k/followers","following_url":"https://api.github.com/users/guillep2k/following{/other_user}","gists_url":"https://api.github.com/users/guillep2k/gists{/gist_id}","starred_url":"https://api.github.com/users/guillep2k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/guillep2k/subscriptions","organizations_url":"https://api.github.com/users/guillep2k/orgs","repos_url":"https://api.github.com/users/guillep2k/repos","events_url":"https://api.github.com/users/guillep2k/events{/privacy}","received_events_url":"https://api.github.com/users/guillep2k/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":1667254252,"node_id":"MDU6TGFiZWwxNjY3MjU0MjUy","url":"https://api.github.com/repos/go-gitea/test_repo/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"},{"id":1667254261,"node_id":"MDU6TGFiZWwxNjY3MjU0MjYx","url":"https://api.github.com/repos/go-gitea/test_repo/labels/good%20first%20issue","name":"good first issue","color":"7057ff","default":true,"description":"Good for newcomers"}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1","html_url":"https://github.com/go-gitea/test_repo/milestone/1","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1/labels","id":4839941,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0MQ==","number":1,"title":"1.0.0","description":"Milestone 1.0.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":1,"state":"closed","created_at":"2019-11-12T19:37:08Z","updated_at":"2019-11-12T21:56:17Z","due_on":"2019-11-11T08:00:00Z","closed_at":"2019-11-12T19:45:49Z"},"comments":0,"created_at":"2019-11-09T17:00:29Z","updated_at":"2019-11-12T20:29:53Z","closed_at":"2019-11-12T20:22:22Z","author_association":"MEMBER","active_lock_reason":null,"body":"I just want the merge button to hurt my eyes a little. 😝 ","closed_by":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"reactions":{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/1/reactions","total_count":1,"+1":1,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/go-gitea/test_repo/issues/1/timeline","performed_via_github_app":null,"state_reason":"completed"},{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/2","repository_url":"https://api.github.com/repos/go-gitea/test_repo","labels_url":"https://api.github.com/repos/go-gitea/test_repo/issues/2/labels{/name}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/2/comments","events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/2/events","html_url":"https://github.com/go-gitea/test_repo/issues/2","id":521799485,"node_id":"MDU6SXNzdWU1MjE3OTk0ODU=","number":2,"title":"Test issue","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":1667254257,"node_id":"MDU6TGFiZWwxNjY3MjU0MjU3","url":"https://api.github.com/repos/go-gitea/test_repo/labels/duplicate","name":"duplicate","color":"cfd3d7","default":true,"description":"This issue or pull request already exists"}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2","html_url":"https://github.com/go-gitea/test_repo/milestone/2","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2/labels","id":4839942,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0Mg==","number":2,"title":"1.1.0","description":"Milestone 1.1.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":0,"closed_issues":2,"state":"closed","created_at":"2019-11-12T19:37:25Z","updated_at":"2019-11-12T21:39:27Z","due_on":"2019-11-12T08:00:00Z","closed_at":"2019-11-12T19:45:46Z"},"comments":2,"created_at":"2019-11-12T21:00:06Z","updated_at":"2019-11-12T22:07:14Z","closed_at":"2019-11-12T21:01:31Z","author_association":"MEMBER","active_lock_reason":null,"body":"This is test issue 2, do not touch!","closed_by":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"reactions":{"url":"https://api.github.com/repos/go-gitea/test_repo/issues/2/reactions","total_count":6,"+1":1,"-1":1,"laugh":1,"hooray":1,"confused":1,"heart":1,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/go-gitea/test_repo/issues/2/timeline","performed_via_github_app":null,"state_reason":"completed"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 deleted file mode 100644 index f1d483aacb..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Flabels%3Fpage=1&per_page=100 +++ /dev/null @@ -1,24 +0,0 @@ -X-Ratelimit-Reset: 1730800941 -Access-Control-Allow-Origin: * -Content-Security-Policy: default-src 'none' -X-Github-Api-Version-Selected: 2022-11-28 -Etag: W/"01cc307b238564f2a086999fed53e0d5c880b8ec1d8d2256d99188ff47ff0ea0" -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Used: 74 -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Ratelimit-Limit: 5000 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Xss-Protection: 0 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -Cache-Control: private, max-age=60, s-maxage=60 -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: repo -X-Ratelimit-Remaining: 4926 -X-Frame-Options: deny -X-Content-Type-Options: nosniff -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Type: application/json; charset=utf-8 -X-Github-Request-Id: C7CC:3118FC:3F5F29C:4035B65:6729E6B4 - -[{"id":1667254252,"node_id":"MDU6TGFiZWwxNjY3MjU0MjUy","url":"https://api.github.com/repos/go-gitea/test_repo/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"},{"id":1667254254,"node_id":"MDU6TGFiZWwxNjY3MjU0MjU0","url":"https://api.github.com/repos/go-gitea/test_repo/labels/documentation","name":"documentation","color":"0075ca","default":true,"description":"Improvements or additions to documentation"},{"id":1667254257,"node_id":"MDU6TGFiZWwxNjY3MjU0MjU3","url":"https://api.github.com/repos/go-gitea/test_repo/labels/duplicate","name":"duplicate","color":"cfd3d7","default":true,"description":"This issue or pull request already exists"},{"id":1667254260,"node_id":"MDU6TGFiZWwxNjY3MjU0MjYw","url":"https://api.github.com/repos/go-gitea/test_repo/labels/enhancement","name":"enhancement","color":"a2eeef","default":true,"description":"New feature or request"},{"id":1667254261,"node_id":"MDU6TGFiZWwxNjY3MjU0MjYx","url":"https://api.github.com/repos/go-gitea/test_repo/labels/good%20first%20issue","name":"good first issue","color":"7057ff","default":true,"description":"Good for newcomers"},{"id":1667254265,"node_id":"MDU6TGFiZWwxNjY3MjU0MjY1","url":"https://api.github.com/repos/go-gitea/test_repo/labels/help%20wanted","name":"help wanted","color":"008672","default":true,"description":"Extra attention is needed"},{"id":1667254269,"node_id":"MDU6TGFiZWwxNjY3MjU0MjY5","url":"https://api.github.com/repos/go-gitea/test_repo/labels/invalid","name":"invalid","color":"e4e669","default":true,"description":"This doesn't seem right"},{"id":1667254273,"node_id":"MDU6TGFiZWwxNjY3MjU0Mjcz","url":"https://api.github.com/repos/go-gitea/test_repo/labels/question","name":"question","color":"d876e3","default":true,"description":"Further information is requested"},{"id":1667254276,"node_id":"MDU6TGFiZWwxNjY3MjU0Mjc2","url":"https://api.github.com/repos/go-gitea/test_repo/labels/wontfix","name":"wontfix","color":"ffffff","default":true,"description":"This will not be worked on"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all deleted file mode 100644 index 50b90ad4ab..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fmilestones%3Fpage=1&per_page=100&state=all +++ /dev/null @@ -1,24 +0,0 @@ -X-Ratelimit-Remaining: 4927 -X-Ratelimit-Used: 73 -X-Frame-Options: deny -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: repo -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Content-Type-Options: nosniff -Content-Type: application/json; charset=utf-8 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Resource: core -X-Github-Request-Id: C7CC:3118FC:3F5F16C:40359F7:6729E6B4 -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Reset: 1730800941 -Content-Security-Policy: default-src 'none' -X-Github-Api-Version-Selected: 2022-11-28 -X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Cache-Control: private, max-age=60, s-maxage=60 -Etag: W/"d6d673f0622636217ee3df16cdabbfea8402d3e8d1abbabe007108267b01f3e9" - -[{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1","html_url":"https://github.com/go-gitea/test_repo/milestone/1","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1/labels","id":4839941,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0MQ==","number":1,"title":"1.0.0","description":"Milestone 1.0.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":1,"state":"closed","created_at":"2019-11-12T19:37:08Z","updated_at":"2019-11-12T21:56:17Z","due_on":"2019-11-11T08:00:00Z","closed_at":"2019-11-12T19:45:49Z"},{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2","html_url":"https://github.com/go-gitea/test_repo/milestone/2","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2/labels","id":4839942,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0Mg==","number":2,"title":"1.1.0","description":"Milestone 1.1.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":0,"closed_issues":2,"state":"closed","created_at":"2019-11-12T19:37:25Z","updated_at":"2019-11-12T21:39:27Z","due_on":"2019-11-12T08:00:00Z","closed_at":"2019-11-12T19:45:46Z"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315859956%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315859956%2Fcomments%3Fper_page=100 deleted file mode 100644 index 435e1a0ee0..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%2F315859956%2Fcomments%3Fper_page=100 +++ /dev/null @@ -1,25 +0,0 @@ -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Used: 92 -X-Frame-Options: deny -X-Content-Type-Options: nosniff -Content-Length: 2 -X-Oauth-Scopes: -X-Ratelimit-Remaining: 4908 -X-Ratelimit-Reset: 1730800941 -X-Github-Request-Id: C7CC:3118FC:3F612D6:4037BAC:6729E6BC -Etag: "450a1c087fec81e5b86092ff5372c3db8ca834c1e23c03c6b06ecca33cefd665" -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -Cache-Control: private, max-age=60, s-maxage=60 -X-Accepted-Oauth-Scopes: -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Security-Policy: default-src 'none' -Content-Type: application/json; charset=utf-8 - -[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 deleted file mode 100644 index 203c363ffa..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F3%2Freviews%3Fper_page=100 +++ /dev/null @@ -1,24 +0,0 @@ -Cache-Control: private, max-age=60, s-maxage=60 -Etag: W/"e38ac3d6f3e77a469f9836bfa52a0b756b9ac8fdc4347530e1cb1072bbb77b46" -X-Github-Media-Type: github.v3; format=json -X-Frame-Options: deny -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Access-Control-Allow-Origin: * -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Accepted-Oauth-Scopes: -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Remaining: 4909 -X-Ratelimit-Reset: 1730800941 -Content-Security-Policy: default-src 'none' -X-Github-Request-Id: C7CC:3118FC:3F6108F:403797A:6729E6BC -Content-Type: application/json; charset=utf-8 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Resource: core -X-Content-Type-Options: nosniff -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Oauth-Scopes: -X-Ratelimit-Used: 91 -X-Xss-Protection: 0 - -[{"id":315859956,"node_id":"MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzE1ODU5OTU2","user":{"login":"jolheiser","id":42128690,"node_id":"MDQ6VXNlcjQyMTI4Njkw","avatar_url":"https://avatars.githubusercontent.com/u/42128690?u=0ee1052506846129445fa12a76cd9ad9d305de71&v=4","gravatar_id":"","url":"https://api.github.com/users/jolheiser","html_url":"https://github.com/jolheiser","followers_url":"https://api.github.com/users/jolheiser/followers","following_url":"https://api.github.com/users/jolheiser/following{/other_user}","gists_url":"https://api.github.com/users/jolheiser/gists{/gist_id}","starred_url":"https://api.github.com/users/jolheiser/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jolheiser/subscriptions","organizations_url":"https://api.github.com/users/jolheiser/orgs","repos_url":"https://api.github.com/users/jolheiser/repos","events_url":"https://api.github.com/users/jolheiser/events{/privacy}","received_events_url":"https://api.github.com/users/jolheiser/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"","state":"APPROVED","html_url":"https://github.com/go-gitea/test_repo/pull/3#pullrequestreview-315859956","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3","author_association":"MEMBER","_links":{"html":{"href":"https://github.com/go-gitea/test_repo/pull/3#pullrequestreview-315859956"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3"}},"submitted_at":"2019-11-12T21:35:24Z","commit_id":"076160cf0b039f13e5eff19619932d181269414b"},{"id":315860062,"node_id":"MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzE1ODYwMDYy","user":{"login":"zeripath","id":1824502,"node_id":"MDQ6VXNlcjE4MjQ1MDI=","avatar_url":"https://avatars.githubusercontent.com/u/1824502?u=fcd8a9dba8714edf6ac3f87596eb72149911c720&v=4","gravatar_id":"","url":"https://api.github.com/users/zeripath","html_url":"https://github.com/zeripath","followers_url":"https://api.github.com/users/zeripath/followers","following_url":"https://api.github.com/users/zeripath/following{/other_user}","gists_url":"https://api.github.com/users/zeripath/gists{/gist_id}","starred_url":"https://api.github.com/users/zeripath/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zeripath/subscriptions","organizations_url":"https://api.github.com/users/zeripath/orgs","repos_url":"https://api.github.com/users/zeripath/repos","events_url":"https://api.github.com/users/zeripath/events{/privacy}","received_events_url":"https://api.github.com/users/zeripath/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"","state":"APPROVED","html_url":"https://github.com/go-gitea/test_repo/pull/3#pullrequestreview-315860062","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3","author_association":"NONE","_links":{"html":{"href":"https://github.com/go-gitea/test_repo/pull/3#pullrequestreview-315860062"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3"}},"submitted_at":"2019-11-12T21:35:36Z","commit_id":"076160cf0b039f13e5eff19619932d181269414b"},{"id":315861440,"node_id":"MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzE1ODYxNDQw","user":{"login":"lafriks","id":165205,"node_id":"MDQ6VXNlcjE2NTIwNQ==","avatar_url":"https://avatars.githubusercontent.com/u/165205?u=efe2335d2197f524c25caa7abdfcb90b77eb8d98&v=4","gravatar_id":"","url":"https://api.github.com/users/lafriks","html_url":"https://github.com/lafriks","followers_url":"https://api.github.com/users/lafriks/followers","following_url":"https://api.github.com/users/lafriks/following{/other_user}","gists_url":"https://api.github.com/users/lafriks/gists{/gist_id}","starred_url":"https://api.github.com/users/lafriks/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lafriks/subscriptions","organizations_url":"https://api.github.com/users/lafriks/orgs","repos_url":"https://api.github.com/users/lafriks/repos","events_url":"https://api.github.com/users/lafriks/events{/privacy}","received_events_url":"https://api.github.com/users/lafriks/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"","state":"APPROVED","html_url":"https://github.com/go-gitea/test_repo/pull/3#pullrequestreview-315861440","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3","author_association":"MEMBER","_links":{"html":{"href":"https://github.com/go-gitea/test_repo/pull/3#pullrequestreview-315861440"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3"}},"submitted_at":"2019-11-12T21:38:00Z","commit_id":"076160cf0b039f13e5eff19619932d181269414b"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338338740%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338338740%2Fcomments%3Fper_page=100 deleted file mode 100644 index 48e5b2c3d9..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338338740%2Fcomments%3Fper_page=100 +++ /dev/null @@ -1,24 +0,0 @@ -Access-Control-Allow-Origin: * -X-Content-Type-Options: nosniff -Content-Security-Policy: default-src 'none' -X-Ratelimit-Resource: core -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Accepted-Oauth-Scopes: -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Remaining: 4903 -X-Ratelimit-Reset: 1730800941 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Frame-Options: deny -X-Github-Request-Id: C7CC:3118FC:3F61BAA:40384A5:6729E6BE -Cache-Control: private, max-age=60, s-maxage=60 -Etag: W/"ff77892df2ec7f6eb61416e0f384ce0a6e8fbbbc1287f5d09b1980ebe9856750" -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Limit: 5000 -X-Ratelimit-Used: 97 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Type: application/json; charset=utf-8 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Xss-Protection: 0 -X-Oauth-Scopes: - -[{"id":363017488,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDM2MzAxNzQ4OA==","url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments/363017488","pull_request_review_id":338338740,"diff_hunk":"@@ -1,2 +1,4 @@\n # test_repo\n Test repository for testing migration from github to gitea\n+","path":"README.md","position":3,"original_position":3,"commit_id":"2be9101c543658591222acbee3eb799edfc3853d","user":{"login":"lunny","id":81045,"node_id":"MDQ6VXNlcjgxMDQ1","avatar_url":"https://avatars.githubusercontent.com/u/81045?u=99b64f0ca6ef63643c7583ab87dd31c52d28e673&v=4","gravatar_id":"","url":"https://api.github.com/users/lunny","html_url":"https://github.com/lunny","followers_url":"https://api.github.com/users/lunny/followers","following_url":"https://api.github.com/users/lunny/following{/other_user}","gists_url":"https://api.github.com/users/lunny/gists{/gist_id}","starred_url":"https://api.github.com/users/lunny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lunny/subscriptions","organizations_url":"https://api.github.com/users/lunny/orgs","repos_url":"https://api.github.com/users/lunny/repos","events_url":"https://api.github.com/users/lunny/events{/privacy}","received_events_url":"https://api.github.com/users/lunny/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"This is a good pull request.","created_at":"2020-01-04T05:33:06Z","updated_at":"2020-01-04T05:33:18Z","html_url":"https://github.com/go-gitea/test_repo/pull/4#discussion_r363017488","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments/363017488"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/4#discussion_r363017488"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"}},"original_commit_id":"2be9101c543658591222acbee3eb799edfc3853d","reactions":{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments/363017488/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0}}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338339651%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338339651%2Fcomments%3Fper_page=100 deleted file mode 100644 index 4cc66424f0..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338339651%2Fcomments%3Fper_page=100 +++ /dev/null @@ -1,25 +0,0 @@ -X-Github-Api-Version-Selected: 2022-11-28 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Frame-Options: deny -X-Content-Type-Options: nosniff -Cache-Control: private, max-age=60, s-maxage=60 -Etag: "450a1c087fec81e5b86092ff5372c3db8ca834c1e23c03c6b06ecca33cefd665" -X-Github-Media-Type: github.v3; format=json -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Ratelimit-Reset: 1730800941 -X-Xss-Protection: 0 -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: -X-Ratelimit-Limit: 5000 -Access-Control-Allow-Origin: * -Content-Length: 2 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Ratelimit-Resource: core -Content-Security-Policy: default-src 'none' -X-Github-Request-Id: C7CC:3118FC:3F61EBA:40387A6:6729E6BF -Content-Type: application/json; charset=utf-8 -X-Ratelimit-Remaining: 4901 -X-Ratelimit-Used: 99 - -[] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338349019%2Fcomments%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338349019%2Fcomments%3Fper_page=100 deleted file mode 100644 index f13d4addc7..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%2F338349019%2Fcomments%3Fper_page=100 +++ /dev/null @@ -1,24 +0,0 @@ -Etag: W/"f74a383d4c87ae26d218fc087bef2c41a12637dff81fd8642f59708adca1a14e" -X-Ratelimit-Remaining: 4900 -X-Content-Type-Options: nosniff -X-Ratelimit-Limit: 5000 -X-Ratelimit-Reset: 1730800941 -X-Xss-Protection: 0 -Content-Type: application/json; charset=utf-8 -X-Oauth-Scopes: -X-Accepted-Oauth-Scopes: -X-Github-Api-Version-Selected: 2022-11-28 -Access-Control-Allow-Origin: * -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Frame-Options: deny -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Used: 100 -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Github-Request-Id: C7CC:3118FC:3F62065:4038955:6729E6C0 -Content-Security-Policy: default-src 'none' -Cache-Control: private, max-age=60, s-maxage=60 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin - -[{"id":363029944,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDM2MzAyOTk0NA==","url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments/363029944","pull_request_review_id":338349019,"diff_hunk":"@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n+","path":"LICENSE","position":4,"original_position":4,"commit_id":"2be9101c543658591222acbee3eb799edfc3853d","user":{"login":"lunny","id":81045,"node_id":"MDQ6VXNlcjgxMDQ1","avatar_url":"https://avatars.githubusercontent.com/u/81045?u=99b64f0ca6ef63643c7583ab87dd31c52d28e673&v=4","gravatar_id":"","url":"https://api.github.com/users/lunny","html_url":"https://github.com/lunny","followers_url":"https://api.github.com/users/lunny/followers","following_url":"https://api.github.com/users/lunny/following{/other_user}","gists_url":"https://api.github.com/users/lunny/gists{/gist_id}","starred_url":"https://api.github.com/users/lunny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lunny/subscriptions","organizations_url":"https://api.github.com/users/lunny/orgs","repos_url":"https://api.github.com/users/lunny/repos","events_url":"https://api.github.com/users/lunny/events{/privacy}","received_events_url":"https://api.github.com/users/lunny/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"test a single comment.","created_at":"2020-01-04T11:21:41Z","updated_at":"2020-01-04T11:21:41Z","html_url":"https://github.com/go-gitea/test_repo/pull/4#discussion_r363029944","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments/363029944"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/4#discussion_r363029944"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"}},"original_commit_id":"2be9101c543658591222acbee3eb799edfc3853d","reactions":{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments/363029944/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0}}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100 deleted file mode 100644 index c4484e078a..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%2F4%2Freviews%3Fper_page=100 +++ /dev/null @@ -1,24 +0,0 @@ -Cache-Control: private, max-age=60, s-maxage=60 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -X-Xss-Protection: 0 -Content-Security-Policy: default-src 'none' -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Github-Media-Type: github.v3; format=json -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Reset: 1730800941 -X-Frame-Options: deny -X-Github-Request-Id: C7CC:3118FC:3F619C6:40382C4:6729E6BE -Access-Control-Allow-Origin: * -X-Content-Type-Options: nosniff -Content-Type: application/json; charset=utf-8 -X-Accepted-Oauth-Scopes: -X-Ratelimit-Used: 96 -X-Ratelimit-Resource: core -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Etag: W/"a47623061be83c50d3932baf8c5961386d2d45c32c42b504122c9a1359efd515" -X-Oauth-Scopes: -X-Ratelimit-Limit: 5000 -X-Ratelimit-Remaining: 4904 - -[{"id":338338740,"node_id":"MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzM4MzM4NzQw","user":{"login":"lunny","id":81045,"node_id":"MDQ6VXNlcjgxMDQ1","avatar_url":"https://avatars.githubusercontent.com/u/81045?u=99b64f0ca6ef63643c7583ab87dd31c52d28e673&v=4","gravatar_id":"","url":"https://api.github.com/users/lunny","html_url":"https://github.com/lunny","followers_url":"https://api.github.com/users/lunny/followers","following_url":"https://api.github.com/users/lunny/following{/other_user}","gists_url":"https://api.github.com/users/lunny/gists{/gist_id}","starred_url":"https://api.github.com/users/lunny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lunny/subscriptions","organizations_url":"https://api.github.com/users/lunny/orgs","repos_url":"https://api.github.com/users/lunny/repos","events_url":"https://api.github.com/users/lunny/events{/privacy}","received_events_url":"https://api.github.com/users/lunny/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"","state":"APPROVED","html_url":"https://github.com/go-gitea/test_repo/pull/4#pullrequestreview-338338740","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","author_association":"MEMBER","_links":{"html":{"href":"https://github.com/go-gitea/test_repo/pull/4#pullrequestreview-338338740"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"}},"submitted_at":"2020-01-04T05:33:18Z","commit_id":"2be9101c543658591222acbee3eb799edfc3853d"},{"id":338339651,"node_id":"MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzM4MzM5NjUx","user":{"login":"lunny","id":81045,"node_id":"MDQ6VXNlcjgxMDQ1","avatar_url":"https://avatars.githubusercontent.com/u/81045?u=99b64f0ca6ef63643c7583ab87dd31c52d28e673&v=4","gravatar_id":"","url":"https://api.github.com/users/lunny","html_url":"https://github.com/lunny","followers_url":"https://api.github.com/users/lunny/followers","following_url":"https://api.github.com/users/lunny/following{/other_user}","gists_url":"https://api.github.com/users/lunny/gists{/gist_id}","starred_url":"https://api.github.com/users/lunny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lunny/subscriptions","organizations_url":"https://api.github.com/users/lunny/orgs","repos_url":"https://api.github.com/users/lunny/repos","events_url":"https://api.github.com/users/lunny/events{/privacy}","received_events_url":"https://api.github.com/users/lunny/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Don't add more reviews","state":"CHANGES_REQUESTED","html_url":"https://github.com/go-gitea/test_repo/pull/4#pullrequestreview-338339651","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","author_association":"MEMBER","_links":{"html":{"href":"https://github.com/go-gitea/test_repo/pull/4#pullrequestreview-338339651"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"}},"submitted_at":"2020-01-04T06:07:06Z","commit_id":"2be9101c543658591222acbee3eb799edfc3853d"},{"id":338349019,"node_id":"MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzM4MzQ5MDE5","user":{"login":"lunny","id":81045,"node_id":"MDQ6VXNlcjgxMDQ1","avatar_url":"https://avatars.githubusercontent.com/u/81045?u=99b64f0ca6ef63643c7583ab87dd31c52d28e673&v=4","gravatar_id":"","url":"https://api.github.com/users/lunny","html_url":"https://github.com/lunny","followers_url":"https://api.github.com/users/lunny/followers","following_url":"https://api.github.com/users/lunny/following{/other_user}","gists_url":"https://api.github.com/users/lunny/gists{/gist_id}","starred_url":"https://api.github.com/users/lunny/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lunny/subscriptions","organizations_url":"https://api.github.com/users/lunny/orgs","repos_url":"https://api.github.com/users/lunny/repos","events_url":"https://api.github.com/users/lunny/events{/privacy}","received_events_url":"https://api.github.com/users/lunny/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"","state":"COMMENTED","html_url":"https://github.com/go-gitea/test_repo/pull/4#pullrequestreview-338349019","pull_request_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","author_association":"MEMBER","_links":{"html":{"href":"https://github.com/go-gitea/test_repo/pull/4#pullrequestreview-338349019"},"pull_request":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"}},"submitted_at":"2020-01-04T11:21:41Z","commit_id":"2be9101c543658591222acbee3eb799edfc3853d"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all deleted file mode 100644 index 30883cc283..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Fpulls%3Fdirection=asc&page=1&per_page=2&sort=created&state=all +++ /dev/null @@ -1,24 +0,0 @@ -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Used: 87 -X-Frame-Options: deny -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Remaining: 4913 -X-Ratelimit-Reset: 1730800941 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Security-Policy: default-src 'none' -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Oauth-Scopes: -X-Ratelimit-Limit: 5000 -X-Ratelimit-Resource: core -Access-Control-Allow-Origin: * -X-Github-Request-Id: C7CC:3118FC:3F6099B:403726B:6729E6BA -X-Xss-Protection: 0 -Content-Type: application/json; charset=utf-8 -Cache-Control: private, max-age=60, s-maxage=60 -Etag: W/"fed37ab8ce0b78e713030ff3e601f540b7f71243ab788b477f6885119bd691c5" -X-Accepted-Oauth-Scopes: -X-Content-Type-Options: nosniff - -[{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3","id":340118745,"node_id":"MDExOlB1bGxSZXF1ZXN0MzQwMTE4NzQ1","html_url":"https://github.com/go-gitea/test_repo/pull/3","diff_url":"https://github.com/go-gitea/test_repo/pull/3.diff","patch_url":"https://github.com/go-gitea/test_repo/pull/3.patch","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/3","number":3,"state":"closed","locked":false,"title":"Update README.md","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"add warning to readme","created_at":"2019-11-12T21:21:43Z","updated_at":"2019-11-12T21:39:28Z","closed_at":"2019-11-12T21:39:27Z","merged_at":"2019-11-12T21:39:27Z","merge_commit_sha":"f32b0a9dfd09a60f616f29158f772cedd89942d2","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":1667254254,"node_id":"MDU6TGFiZWwxNjY3MjU0MjU0","url":"https://api.github.com/repos/go-gitea/test_repo/labels/documentation","name":"documentation","color":"0075ca","default":true,"description":"Improvements or additions to documentation"}],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2","html_url":"https://github.com/go-gitea/test_repo/milestone/2","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/2/labels","id":4839942,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0Mg==","number":2,"title":"1.1.0","description":"Milestone 1.1.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":0,"closed_issues":2,"state":"closed","created_at":"2019-11-12T19:37:25Z","updated_at":"2019-11-12T21:39:27Z","due_on":"2019-11-12T08:00:00Z","closed_at":"2019-11-12T19:45:46Z"},"draft":false,"commits_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/commits","review_comments_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/comments","review_comment_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/3/comments","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/076160cf0b039f13e5eff19619932d181269414b","head":{"label":"mrsdizzie:master","ref":"master","sha":"076160cf0b039f13e5eff19619932d181269414b","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"repo":{"id":221313794,"node_id":"MDEwOlJlcG9zaXRvcnkyMjEzMTM3OTQ=","name":"test_repo","full_name":"mrsdizzie/test_repo","private":false,"owner":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"html_url":"https://github.com/mrsdizzie/test_repo","description":"Test repository for testing migration from github to gitea","fork":true,"url":"https://api.github.com/repos/mrsdizzie/test_repo","forks_url":"https://api.github.com/repos/mrsdizzie/test_repo/forks","keys_url":"https://api.github.com/repos/mrsdizzie/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mrsdizzie/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mrsdizzie/test_repo/teams","hooks_url":"https://api.github.com/repos/mrsdizzie/test_repo/hooks","issue_events_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/mrsdizzie/test_repo/events","assignees_url":"https://api.github.com/repos/mrsdizzie/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/mrsdizzie/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/tags","blobs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mrsdizzie/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/mrsdizzie/test_repo/languages","stargazers_url":"https://api.github.com/repos/mrsdizzie/test_repo/stargazers","contributors_url":"https://api.github.com/repos/mrsdizzie/test_repo/contributors","subscribers_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscribers","subscription_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscription","commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/mrsdizzie/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/mrsdizzie/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/mrsdizzie/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mrsdizzie/test_repo/merges","archive_url":"https://api.github.com/repos/mrsdizzie/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mrsdizzie/test_repo/downloads","issues_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/mrsdizzie/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/mrsdizzie/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/mrsdizzie/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mrsdizzie/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/mrsdizzie/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/mrsdizzie/test_repo/deployments","created_at":"2019-11-12T21:17:42Z","updated_at":"2019-11-12T21:18:46Z","pushed_at":"2019-11-12T21:53:39Z","git_url":"git://github.com/mrsdizzie/test_repo.git","ssh_url":"git@github.com:mrsdizzie/test_repo.git","clone_url":"https://github.com/mrsdizzie/test_repo.git","svn_url":"https://github.com/mrsdizzie/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":3,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"go-gitea:master","ref":"master","sha":"72866af952e98d02a73003501836074b286a78f6","user":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/3"},"issue":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/3"},"comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/3/comments"},"review_comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/comments"},"review_comment":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/3/commits"},"statuses":{"href":"https://api.github.com/repos/go-gitea/test_repo/statuses/076160cf0b039f13e5eff19619932d181269414b"}},"author_association":"MEMBER","auto_merge":null,"active_lock_reason":null},{"url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4","id":340131577,"node_id":"MDExOlB1bGxSZXF1ZXN0MzQwMTMxNTc3","html_url":"https://github.com/go-gitea/test_repo/pull/4","diff_url":"https://github.com/go-gitea/test_repo/pull/4.diff","patch_url":"https://github.com/go-gitea/test_repo/pull/4.patch","issue_url":"https://api.github.com/repos/go-gitea/test_repo/issues/4","number":4,"state":"open","locked":false,"title":"Test branch","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"do not merge this PR","created_at":"2019-11-12T21:54:18Z","updated_at":"2020-01-04T11:30:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"565d1208f5fffdc1c5ae1a2436491eb9a5e4ebae","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":1667254252,"node_id":"MDU6TGFiZWwxNjY3MjU0MjUy","url":"https://api.github.com/repos/go-gitea/test_repo/labels/bug","name":"bug","color":"d73a4a","default":true,"description":"Something isn't working"}],"milestone":{"url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1","html_url":"https://github.com/go-gitea/test_repo/milestone/1","labels_url":"https://api.github.com/repos/go-gitea/test_repo/milestones/1/labels","id":4839941,"node_id":"MDk6TWlsZXN0b25lNDgzOTk0MQ==","number":1,"title":"1.0.0","description":"Milestone 1.0.0","creator":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":1,"closed_issues":1,"state":"closed","created_at":"2019-11-12T19:37:08Z","updated_at":"2019-11-12T21:56:17Z","due_on":"2019-11-11T08:00:00Z","closed_at":"2019-11-12T19:45:49Z"},"draft":false,"commits_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/commits","review_comments_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/comments","review_comment_url":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/issues/4/comments","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/2be9101c543658591222acbee3eb799edfc3853d","head":{"label":"mrsdizzie:test-branch","ref":"test-branch","sha":"2be9101c543658591222acbee3eb799edfc3853d","user":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"repo":{"id":221313794,"node_id":"MDEwOlJlcG9zaXRvcnkyMjEzMTM3OTQ=","name":"test_repo","full_name":"mrsdizzie/test_repo","private":false,"owner":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"html_url":"https://github.com/mrsdizzie/test_repo","description":"Test repository for testing migration from github to gitea","fork":true,"url":"https://api.github.com/repos/mrsdizzie/test_repo","forks_url":"https://api.github.com/repos/mrsdizzie/test_repo/forks","keys_url":"https://api.github.com/repos/mrsdizzie/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mrsdizzie/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mrsdizzie/test_repo/teams","hooks_url":"https://api.github.com/repos/mrsdizzie/test_repo/hooks","issue_events_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/mrsdizzie/test_repo/events","assignees_url":"https://api.github.com/repos/mrsdizzie/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/mrsdizzie/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/tags","blobs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mrsdizzie/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/mrsdizzie/test_repo/languages","stargazers_url":"https://api.github.com/repos/mrsdizzie/test_repo/stargazers","contributors_url":"https://api.github.com/repos/mrsdizzie/test_repo/contributors","subscribers_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscribers","subscription_url":"https://api.github.com/repos/mrsdizzie/test_repo/subscription","commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/mrsdizzie/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/mrsdizzie/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/mrsdizzie/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/mrsdizzie/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mrsdizzie/test_repo/merges","archive_url":"https://api.github.com/repos/mrsdizzie/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mrsdizzie/test_repo/downloads","issues_url":"https://api.github.com/repos/mrsdizzie/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/mrsdizzie/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/mrsdizzie/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/mrsdizzie/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mrsdizzie/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/mrsdizzie/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/mrsdizzie/test_repo/deployments","created_at":"2019-11-12T21:17:42Z","updated_at":"2019-11-12T21:18:46Z","pushed_at":"2019-11-12T21:53:39Z","git_url":"git://github.com/mrsdizzie/test_repo.git","ssh_url":"git@github.com:mrsdizzie/test_repo.git","clone_url":"https://github.com/mrsdizzie/test_repo.git","svn_url":"https://github.com/mrsdizzie/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":3,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":0,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":0,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":[],"visibility":"public","forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"go-gitea:master","ref":"master","sha":"f32b0a9dfd09a60f616f29158f772cedd89942d2","user":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"repo":{"id":220672974,"node_id":"MDEwOlJlcG9zaXRvcnkyMjA2NzI5NzQ=","name":"test_repo","full_name":"go-gitea/test_repo","private":false,"owner":{"login":"go-gitea","id":12724356,"node_id":"MDEyOk9yZ2FuaXphdGlvbjEyNzI0MzU2","avatar_url":"https://avatars.githubusercontent.com/u/12724356?v=4","gravatar_id":"","url":"https://api.github.com/users/go-gitea","html_url":"https://github.com/go-gitea","followers_url":"https://api.github.com/users/go-gitea/followers","following_url":"https://api.github.com/users/go-gitea/following{/other_user}","gists_url":"https://api.github.com/users/go-gitea/gists{/gist_id}","starred_url":"https://api.github.com/users/go-gitea/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/go-gitea/subscriptions","organizations_url":"https://api.github.com/users/go-gitea/orgs","repos_url":"https://api.github.com/users/go-gitea/repos","events_url":"https://api.github.com/users/go-gitea/events{/privacy}","received_events_url":"https://api.github.com/users/go-gitea/received_events","type":"Organization","user_view_type":"public","site_admin":false},"html_url":"https://github.com/go-gitea/test_repo","description":"Test repository for testing migration from github to gitea","fork":false,"url":"https://api.github.com/repos/go-gitea/test_repo","forks_url":"https://api.github.com/repos/go-gitea/test_repo/forks","keys_url":"https://api.github.com/repos/go-gitea/test_repo/keys{/key_id}","collaborators_url":"https://api.github.com/repos/go-gitea/test_repo/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/go-gitea/test_repo/teams","hooks_url":"https://api.github.com/repos/go-gitea/test_repo/hooks","issue_events_url":"https://api.github.com/repos/go-gitea/test_repo/issues/events{/number}","events_url":"https://api.github.com/repos/go-gitea/test_repo/events","assignees_url":"https://api.github.com/repos/go-gitea/test_repo/assignees{/user}","branches_url":"https://api.github.com/repos/go-gitea/test_repo/branches{/branch}","tags_url":"https://api.github.com/repos/go-gitea/test_repo/tags","blobs_url":"https://api.github.com/repos/go-gitea/test_repo/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/go-gitea/test_repo/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/go-gitea/test_repo/git/refs{/sha}","trees_url":"https://api.github.com/repos/go-gitea/test_repo/git/trees{/sha}","statuses_url":"https://api.github.com/repos/go-gitea/test_repo/statuses/{sha}","languages_url":"https://api.github.com/repos/go-gitea/test_repo/languages","stargazers_url":"https://api.github.com/repos/go-gitea/test_repo/stargazers","contributors_url":"https://api.github.com/repos/go-gitea/test_repo/contributors","subscribers_url":"https://api.github.com/repos/go-gitea/test_repo/subscribers","subscription_url":"https://api.github.com/repos/go-gitea/test_repo/subscription","commits_url":"https://api.github.com/repos/go-gitea/test_repo/commits{/sha}","git_commits_url":"https://api.github.com/repos/go-gitea/test_repo/git/commits{/sha}","comments_url":"https://api.github.com/repos/go-gitea/test_repo/comments{/number}","issue_comment_url":"https://api.github.com/repos/go-gitea/test_repo/issues/comments{/number}","contents_url":"https://api.github.com/repos/go-gitea/test_repo/contents/{+path}","compare_url":"https://api.github.com/repos/go-gitea/test_repo/compare/{base}...{head}","merges_url":"https://api.github.com/repos/go-gitea/test_repo/merges","archive_url":"https://api.github.com/repos/go-gitea/test_repo/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/go-gitea/test_repo/downloads","issues_url":"https://api.github.com/repos/go-gitea/test_repo/issues{/number}","pulls_url":"https://api.github.com/repos/go-gitea/test_repo/pulls{/number}","milestones_url":"https://api.github.com/repos/go-gitea/test_repo/milestones{/number}","notifications_url":"https://api.github.com/repos/go-gitea/test_repo/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/go-gitea/test_repo/labels{/name}","releases_url":"https://api.github.com/repos/go-gitea/test_repo/releases{/id}","deployments_url":"https://api.github.com/repos/go-gitea/test_repo/deployments","created_at":"2019-11-09T16:49:20Z","updated_at":"2023-03-02T14:02:26Z","pushed_at":"2019-11-12T21:54:19Z","git_url":"git://github.com/go-gitea/test_repo.git","ssh_url":"git@github.com:go-gitea/test_repo.git","clone_url":"https://github.com/go-gitea/test_repo.git","svn_url":"https://github.com/go-gitea/test_repo","homepage":"https://codeberg.org/forgejo/forgejo/","size":1,"stargazers_count":3,"watchers_count":3,"language":null,"has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"has_discussions":false,"forks_count":6,"mirror_url":null,"archived":false,"disabled":false,"open_issues_count":2,"license":{"key":"mit","name":"MIT License","spdx_id":"MIT","url":"https://api.github.com/licenses/mit","node_id":"MDc6TGljZW5zZTEz"},"allow_forking":true,"is_template":false,"web_commit_signoff_required":false,"topics":["gitea"],"visibility":"public","forks":6,"open_issues":2,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4"},"html":{"href":"https://github.com/go-gitea/test_repo/pull/4"},"issue":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/4"},"comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/issues/4/comments"},"review_comments":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/comments"},"review_comment":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/go-gitea/test_repo/pulls/4/commits"},"statuses":{"href":"https://api.github.com/repos/go-gitea/test_repo/statuses/2be9101c543658591222acbee3eb799edfc3853d"}},"author_association":"MEMBER","auto_merge":null,"active_lock_reason":null}] diff --git a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 b/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 deleted file mode 100644 index 470f5c5769..0000000000 --- a/services/migrations/testdata/github/full_download/GET_%2Frepos%2Fgo-gitea%2Ftest_repo%2Freleases%3Fpage=1&per_page=100 +++ /dev/null @@ -1,24 +0,0 @@ -Access-Control-Allow-Origin: * -X-Frame-Options: deny -Etag: W/"2986c85fcc06cc478457abb86a88ac7f065b6861e873ae0eeb9ac16a22efca45" -X-Github-Api-Version-Selected: 2022-11-28 -X-Ratelimit-Used: 75 -X-Ratelimit-Resource: core -Content-Type: application/json; charset=utf-8 -Cache-Control: private, max-age=60, s-maxage=60 -Vary: Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With -X-Accepted-Oauth-Scopes: repo -X-Ratelimit-Limit: 5000 -X-Ratelimit-Reset: 1730800941 -Strict-Transport-Security: max-age=31536000; includeSubdomains; preload -X-Xss-Protection: 0 -Referrer-Policy: origin-when-cross-origin, strict-origin-when-cross-origin -Content-Security-Policy: default-src 'none' -X-Oauth-Scopes: -X-Github-Media-Type: github.v3; format=json -X-Ratelimit-Remaining: 4925 -Access-Control-Expose-Headers: ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset -X-Content-Type-Options: nosniff -X-Github-Request-Id: C7CC:3118FC:3F5F41A:4035CB2:6729E6B4 - -[{"url":"https://api.github.com/repos/go-gitea/test_repo/releases/21419432","assets_url":"https://api.github.com/repos/go-gitea/test_repo/releases/21419432/assets","upload_url":"https://uploads.github.com/repos/go-gitea/test_repo/releases/21419432/assets{?name,label}","html_url":"https://github.com/go-gitea/test_repo/releases/tag/v0.9.99","id":21419432,"author":{"login":"mrsdizzie","id":1669571,"node_id":"MDQ6VXNlcjE2Njk1NzE=","avatar_url":"https://avatars.githubusercontent.com/u/1669571?v=4","gravatar_id":"","url":"https://api.github.com/users/mrsdizzie","html_url":"https://github.com/mrsdizzie","followers_url":"https://api.github.com/users/mrsdizzie/followers","following_url":"https://api.github.com/users/mrsdizzie/following{/other_user}","gists_url":"https://api.github.com/users/mrsdizzie/gists{/gist_id}","starred_url":"https://api.github.com/users/mrsdizzie/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mrsdizzie/subscriptions","organizations_url":"https://api.github.com/users/mrsdizzie/orgs","repos_url":"https://api.github.com/users/mrsdizzie/repos","events_url":"https://api.github.com/users/mrsdizzie/events{/privacy}","received_events_url":"https://api.github.com/users/mrsdizzie/received_events","type":"User","user_view_type":"public","site_admin":false},"node_id":"MDc6UmVsZWFzZTIxNDE5NDMy","tag_name":"v0.9.99","target_commitish":"master","name":"First Release","draft":false,"prerelease":false,"created_at":"2019-11-09T16:49:21Z","published_at":"2019-11-12T20:12:10Z","assets":[],"tarball_url":"https://api.github.com/repos/go-gitea/test_repo/tarball/v0.9.99","zipball_url":"https://api.github.com/repos/go-gitea/test_repo/zipball/v0.9.99","body":"A test release"}] \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/HEAD b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/config b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/config new file mode 100644 index 0000000000..07d359d07c --- /dev/null +++ b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/config @@ -0,0 +1,4 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = true diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/description b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/description new file mode 100644 index 0000000000..498b267a8c --- /dev/null +++ b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/exclude b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/exclude new file mode 100644 index 0000000000..a5196d1be8 --- /dev/null +++ b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/refs b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/refs new file mode 100644 index 0000000000..07fe1b45cd --- /dev/null +++ b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/info/refs @@ -0,0 +1 @@ +ca43b48ca2c461f9a5cb66500a154b23d07c9f90 refs/heads/main \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/17/58ff42c9ab82988ad33d211ef5433afd779a9e b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/17/58ff42c9ab82988ad33d211ef5433afd779a9e new file mode 100644 index 0000000000000000000000000000000000000000..5a598008b940670dab7ce536e9b25482578087a0 GIT binary patch literal 884 zcmV-)1B?840d12@&!SimhG)&MIJ;Rm{lNi_K+-d5Kt({1`^6n{7r7r4{q^niWOiMf zs(S0Kq*BR~_HA2Z03ieaz*C+BZi@qyV$K?vXcm0$P_t%fI#?^BD`WSw*Ru1J{ zm*CsKA?+2_1eRg}S5-{W^PT-Kod5)Q&WBVIMM)M#Te7wOt{)iLkzrzU%YyvsE{bB= z&9!!20vCN*OF%)@sYY>$OXrj@BKP2tBu>nyL_dCx+cuY8gFPHf1f z?@TY*P25OA4FOIlQc!Et6fHp3-)~P&d5>Lliu=$%;bfmm@2BNHMn6AG(nWu=V!MU~ z)-DnMUY^}V0K`h0dl_TUqYBSf9Ex>O@cu_P_6lZ+Y^s6@pHnmy(ZHy4TCMv=!0 z#pqTawFI~uA$%Nek9faL1NJCW`;(_EO8R1r=TqNYj$ZO;h7kIwGpgh9AP;B^g1$+- z&Th!OBtZC>lFpDwVPnr0Z#}X_?u>F{Q;~v~Mb%bKz3e0VAYPTc<)4pN&qvrOwUrYl zmp5brlrm{+)K=wgoRgIA8&qE!0sbB{eM9`m#NOQfo(MwBGH|ccyqj}^klfuj>i_mH zFf7;42xNVm)ZG_Q@t-rm`HZs!C*T|CdJO9B!|F$>yAt3YRLLU;X+ONCx~wqhA0Low K{Ph#QQ)Z-W|GLNk literal 0 HcmV?d00001 diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/1a/0f6de7bb300de4a569371cfedf1c6ff189d374 b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/1a/0f6de7bb300de4a569371cfedf1c6ff189d374 new file mode 100644 index 0000000000000000000000000000000000000000..e296f574da3ee4d86905839eded08e6fd0cc9b9e GIT binary patch literal 85 zcmV-b0IL6Z0V^p=O;s>AVlXr?Ff%bx@bPqZ^$T`os6G5S_4A%)MtR<^@7m1o)!y`| rVSy?sN=;13P1VayVW^pWk@fPstgq~>M=qV|z3Or3=i8G2gN`9-)8#D@ literal 0 HcmV?d00001 diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/44/2d28a55b842472c95bead51a4c61f209ac1636 b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/44/2d28a55b842472c95bead51a4c61f209ac1636 new file mode 100644 index 0000000000000000000000000000000000000000..f7aae32db031c1f067509e442e70a2e8e1f8c7ef GIT binary patch literal 790 zcmV+x1L^#D0ew@+uA^2I%$l#bdz8m?nWRqC%m!n?=3--ZOvcPS1{?VLIO!~-oW)s` zR8pyiv~SyT1{6v0zs+Nw16CB7L=Y$lp_sxn8UJ9vLZ=m z`E2!#1qJ1zQ1O{x7B+Y`k|gO7JWGdb&uddB)lZ`D+c_mUEBp#Vs{rvB9ObRk~1;& z&mKx+s9RY^Yh8H}WilJ?39|Xz=yYB(ku!cTb7Elt-CT>q!)LUFM082a4mK1WQh~AN zp0<>=NXk7Ih~nm?l@484*T>~eyq1YRo4rZ!pER>tK$q>b<^5&wL@jpLYsE6F9kQs#2*Ki>|VQbZ2B^CZ&lBqmXXFlNaM(paH!w*u~B2q7(%A%SY&t8$Iz>C zYVb+%gss>zMX{U%9_(tDuyx|`8R?B5ADF|%?O#xsDgXcg literal 0 HcmV?d00001 diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/56/38cb8f3278e467fc1eefcac14d3c0d5d91601f b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/56/38cb8f3278e467fc1eefcac14d3c0d5d91601f new file mode 100644 index 0000000000000000000000000000000000000000..b7d43b3d6de72dde88e896e6d865d98ae35535ee GIT binary patch literal 827 zcmV-B1H}Az0ezFnuB%1>M6>2AdXI9Mhn8NV$7o}0%wVwbj-eZ{nFnL@^|^V;GD(82^`DjDHIb)OnHctgk@R8W&*^#EaXI=jHsP~ znCEmR$~2SbX)19XC&?0$I0T^}Gpq!8$YRX%kVuxf>_*@wv;Jwp4^Covj+c4zAMkUT z&^Udy-H7`7^Ho%HxujoN*Zc_>N#F&E5oH08G)vROH~r2$qY>14^G%oQhXVl^c?=!ITk5r;UEkI|pN@UjWzu$jB(#<4DcubP zQ@R_C(neD6-cwxPWwwaNO$t_*0I_d0;xmfZM40?VT*wQD^B_aZ+TPu)iqV~+z4U^m zDl;zo*RmaPkiqw`CBDacYCr(8x6UR_sn1=lud>BZ$;@}SQ2B!oD^Ea?Z8OQL)kVn| z&G{bgU0d#}F&bX=+po2l08K7&cpqfytG4<>E&86T7AG7vd`xNU{pO~XCq3{S7$?rt z%g9X}b&41xZD!XSX``OT|7#P6>34eDAXxa=xU-%8G@aQ$H>|3*qxOg%RB*@pVo zh$&`OhL;aVSFY$Y0U~I;3t1FRHL&$3OqJoSF6llNC_7pmXh7qBIZk$Sg`s(bl+cw? z_Ntlv=%+VmqzVBHiyF6JGQ(Gq(sCG0SKF(e%o>_7z(c}pvrXo=*q+}bO|kMtnC^J7 z3d*&wmFE;A0Y>%JGP$h@OGobtY*$XADrDuWjYJye#0>!)T3BM*H@15?WTAhe%X96) z?3i3!KOz8kxjiB+X?wM5+c{bL`fyTs=5lgr>SY!6v4iFyKdyV>=3q6boAsqVY)5%0 zK~_JJN&pR?-`Oc}m^ObSD9pOMGjAf^9XD~D{Ff%bxC`wIC$xYSEO<~xt$A6tC@8jwqhs6%NEMB{B Me&H}507ym<^Be6I$;d|j@?SXO#>k`7qi(G6ObeV%VHwN(pC`wmu3WKT)NFNPFAo0M5>Di z(tMmilCw^fppdSl^4@vPQKE%{`2PsQ39MK1DauZmZd)Gr}IPx+AJn(PUs*1+Wz`q zTi)ekk<-8b9Mv%Q9(^)T^kr{)FhIWXOQ&dxUdCOjLa1sGp4IcAt!z)%e2t6GV4fxP zs33%u8=m3r9`W&+)JT}QCkF{$S7Z4$DEjeIA0@}QX?yo7rnN4g{M+*q+>T~$+DEgr zt6*D~lVx`3o$yF}4*q$gN^x-<=C#r&8b|M{4@x3j%_5(*%xomT$eGt1Flp3R5I=Wo z$nSbw?5^H<{oZa0Uwg3rkal}eEGk8xbiS6=;<%htzhIH(JglsHU6&Jot0XoJfe?*k znUp|mhv}kq^Im@jT5oq4KVR=rrf9>1;8oq^n{?Tf9RyLLv&pHdLS#rNYu6s_4M#j! zB7B&-oo++Etb(ktNm6HAKX?@FyNxKDDPO##&GFk0+SRz0#FPd!S$C2n!iFx(cZr|H z3v2cJMm1+QQ(V_BT_t@qpANgjuE5gw7;c2yD5prjE*bElo1~TEsFI$te=yP6pYzX2GS{>>fpU$0XJ%kSvPH(@u zr1Sg(nf-~PO+9o_neS5P=8QU-mDQ{fx(1y0W;V_dU*Tzxj{fr-?mcg@xjA|p%%SwG zO+vIga40&xM7?EqO5t8Ej##>ZTYpIX!@888g1^`|JpI2vf@!#p^ FQcFJoq5%K^ literal 0 HcmV?d00001 diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/76/5f7381bf10911971845cc881fa54a93389a270 b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/76/5f7381bf10911971845cc881fa54a93389a270 new file mode 100644 index 0000000000000000000000000000000000000000..2dfcea6cd379926bbecced1e1c180d883692c95e GIT binary patch literal 85 zcmV-b0IL6Z0V^p=O;s>AVlXr?Ff%bx@bPqZ^$T`os6G5S_4A%)MtR<^@7m1o)!y`| rVSy?sN=;13P1VayVc4(7f1M}qTnY?B_>ujncp literal 0 HcmV?d00001 diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/7c/93d105d3ee6af50705c4d2cc8dd548c2f9edc9 b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/7c/93d105d3ee6af50705c4d2cc8dd548c2f9edc9 new file mode 100644 index 0000000000000000000000000000000000000000..0fe680ed57340bedf7024e1b00e1b5507c507a08 GIT binary patch literal 85 zcmV-b0IL6Z0Ts!?4S*mFMq$rP@h4u5S2l0}!vY`;)F?Hy?k}F+m%MaIg7?#mO?shI r5J(L>J>p!btmuOneU&myVE@0l^k^(i7i2*b!%<@^1h&ict 6 OʽqXW`fsbfSoy-RɥХD3 ՉPtt>Ȃ8՝;||KjoQw \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/9b/fcee0cb75322db96a76b49faa222e2c59f485d b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/9b/fcee0cb75322db96a76b49faa222e2c59f485d new file mode 100644 index 0000000000000000000000000000000000000000..64a7a811da6cf76d726b520b03ce875376766a96 GIT binary patch literal 85 zcmV-b0IL6Z0V^p=O;s>AVlXr?Ff%bx@bPqZ^$T`os6G5S_4A%)MtR<^@7m1o)!y`| rVSy?sN=;13P1VayVc0dzT WQhb.ðe8][+,oKiY:Dž]KP~+"0kKbe>@إ0~(< \ No newline at end of file diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/bf/2e0fd70c6ef1ab5240a340ba38eb47b3e8409f b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/bf/2e0fd70c6ef1ab5240a340ba38eb47b3e8409f new file mode 100644 index 0000000000000000000000000000000000000000..e92cf50fa53248a7fac7e4d58ae1212de58031ec GIT binary patch literal 69 zcmV-L0J{Hp0ZYosPf{?oU{F?Y%P&e#&B|8@Ni8l>2udx;=i&;<$ShVU0x}hVbZK#F bib6@gLJ3GdH#5B`u_QA;ub2w}$Z!@*uwfa^ literal 0 HcmV?d00001 diff --git a/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/ca/43b48ca2c461f9a5cb66500a154b23d07c9f90 b/services/migrations/testdata/github/full_download/forgejo/test_repo.git/objects/ca/43b48ca2c461f9a5cb66500a154b23d07c9f90 new file mode 100644 index 0000000000000000000000000000000000000000..467ab42ca395579ad36c70bfb218bca01bd2790a GIT binary patch literal 901 zcmV;01A6>;0ezE6v*Jb&gnibp=m;N)d1!03I%4b?3A6zc+Ckj3v=-35JOsafo;~)> z;W~bmd8o?Bv}>Ab1`H#>-{z4JfRj8WgiaF#IhM^5EW}7ic$~*E%d&(`Ag=K zq@96~WppZtG?QgXf;f&tA|i+*7(DGMC*5?0DY4TJW7iEYI;Gzxf4zEE5^0kG300U%x(zYA%=LBkh_$ z0fQh95ra4kHZ)7q)EE7_aV8_ss##wW@ckd#kN>q5eKA!9`1aSxie^~A)@+>~sUKK1q$TBRinHsPYak3w9RTTYQZvE2r3e1fbvEcrW^ zD9}7$=+cjvKenQ%%05*3tK%EdwyuK{*VjoXUP)60@ntygi!j9W>2lo;V{CNeF*+V7 zz&9tuK3)!yX5t{8bv<-DL(p8h(MSM`K(lx#bKWUd8)n_#&kUpOMCa&=XXjF!+?ycN)J&3;Grj7ihxoX%o&ReEHFFfyO zm!->At@ruP4d+?Y3a%Gb;&|lL@uk>BdXA@p?6I-TrVwe5gy_47pYx*W(dS_idWUj`5(td^=28$D^<^ z^qq(7dNR^XLNDv3-yUl)_v^d9dgyhQN9S{vqHXRscRf&ErrMgS;#1(QT7j)>Di3q% zuE6`mFSiuKt6U2$h)uAz1zlZH Date: Wed, 3 Sep 2025 16:47:02 +0200 Subject: [PATCH 76/80] [v12.0/forgejo] fix(api): deactivate issue api for disabled or external issue-tracker (#9147) **Backport:** https://codeberg.org/forgejo/forgejo/pulls/8829 - When the issue unit is disabled for a repository, don't allow issue related APIs. - Added integration tests. - Resolves #8408 Co-authored-by: zokki Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9147 Co-authored-by: forgejo-backport-action Co-committed-by: forgejo-backport-action --- routers/api/v1/api.go | 35 +++- .../api_comment_attachment_test.go | 31 +-- .../integration/api_issue_attachment_test.go | 31 +-- tests/integration/api_issue_test.go | 187 ++++++++++++++++++ tests/integration/api_releases_test.go | 26 +-- tests/integration/repo_badges_test.go | 4 +- tests/test_utils.go | 13 ++ 7 files changed, 252 insertions(+), 75 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index b50ca19148..1e6aa98ce5 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -69,6 +69,7 @@ package v1 import ( "fmt" "net/http" + "slices" "strings" actions_model "forgejo.org/models/actions" @@ -468,6 +469,12 @@ func reqAdmin() func(ctx *context.APIContext) { // reqRepoWriter user should have a permission to write to a repo, or be a site admin func reqRepoWriter(unitTypes ...unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { + if !slices.ContainsFunc(unitTypes, func(unitType unit.Type) bool { + return ctx.Repo.Repository.UnitEnabled(ctx, unitType) + }) { + ctx.NotFound() + return + } if !ctx.IsUserRepoWriter(unitTypes) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqRepoWriter", "user should have a permission to write to a repo") return @@ -487,6 +494,10 @@ func reqRepoBranchWriter(ctx *context.APIContext) { // reqRepoReader user should have specific read permission or be a repo admin or a site admin func reqRepoReader(unitType unit.Type) func(ctx *context.APIContext) { return func(ctx *context.APIContext) { + if !ctx.Repo.Repository.UnitEnabled(ctx, unitType) { + ctx.NotFound() + return + } if !ctx.Repo.CanRead(unitType) && !ctx.IsUserRepoAdmin() && !ctx.IsUserSiteAdmin() { ctx.Error(http.StatusForbidden, "reqRepoReader", "user should have specific read permission or be a repo admin or a site admin") return @@ -744,6 +755,26 @@ func mustEnableIssuesOrPulls(ctx *context.APIContext) { } } +func mustEnableLocalIssuesIfIsIssue(ctx *context.APIContext) { + if ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeIssues) { + return + } + + issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + if err != nil { + if issues_model.IsErrIssueNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) + } + return + } + if !issue.IsPull { + ctx.NotFound() + return + } +} + func mustEnableWiki(ctx *context.APIContext) { if !(ctx.Repo.CanRead(unit.TypeWiki)) { ctx.NotFound() @@ -1423,7 +1454,7 @@ func Routes() *web.Route { m.Group("/comments", func() { m.Combo("").Get(repo.ListIssueComments). Post(reqToken(), mustNotBeArchived, bind(api.CreateIssueCommentOption{}), repo.CreateIssueComment) - m.Combo("/{id}", reqToken()).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). + m.Combo("/{id}", reqToken(), commentAssignment(":id")).Patch(bind(api.EditIssueCommentOption{}), repo.EditIssueCommentDeprecated). Delete(repo.DeleteIssueCommentDeprecated) }) m.Get("/timeline", repo.ListIssueCommentsAndTimeline) @@ -1480,7 +1511,7 @@ func Routes() *web.Route { Delete(reqToken(), reqAdmin(), repo.UnpinIssue) m.Patch("/{position}", reqToken(), reqAdmin(), repo.MoveIssuePin) }) - }) + }, mustEnableLocalIssuesIfIsIssue) }, mustEnableIssuesOrPulls) m.Group("/labels", func() { m.Combo("").Get(repo.ListLabels). diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go index 16eb3c9b12..4bad3ca67e 100644 --- a/tests/integration/api_comment_attachment_test.go +++ b/tests/integration/api_comment_attachment_test.go @@ -6,7 +6,6 @@ package integration import ( "bytes" "fmt" - "io" "mime/multipart" "net/http" "testing" @@ -110,17 +109,11 @@ func TestAPICreateCommentAttachment(t *testing.T) { body := &bytes.Buffer{} // Setup multi-part - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, &buff) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets", repoOwner.Name, repo.Name, comment.ID), body). AddTokenAuth(token). - SetHeader("Content-Type", writer.FormDataContentType()) + SetHeader("Content-Type", contentType) resp := session.MakeRequest(t, req, http.StatusCreated) apiAttachment := new(api.Attachment) @@ -150,16 +143,10 @@ func TestAPICreateCommentAttachmentAutoDate(t *testing.T) { defer tests.PrintCurrentTest(t)() // Setup multi-part - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, &buff) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, "POST", urlStr, body).AddTokenAuth(token) - req.Header.Add("Content-Type", writer.FormDataContentType()) + req.Header.Add("Content-Type", contentType) resp := session.MakeRequest(t, req, http.StatusCreated) apiAttachment := new(api.Attachment) DecodeJSON(t, resp, &apiAttachment) @@ -181,16 +168,10 @@ func TestAPICreateCommentAttachmentAutoDate(t *testing.T) { urlStr += fmt.Sprintf("?updated_at=%s", updatedAt.UTC().Format(time.RFC3339)) // Setup multi-part - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, &buff) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, "POST", urlStr, body).AddTokenAuth(token) - req.Header.Add("Content-Type", writer.FormDataContentType()) + req.Header.Add("Content-Type", contentType) resp := session.MakeRequest(t, req, http.StatusCreated) apiAttachment := new(api.Attachment) DecodeJSON(t, resp, &apiAttachment) diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go index ef7e6183f9..8f676e5017 100644 --- a/tests/integration/api_issue_attachment_test.go +++ b/tests/integration/api_issue_attachment_test.go @@ -6,7 +6,6 @@ package integration import ( "bytes" "fmt" - "io" "mime/multipart" "net/http" "testing" @@ -79,17 +78,11 @@ func TestAPICreateIssueAttachment(t *testing.T) { body := &bytes.Buffer{} // Setup multi-part - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, &buff) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets", repoOwner.Name, repo.Name, issue.Index), body). AddTokenAuth(token) - req.Header.Add("Content-Type", writer.FormDataContentType()) + req.Header.Add("Content-Type", contentType) resp := session.MakeRequest(t, req, http.StatusCreated) apiAttachment := new(api.Attachment) @@ -118,16 +111,10 @@ func TestAPICreateIssueAttachmentAutoDate(t *testing.T) { defer tests.PrintCurrentTest(t)() // Setup multi-part - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, &buff) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, "POST", urlStr, body).AddTokenAuth(token) - req.Header.Add("Content-Type", writer.FormDataContentType()) + req.Header.Add("Content-Type", contentType) resp := session.MakeRequest(t, req, http.StatusCreated) apiAttachment := new(api.Attachment) @@ -150,16 +137,10 @@ func TestAPICreateIssueAttachmentAutoDate(t *testing.T) { urlStr += fmt.Sprintf("?updated_at=%s", updatedAt.UTC().Format(time.RFC3339)) // Setup multi-part - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, &buff) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, "POST", urlStr, body).AddTokenAuth(token) - req.Header.Add("Content-Type", writer.FormDataContentType()) + req.Header.Add("Content-Type", contentType) resp := session.MakeRequest(t, req, http.StatusCreated) apiAttachment := new(api.Attachment) diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index 764ddd38d2..e8ea447463 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -4,6 +4,7 @@ package integration import ( + "bytes" "fmt" "net/http" "net/url" @@ -16,14 +17,18 @@ import ( "forgejo.org/models/db" issues_model "forgejo.org/models/issues" repo_model "forgejo.org/models/repo" + "forgejo.org/models/unit" "forgejo.org/models/unittest" user_model "forgejo.org/models/user" + "forgejo.org/modules/optional" "forgejo.org/modules/setting" api "forgejo.org/modules/structs" "forgejo.org/tests" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "xorm.io/xorm/convert" ) func TestAPIListIssues(t *testing.T) { @@ -620,3 +625,185 @@ func TestAPISearchIssuesWithLabels(t *testing.T) { DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, 2) } + +func TestAPIInternalAndExternalIssueTracker(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + otherUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) + token := getUserToken(t, user.Name, auth_model.AccessTokenScopeAll) + + internalIssueRepo, _, reset := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ + Name: optional.Some("internal-issues"), + EnabledUnits: optional.Some([]unit.Type{unit.TypeIssues}), + DisabledUnits: optional.Some([]unit.Type{unit.TypeExternalTracker}), + UnitConfig: optional.Some(map[unit.Type]convert.Conversion{ + unit.TypeIssues: &repo_model.IssuesConfig{ + EnableTimetracker: true, + EnableDependencies: true, + }, + }), + }) + defer reset() + + externalIssueRepo, _, reset := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ + Name: optional.Some("external-issues"), + EnabledUnits: optional.Some([]unit.Type{unit.TypeExternalTracker}), + DisabledUnits: optional.Some([]unit.Type{unit.TypeIssues}), + }) + defer reset() + + disabledIssueRepo, _, reset := tests.CreateDeclarativeRepoWithOptions(t, user, tests.DeclarativeRepoOptions{ + Name: optional.Some("disabled-issues"), + DisabledUnits: optional.Some([]unit.Type{unit.TypeIssues, unit.TypeExternalTracker}), + }) + defer reset() + + runTest := func(t *testing.T, repo *repo_model.Repository, requestAllowed bool) { + t.Helper() + getPath := func(path string, args ...any) string { + suffix := path + if len(args) > 0 { + suffix = fmt.Sprintf(path, args...) + } + return fmt.Sprintf("/api/v1/repos/%s/%s/issues%s", repo.OwnerName, repo.Name, suffix) + } + getStatus := func(allowStatus int) int { + if requestAllowed { + return allowStatus + } + return http.StatusNotFound + } + okStatus := getStatus(http.StatusOK) + createdStatus := getStatus(http.StatusCreated) + noContentStatus := getStatus(http.StatusNoContent) + + // setup + issue := createIssue(t, user, repo, "normal issue", uuid.NewString()) + deleteIssue := createIssue(t, user, repo, "delete this issue", uuid.NewString()) + dependencyIssue := createIssue(t, user, repo, "depend on this issue", uuid.NewString()) + blocksIssue := createIssue(t, user, repo, "depend on this issue", uuid.NewString()) + + // issues + MakeRequest(t, NewRequest(t, "GET", getPath("/")).AddTokenAuth(token), http.StatusOK) + MakeRequest(t, NewRequestWithValues(t, "POST", getPath("/"), map[string]string{"title": uuid.NewString()}).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "GET", getPath("/%d", issue.Index)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequestWithValues(t, "PATCH", getPath("/%d", deleteIssue.Index), map[string]string{"title": uuid.NewString()}).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d", deleteIssue.Index)).AddTokenAuth(token), noContentStatus) + + MakeRequest(t, NewRequest(t, "GET", getPath("/pinned")).AddTokenAuth(token), okStatus) + + // comments + MakeRequest(t, NewRequest(t, "GET", getPath("/comments")).AddTokenAuth(token), http.StatusOK) + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/comments", issue.Index)).AddTokenAuth(token), okStatus) + resp := MakeRequest(t, NewRequestWithValues(t, "POST", getPath("/%d/comments", issue.Index), map[string]string{"body": uuid.NewString()}).AddTokenAuth(token), createdStatus) + var comment api.Comment + DecodeJSON(t, resp, &comment) + resp = MakeRequest(t, NewRequestWithValues(t, "POST", getPath("/%d/comments", issue.Index), map[string]string{"body": uuid.NewString()}).AddTokenAuth(token), createdStatus) + var commentTwo api.Comment + DecodeJSON(t, resp, &commentTwo) + resp = MakeRequest(t, NewRequestWithValues(t, "POST", getPath("/%d/comments", issue.Index), map[string]string{"body": uuid.NewString()}).AddTokenAuth(token), createdStatus) + var commentThree api.Comment + DecodeJSON(t, resp, &commentThree) + MakeRequest(t, NewRequest(t, "GET", getPath("/comments/%d", commentTwo.ID)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequestWithValues(t, "PATCH", getPath("/comments/%d", commentTwo.ID), map[string]string{"body": uuid.NewString()}).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/comments/%d", commentTwo.ID)).AddTokenAuth(token), noContentStatus) + MakeRequest(t, NewRequestWithValues(t, "PATCH", getPath("/%d/comments/%d", issue.Index, commentThree.ID), map[string]string{"body": uuid.NewString()}).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/comments/%d", issue.Index, commentThree.ID)).AddTokenAuth(token), noContentStatus) + // comment-reactions + MakeRequest(t, NewRequest(t, "GET", getPath("/comments/%d/reactions", comment.ID)).AddTokenAuth(token), okStatus) + reaction := &api.EditReactionOption{Reaction: "+1"} + MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/comments/%d/reactions", comment.ID), reaction).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequestWithJSON(t, "DELETE", getPath("/comments/%d/reactions", comment.ID), reaction).AddTokenAuth(token), okStatus) + // comment-assets + MakeRequest(t, NewRequest(t, "GET", getPath("/comments/%d/assets", comment.ID)).AddTokenAuth(token), okStatus) + body := &bytes.Buffer{} + contentType := tests.WriteImageBody(t, generateImg(), "image.png", body) + req := NewRequestWithBody(t, "POST", getPath("/comments/%d/assets", comment.ID), bytes.NewReader(body.Bytes())).AddTokenAuth(token) + req.Header.Add("Content-Type", contentType) + resp = MakeRequest(t, req, createdStatus) + var commentAttachment api.Attachment + DecodeJSON(t, resp, &commentAttachment) + MakeRequest(t, NewRequest(t, "GET", getPath("/comments/%d/assets/%d", comment.ID, commentAttachment.ID)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequestWithValues(t, "PATCH", getPath("/comments/%d/assets/%d", comment.ID, commentAttachment.ID), map[string]string{"name": uuid.NewString()}).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/comments/%d/assets/%d", comment.ID, commentAttachment.ID)).AddTokenAuth(token), noContentStatus) + + // timeline + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/timeline", issue.Index)).AddTokenAuth(token), okStatus) + + // labels + labelName := uuid.NewString() + labelCreateURL := fmt.Sprintf("/api/v1/repos/%s/%s/labels", repo.OwnerName, repo.Name) + resp = MakeRequest(t, NewRequestWithValues(t, "POST", labelCreateURL, map[string]string{"name": labelName, "color": "#333333"}).AddTokenAuth(token), http.StatusCreated) + var label api.Label + DecodeJSON(t, resp, &label) + + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/labels", issue.Index)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/%d/labels", issue.Index), api.IssueLabelsOption{Labels: []any{labelName}}).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequestWithJSON(t, "PUT", getPath("/%d/labels", issue.Index), api.IssueLabelsOption{Labels: []any{labelName}}).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/labels", issue.Index)).AddTokenAuth(token), noContentStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/labels/%d", issue.Index, label.ID)).AddTokenAuth(token), noContentStatus) + + // times + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/times", issue.Index)).AddTokenAuth(token), okStatus) + resp = MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/%d/times", issue.Index), api.AddTimeOption{Time: 60}).AddTokenAuth(token), okStatus) + var trackedTime api.TrackedTime + DecodeJSON(t, resp, &trackedTime) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/times", issue.Index)).AddTokenAuth(token), noContentStatus) + resp = MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/%d/times", issue.Index), api.AddTimeOption{Time: 75}).AddTokenAuth(token), okStatus) + DecodeJSON(t, resp, &trackedTime) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/times/%d", issue.Index, trackedTime.ID)).AddTokenAuth(token), noContentStatus) + + // deadline + MakeRequest(t, NewRequestWithValues(t, "POST", getPath("/%d/deadline", issue.Index), map[string]string{"due_date": "2022-04-06T00:00:00.000Z"}).AddTokenAuth(token), createdStatus) + + // stopwatch + MakeRequest(t, NewRequest(t, "POST", getPath("/%d/stopwatch/start", issue.Index)).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "POST", getPath("/%d/stopwatch/stop", issue.Index)).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "POST", getPath("/%d/stopwatch/start", issue.Index)).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/stopwatch/delete", issue.Index)).AddTokenAuth(token), noContentStatus) + + // subscriptions + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/subscriptions", issue.Index)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/subscriptions/check", issue.Index)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequest(t, "PUT", getPath("/%d/subscriptions/%s", issue.Index, otherUser.Name)).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/subscriptions/%s", issue.Index, otherUser.Name)).AddTokenAuth(token), createdStatus) + + // reactions + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/reactions", issue.Index)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/%d/reactions", issue.Index), api.EditReactionOption{Reaction: "+1"}).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequestWithJSON(t, "DELETE", getPath("/%d/reactions", issue.Index), api.EditReactionOption{Reaction: "+1"}).AddTokenAuth(token), okStatus) + + // assets + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/assets", issue.Index)).AddTokenAuth(token), okStatus) + req = NewRequestWithBody(t, "POST", getPath("/%d/assets", issue.Index), bytes.NewReader(body.Bytes())).AddTokenAuth(token) + req.Header.Add("Content-Type", contentType) + resp = MakeRequest(t, req, createdStatus) + var attachment api.Attachment + DecodeJSON(t, resp, &attachment) + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/assets/%d", issue.Index, attachment.ID)).AddTokenAuth(token), okStatus) + MakeRequest(t, NewRequestWithValues(t, "PATCH", getPath("/%d/assets/%d", issue.Index, attachment.ID), map[string]string{"name": uuid.NewString()}).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequest(t, "DELETE", getPath("/%d/assets/%d", issue.Index, attachment.ID)).AddTokenAuth(token), noContentStatus) + + // dependencies + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/dependencies", issue.Index)).AddTokenAuth(token), okStatus) + dependencyMeta := api.IssueMeta{Index: dependencyIssue.Index, Owner: dependencyIssue.Repo.OwnerName, Name: dependencyIssue.Repo.Name} + MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/%d/dependencies", issue.Index), dependencyMeta).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequestWithJSON(t, "DELETE", getPath("/%d/dependencies", issue.Index), dependencyMeta).AddTokenAuth(token), createdStatus) + + // blocks + MakeRequest(t, NewRequest(t, "GET", getPath("/%d/blocks", issue.Index)).AddTokenAuth(token), okStatus) + blockMeta := api.IssueMeta{Index: blocksIssue.Index, Owner: blocksIssue.Repo.OwnerName, Name: blocksIssue.Repo.Name} + MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/%d/blocks", issue.Index), blockMeta).AddTokenAuth(token), createdStatus) + MakeRequest(t, NewRequestWithJSON(t, "DELETE", getPath("/%d/blocks", issue.Index), blockMeta).AddTokenAuth(token), createdStatus) + + // pin + MakeRequest(t, NewRequestWithJSON(t, "POST", getPath("/%d/pin", issue.Index), blockMeta).AddTokenAuth(token), noContentStatus) + MakeRequest(t, NewRequestWithJSON(t, "PATCH", getPath("/%d/pin/1", issue.Index), blockMeta).AddTokenAuth(token), noContentStatus) + MakeRequest(t, NewRequestWithJSON(t, "DELETE", getPath("/%d/pin", issue.Index), blockMeta).AddTokenAuth(token), noContentStatus) + } + + runTest(t, internalIssueRepo, true) + runTest(t, externalIssueRepo, false) + runTest(t, disabledIssueRepo, false) +} diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index f25948989a..a904f837b6 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -6,8 +6,6 @@ package integration import ( "bytes" "fmt" - "io" - "mime/multipart" "net/http" "net/url" "strings" @@ -319,18 +317,11 @@ func TestAPIUploadAssetRelease(t *testing.T) { defer tests.PrintCurrentTest(t)() body := &bytes.Buffer{} - - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, bytes.NewReader(buff.Bytes())) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, http.MethodPost, assetURL, bytes.NewReader(body.Bytes())). AddTokenAuth(token). - SetHeader("Content-Type", writer.FormDataContentType()) + SetHeader("Content-Type", contentType) resp := MakeRequest(t, req, http.StatusCreated) var attachment *api.Attachment @@ -341,7 +332,7 @@ func TestAPIUploadAssetRelease(t *testing.T) { req = NewRequestWithBody(t, http.MethodPost, assetURL+"?name=test-asset", bytes.NewReader(body.Bytes())). AddTokenAuth(token). - SetHeader("Content-Type", writer.FormDataContentType()) + SetHeader("Content-Type", contentType) resp = MakeRequest(t, req, http.StatusCreated) var attachment2 *api.Attachment @@ -467,18 +458,11 @@ func TestAPIDuplicateAssetRelease(t *testing.T) { filename := "image.png" buff := generateImg() body := &bytes.Buffer{} - - writer := multipart.NewWriter(body) - part, err := writer.CreateFormFile("attachment", filename) - require.NoError(t, err) - _, err = io.Copy(part, &buff) - require.NoError(t, err) - err = writer.Close() - require.NoError(t, err) + contentType := tests.WriteImageBody(t, buff, filename, body) req := NewRequestWithBody(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d/assets?name=test-asset&external_url=https%%3A%%2F%%2Fforgejo.org%%2F", owner.Name, repo.Name, r.ID), body). AddTokenAuth(token) - req.Header.Add("Content-Type", writer.FormDataContentType()) + req.Header.Add("Content-Type", contentType) MakeRequest(t, req, http.StatusBadRequest) } diff --git a/tests/integration/repo_badges_test.go b/tests/integration/repo_badges_test.go index baaa8c136a..928a9975fe 100644 --- a/tests/integration/repo_badges_test.go +++ b/tests/integration/repo_badges_test.go @@ -36,8 +36,8 @@ func TestBadges(t *testing.T) { owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) repo, _, f := tests.CreateDeclarativeRepo(t, owner, "", - []unit_model.Type{unit_model.TypeActions}, - []unit_model.Type{unit_model.TypeIssues, unit_model.TypePullRequests, unit_model.TypeReleases}, + []unit_model.Type{unit_model.TypeActions, unit_model.TypeReleases}, + []unit_model.Type{unit_model.TypeIssues, unit_model.TypePullRequests}, []*files_service.ChangeRepoFile{ { Operation: "create", diff --git a/tests/test_utils.go b/tests/test_utils.go index 7bbab2751a..073961328c 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -5,9 +5,12 @@ package tests import ( + "bytes" "context" "database/sql" "fmt" + "io" + "mime/multipart" "os" "path" "path/filepath" @@ -506,3 +509,13 @@ func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, en return CreateDeclarativeRepoWithOptions(t, owner, opts) } + +func WriteImageBody(t *testing.T, buff bytes.Buffer, filename string, body *bytes.Buffer) string { + writer := multipart.NewWriter(body) + defer writer.Close() + part, err := writer.CreateFormFile("attachment", filename) + require.NoError(t, err) + _, err = io.Copy(part, &buff) + require.NoError(t, err) + return writer.FormDataContentType() +} From 05dcc895d271518615523d65c38a117b18d7a293 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 4 Sep 2025 00:42:07 +0200 Subject: [PATCH 77/80] Update dependency mermaid to v11.10.0 [SECURITY] (v12.0/forgejo) (#9155) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9155 Reviewed-by: Gusted Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot --- package-lock.json | 36 ++++++++++++++++++------------------ package.json | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9de06a8055..5a70ac46b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", + "mermaid": "11.10.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", @@ -2088,9 +2088,9 @@ } }, "node_modules/@mermaid-js/parser": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.4.0.tgz", - "integrity": "sha512-wla8XOWvQAwuqy+gxiZqY+c7FokraOTHRWMsbB4AgRx9Sy7zKslNyejy7E+a77qHfey5GXw/ik3IXv/NHMJgaA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz", + "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==", "license": "MIT", "dependencies": { "langium": "3.3.1" @@ -10562,14 +10562,14 @@ } }, "node_modules/mermaid": { - "version": "11.6.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.6.0.tgz", - "integrity": "sha512-PE8hGUy1LDlWIHWBP05SFdqUHGmRcCcK4IzpOKPE35eOw+G9zZgcnMpyunJVUEOgb//KBORPjysKndw8bFLuRg==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.10.0.tgz", + "integrity": "sha512-oQsFzPBy9xlpnGxUqLbVY8pvknLlsNIJ0NWwi8SUJjhbP1IT0E0o1lfhU4iYV3ubpy+xkzkaOyDUQMn06vQElQ==", "license": "MIT", "dependencies": { "@braintree/sanitize-url": "^7.0.4", "@iconify/utils": "^2.1.33", - "@mermaid-js/parser": "^0.4.0", + "@mermaid-js/parser": "^0.6.2", "@types/d3": "^7.4.3", "cytoscape": "^3.29.3", "cytoscape-cose-bilkent": "^4.1.0", @@ -10578,11 +10578,11 @@ "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.11", "dayjs": "^1.11.13", - "dompurify": "^3.2.4", - "katex": "^0.16.9", + "dompurify": "^3.2.5", + "katex": "^0.16.22", "khroma": "^2.1.0", "lodash-es": "^4.17.21", - "marked": "^15.0.7", + "marked": "^16.0.0", "roughjs": "^4.6.6", "stylis": "^4.3.6", "ts-dedent": "^2.2.0", @@ -10590,21 +10590,21 @@ } }, "node_modules/mermaid/node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.18.tgz", + "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", "license": "MIT" }, "node_modules/mermaid/node_modules/marked": { - "version": "15.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", - "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "version": "16.2.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.2.1.tgz", + "integrity": "sha512-r3UrXED9lMlHF97jJByry90cwrZBBvZmjG1L68oYfuPMW+uDTnuMbyJDymCWwbTE+f+3LhpNDKfpR3a3saFyjA==", "license": "MIT", "bin": { "marked": "bin/marked.js" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/micromark": { diff --git a/package.json b/package.json index f7df1b3f38..7a238fbfb2 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "idiomorph": "0.3.0", "jquery": "3.7.1", "katex": "0.16.22", - "mermaid": "11.6.0", + "mermaid": "11.10.0", "mini-css-extract-plugin": "2.9.2", "minimatch": "10.0.3", "monaco-editor": "0.52.2", From e9ad9855e2b41e4d66d8f1ce2e35ff7d6420b9cb Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 4 Sep 2025 01:15:34 +0200 Subject: [PATCH 78/80] Update module github.com/ulikunitz/xz to v0.5.15 [SECURITY] (v12.0/forgejo) (#9154) Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9154 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 caf6b8f00a..dc547bccca 100644 --- a/go.mod +++ b/go.mod @@ -91,7 +91,7 @@ require ( github.com/sergi/go-diff v1.4.0 github.com/stretchr/testify v1.10.0 github.com/syndtr/goleveldb v1.0.0 - github.com/ulikunitz/xz v0.5.12 + github.com/ulikunitz/xz v0.5.15 github.com/urfave/cli/v3 v3.3.3 github.com/valyala/fastjson v1.6.4 github.com/yohcop/openid-go v1.0.1 diff --git a/go.sum b/go.sum index 639880e2ce..d1630b622d 100644 --- a/go.sum +++ b/go.sum @@ -527,8 +527,8 @@ github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww= github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= -github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= +github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v3 v3.3.3 h1:byCBaVdIXuLPIDm5CYZRVG6NvT7tv1ECqdU4YzlEa3I= github.com/urfave/cli/v3 v3.3.3/go.mod h1:FJSKtM/9AiiTOJL4fJ6TbMUkxBXn7GO9guZqoZtpYpo= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= From ea5371e4cff6bf0de503a99e4030c6a37da31aeb Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Wed, 3 Sep 2025 22:25:08 +0200 Subject: [PATCH 79/80] Update https://data.forgejo.org/infrastructure/next-digest action to v1.2.0 (forgejo) (#9157) 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 | |---|---|---|---| | [https://data.forgejo.org/infrastructure/next-digest](https://data.forgejo.org/infrastructure/next-digest) | action | minor | `v1.1.0` -> `v1.2.0` | --- ### Release Notes
    infrastructure/next-digest (https://data.forgejo.org/infrastructure/next-digest) ### [`v1.2.0`](https://data.forgejo.org/infrastructure/next-digest/compare/v1.1.0...v1.2.0) [Compare Source](https://data.forgejo.org/infrastructure/next-digest/compare/v1.1.0...v1.2.0)
    --- ### Configuration 📅 **Schedule**: Branch creation - Between 12:00 AM and 03:59 AM ( * 0-3 * * * ) (UTC), Automerge - Between 12:00 AM and 03:59 AM ( * 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/9157 Reviewed-by: Earl Warren Co-authored-by: Renovate Bot Co-committed-by: Renovate Bot (cherry picked from commit 7af04391a3c85b9e0b75c351bdf8a06f609dac66) --- .forgejo/workflows/publish-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 91fa03ab8e..1c63868938 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -80,7 +80,7 @@ jobs: label: trigger - name: upgrade v*.next.forgejo.org - uses: https://data.forgejo.org/infrastructure/next-digest@v1.1.0 + uses: https://data.forgejo.org/infrastructure/next-digest@v1.2.0 with: url: https://placeholder:${{ secrets.TOKEN_NEXT_DIGEST }}@invisible.forgejo.org/infrastructure/next-digest ref_name: '${{ github.ref_name }}' From 019430c0d5c99ad7eea4c070115d1b375e4e3fc6 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 5 Sep 2025 00:23:30 +0200 Subject: [PATCH 80/80] [v12.0/forgejo] fix: do better parsing of file modes (#9171) **Backport: forgejo/forgejo#9161** - No longer hardcode the file modes we expect, parse them as numbers and do bitmask tricks that Git does so we allow a more variety of _weird_ file modes that can happen in the wild. - Ref: https://codeberg.org/forgejo/forgejo/pulls/8900#issuecomment-6429175 - Resolves Codeberg/Community#2111 (cherry picked from commit 1cfd5e0b98) Conflict resolution: trivial, choose the new code. Confliction arised because v12 doesn't contain forgejo/forgejo#8900 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9171 Reviewed-by: Earl Warren Reviewed-by: Otto Co-authored-by: Gusted Co-committed-by: Gusted --- modules/git/parse.go | 65 ++++++++++++++++++++------------------- modules/git/parse_test.go | 35 +++++++++++++++++++++ 2 files changed, 69 insertions(+), 31 deletions(-) diff --git a/modules/git/parse.go b/modules/git/parse.go index 6bc32057a7..c7b84d7198 100644 --- a/modules/git/parse.go +++ b/modules/git/parse.go @@ -10,8 +10,6 @@ import ( "io" "strconv" "strings" - - "forgejo.org/modules/log" ) // ParseTreeEntries parses the output of a `git ls-tree -l` command. @@ -55,19 +53,9 @@ func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { entry.sized = true } - switch string(entryMode) { - case "100644": - entry.entryMode = EntryModeBlob - case "100755": - entry.entryMode = EntryModeExec - case "120000": - entry.entryMode = EntryModeSymlink - case "160000": - entry.entryMode = EntryModeCommit - case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons - entry.entryMode = EntryModeTree - default: - return nil, fmt.Errorf("unknown type: %v", string(entryMode)) + entry.entryMode, err = parseMode(string(entryMode)) + if err != nil { + return nil, err } entry.ID, err = NewIDFromString(string(entryObjectID)) @@ -108,23 +96,10 @@ loop: sz -= int64(count) entry := new(TreeEntry) entry.ptree = ptree - - switch string(mode) { - case "100644": - entry.entryMode = EntryModeBlob - case "100755": - entry.entryMode = EntryModeExec - case "120000": - entry.entryMode = EntryModeSymlink - case "160000": - entry.entryMode = EntryModeCommit - case "40000", "40755": // git uses 40000 for tree object, but some users may get 40755 for unknown reasons - entry.entryMode = EntryModeTree - default: - log.Debug("Unknown mode: %v", string(mode)) - return nil, fmt.Errorf("unknown mode: %v", string(mode)) + entry.entryMode, err = parseMode(string(mode)) + if err != nil { + return nil, err } - entry.ID = objectFormat.MustID(sha) entry.name = string(fname) entries = append(entries, entry) @@ -135,3 +110,31 @@ loop: return entries, nil } + +// Parse the file mode, we cannot hardcode the modes that we expect for +// a variety of reasons (that is not known to us) the permissions bits +// of files can vary, usually the result because of tooling that uses Git in +// a funny way. So we have to parse the mode as a integer and do bit tricks. +func parseMode(modeStr string) (EntryMode, error) { + mode, err := strconv.ParseUint(modeStr, 8, 64) + if err != nil { + return 0, fmt.Errorf("cannot parse mode: %v", err) + } + + switch mode & 0o170000 { + case 0o040000: + return EntryModeTree, nil + case 0o120000: + return EntryModeSymlink, nil + case 0o160000: + return EntryModeCommit, nil + case 0o100000: + // Check for the permission bit on the owner. + if mode&0o100 == 0o100 { + return EntryModeExec, nil + } + return EntryModeBlob, nil + } + + return 0, fmt.Errorf("unknown mode: %o", mode) +} diff --git a/modules/git/parse_test.go b/modules/git/parse_test.go index 03f359f6c1..502adab4da 100644 --- a/modules/git/parse_test.go +++ b/modules/git/parse_test.go @@ -101,3 +101,38 @@ func TestParseTreeEntriesInvalid(t *testing.T) { require.Error(t, err) assert.Empty(t, entries) } + +func TestParseMode(t *testing.T) { + ok := func(t *testing.T, mode string, entry EntryMode) { + t.Helper() + actualEntry, err := parseMode(mode) + require.NoError(t, err) + assert.Equal(t, entry, actualEntry) + } + + fail := func(t *testing.T, mode string) { + t.Helper() + entry, err := parseMode(mode) + require.Error(t, err) + assert.Zero(t, entry) + } + + ok(t, "100644", EntryModeBlob) + ok(t, "100755", EntryModeExec) + ok(t, "100754", EntryModeExec) + ok(t, "100700", EntryModeExec) + ok(t, "100744", EntryModeExec) + ok(t, "120000", EntryModeSymlink) + ok(t, "120644", EntryModeSymlink) + ok(t, "160000", EntryModeCommit) + ok(t, "160644", EntryModeCommit) + ok(t, "040000", EntryModeTree) + ok(t, "040755", EntryModeTree) + ok(t, "040775", EntryModeTree) + ok(t, "040754", EntryModeTree) + + fail(t, "not-a-number") + fail(t, "000000") + fail(t, "400000") + fail(t, "111111") +}